By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
458,012 Members | 1,278 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 458,012 IT Pros & Developers. It's quick & easy.

Is this C style function well written and thread safe?

P: n/a
Dear C++ Users:

I alwasy use std::string and avoid char buffers but last night I
wanted to see if I could make a C style function that would be thread
safe.

For me, it was a good learning experience since my C/C++ knowledge is
limited but I do understand threading issues due to prior Delphi
experience.

In the following function, pleas assume that the Date object is well
written. What I really want to know is if my char buff is being
handled safely.
void fbDateToStr( const IBPP::Date &d, char *buff )
{
if ( d < IBPP::MinDate || d IBPP::MaxDate )
{
strcpy(buff, "");
}
else
{
int iMonth=0, iDay=0, iYear=0;
d.GetDate(iYear, iMonth, iDay);
sprintf(buff, "%d/%d/%d", iMonth, iDay, iYear);
}
}
USAGE:
char buffer[15];
fbDateToStr(dtInitialContactDt, buffer);

RETURNS:
MM/DD/YYYY or if date is invalid, a blank string

NOTES:
Initially, I thought of creating a static buffer within the function
instead of passing a buffer as this function currently is doing, but
doing so would have been thread-unsafe since the buffer would now be
visible/editable by all threads.

Sep 12 '07 #1
Share this Question
Share on Google+
18 Replies


P: n/a
On Sep 12, 10:10 am, jeff_j_dun...@yahoo.com wrote:
Dear C++ Users:

I alwasy use std::string and avoid char buffers but last night I
wanted to see if I could make a C style function that would be thread
safe.

For me, it was a good learning experience since my C/C++ knowledge is
limited but I do understand threading issues due to prior Delphi
experience.

In the following function, pleas assume that the Date object is well
written. What I really want to know is if my char buff is being
handled safely.

void fbDateToStr( const IBPP::Date &d, char *buff )
{
if ( d < IBPP::MinDate || d IBPP::MaxDate )
{
strcpy(buff, "");
}
else
{
int iMonth=0, iDay=0, iYear=0;
d.GetDate(iYear, iMonth, iDay);
sprintf(buff, "%d/%d/%d", iMonth, iDay, iYear);
}

}

USAGE:
char buffer[15];
fbDateToStr(dtInitialContactDt, buffer);

RETURNS:
MM/DD/YYYY or if date is invalid, a blank string

NOTES:
Initially, I thought of creating a static buffer within the function
instead of passing a buffer as this function currently is doing, but
doing so would have been thread-unsafe since the buffer would now be
visible/editable by all threads.
Oh, and another thing that I don't really understand is if I can make
my buffer [10+1] to exactly accomodate the MM/DD/YYYY string size of
10 + 1 for the terminating character?

Thanks again

Sep 12 '07 #2

P: n/a
<je***********@yahoo.comwrote in message
news:11**********************@g4g2000hsf.googlegro ups.com...
: Dear C++ Users:
:
: I alwasy use std::string and avoid char buffers but last night I
: wanted to see if I could make a C style function that would be thread
: safe.
:
: For me, it was a good learning experience since my C/C++ knowledge is
: limited but I do understand threading issues due to prior Delphi
: experience.
:
: In the following function, pleas assume that the Date object is well
: written. What I really want to know is if my char buff is being
: handled safely.
:
:
: void fbDateToStr( const IBPP::Date &d, char *buff )
: {
: if ( d < IBPP::MinDate || d IBPP::MaxDate )
: {
: strcpy(buff, "");
Ok, or you could just write: *buff = '\0';

: }
: else
: {
: int iMonth=0, iDay=0, iYear=0;
: d.GetDate(iYear, iMonth, iDay);
: sprintf(buff, "%d/%d/%d", iMonth, iDay, iYear);
This call could cause an unexpected buffer overfow
if iYear/iMonth/iDay has an out-of-range value
(e.g. if year somehow gets to be 12345678 instead of 2007).
Your platform probably provides a call such as
snprintf or slprintf or sprintf_s, which are
all safer by allowing to restrict output size.
[ you can then also more safely rely on a "tight fit" buffer ]

Also, if you want the output to include leading
zeroes (01/01/1999), you'll want to use the following
format string: "%02d/%02d/%04d"

: }
: }
:
:
: USAGE:
: char buffer[15];
: fbDateToStr(dtInitialContactDt, buffer);
:
: RETURNS:
: MM/DD/YYYY or if date is invalid, a blank string
:
: NOTES:
: Initially, I thought of creating a static buffer within the function
: instead of passing a buffer as this function currently is doing, but
: doing so would have been thread-unsafe since the buffer would now be
: visible/editable by all threads.
Indeed: making sure that a buffer of the right size is provided
is a key issue when using C-style character buffers.

Some ideas for dealing with fixed-size char buffers in C++:

You can take a fixed size char array by reference:
void fbDateToStr( const IBPP::Date &d, char (&buff)[11] )
This will check that the caller provides an array of the
exact desired size.

You could also return a character array encapsulated
into a struct. For instance, using something like
boost::array:
array<char,11fbDateAsStr( IBPP::Date const& d )
{
array<char,11ans;
...
return ans;
}
hth-Ivan
--
http://ivan.vecerina.com/contact/?subject=NG_POST <- email contact form
Brainbench MVP for C++ <http://www.brainbench.com

Sep 12 '07 #3

P: n/a
On Sep 12, 4:15 pm, jeff_j_dun...@yahoo.com wrote:
On Sep 12, 10:10 am, jeff_j_dun...@yahoo.com wrote:
>>
For me, it was a good learning experience since my C/C++ knowledge is
limited but I do understand threading issues due to prior Delphi
experience.
In the following function, pleas assume that the Date object is well
written. What I really want to know is if my char buff is being
handled safely.
void fbDateToStr( const IBPP::Date &d, char *buff )
{
if ( d < IBPP::MinDate || d IBPP::MaxDate )
{
strcpy(buff, "");
}
else
{
int iMonth=0, iDay=0, iYear=0;
d.GetDate(iYear, iMonth, iDay);
sprintf(buff, "%d/%d/%d", iMonth, iDay, iYear);
}
}
USAGE:
char buffer[15];
fbDateToStr(dtInitialContactDt, buffer);
If buffer is stack allocated than it is thread local
and can not be accessed by other threads, so there
are no concurrency issues.

What do you mean that Date is well written?

what if the thread enteres else block and is
then preempted before GetDate is called,
and another thread then changes d so that

d < IBPP::MinDate || d IBPP::MaxDate

what values will GetDate initialize iMonth, etc...?

passing d by const reference only means that
you can not call non const members of Date
within fbDateToStr. It doesn't mean that it can
not be changed by some other thread.
RETURNS:
MM/DD/YYYY or if date is invalid, a blank string
NOTES:
Initially, I thought of creating a static buffer within the function
instead of passing a buffer as this function currently is doing, but
doing so would have been thread-unsafe since the buffer would now be
visible/editable by all threads.

Oh, and another thing that I don't really understand is if I can make
my buffer [10+1] to exactly accomodate the MM/DD/YYYY string size of
10 + 1 for the terminating character?
If you are sure that month and day can not be greater than 99
and that the year can not be greater than 9999 then you need
at most 11 character buffer.

DS

Sep 12 '07 #4

P: n/a
DS & Ivan,

Thank you for your very informative responses.
Best regards,

Jeff

Sep 12 '07 #5

P: n/a
On 2007-09-12 17:10, je***********@yahoo.com wrote:
Dear C++ Users:

I alwasy use std::string and avoid char buffers but last night I
wanted to see if I could make a C style function that would be thread
safe.
The easiest way to make things thread safe is to avoid shared data, for
functions this usually means static variables, or parameters passed as
pointers.
For me, it was a good learning experience since my C/C++ knowledge is
limited but I do understand threading issues due to prior Delphi
experience.

In the following function, pleas assume that the Date object is well
written. What I really want to know is if my char buff is being
handled safely.
It is safe if you trust the user, which more or less means that it is
unsafe. The problem with a pointer to a buffer is that it contains no
information about the size of the buffer, which means that you have to
trust the user to supply a large enough buffer. To solve this versions
like snprintf() and strncpy() were created, but once again you have to
trust the user, this time to not lie about the buffer size.

--
Erik Wikström
Sep 12 '07 #6

P: n/a
Erik Wikström <Er***********@telia.comwrote in news:pwVFi.8544$ZA.4322
@newsb.telia.net:
On 2007-09-12 17:10, je***********@yahoo.com wrote:
>Dear C++ Users:

I alwasy use std::string and avoid char buffers but last night I
wanted to see if I could make a C style function that would be thread
safe.

The easiest way to make things thread safe is to avoid shared data, for
functions this usually means static variables, or parameters passed as
pointers.
Uh... static variables are bad for thread safety. Static says there's only
one instance of the variable regardless of how many "instances" of the
function that are currently executing. You want non-static local variables
so that each "instance" of the function has it's own instance of the local
variable.

(Oh yeah... and threading issues are off-topic for comp.lang.c++ ....)
Sep 12 '07 #7

P: n/a
Uh... static variables are bad for thread safety. Static says there's only
one instance of the variable regardless of how many "instances" of the
function that are currently executing. You want non-static local variables
so that each "instance" of the function has it's own instance of the local
variable.
I took it as Erik suggesting to avoid shared data such, i.e. static
vars... but I definately enjoyed the additional information you
provided as to how they are bad for thread saftey.
(Oh yeah... and threading issues are off-topic for comp.lang.c++ ....)
Understood. Although I learned a whole lot from this thread :)

Thanks

Sep 12 '07 #8

P: n/a
Andre Kostur wrote:
Erik Wikstrvm <Er***********@telia.comwrote in
news:pwVFi.8544$ZA.4322 @newsb.telia.net:
On 2007-09-12 17:10, je***********@yahoo.com wrote:
Dear C++ Users:

I alwasy use std::string and avoid char buffers but last night I
wanted to see if I could make a C style function that would be
thread >safe.

The easiest way to make things thread safe is to avoid shared data,
for functions this usually means static variables, or parameters
passed as pointers.

Uh... static variables are bad for thread safety.
That's what he said.


Brian
Sep 12 '07 #9

P: n/a
"Default User" <de***********@yahoo.comwrote in news:5kqsg0F50upaU1
@mid.individual.net:
Andre Kostur wrote:
>Erik Wikstrvm <Er***********@telia.comwrote in
news:pwVFi.8544$ZA.4322 @newsb.telia.net:
On 2007-09-12 17:10, je***********@yahoo.com wrote:
Dear C++ Users:

I alwasy use std::string and avoid char buffers but last night I
wanted to see if I could make a C style function that would be
thread >safe.
>
The easiest way to make things thread safe is to avoid shared data,
for functions this usually means static variables, or parameters
passed as pointers.

Uh... static variables are bad for thread safety.

That's what he said.
Hmm, I managed to read it as examples of how to avoid shared data, not
examples _of_ shared data.
Sep 12 '07 #10

P: n/a
Andre Kostur wrote:
"Default User" <de***********@yahoo.comwrote in news:5kqsg0F50upaU1
@mid.individual.net:
Andre Kostur wrote:
Erik Wikstrvm <Er***********@telia.comwrote in
news:pwVFi.8544$ZA.4322 @newsb.telia.net:

On 2007-09-12 17:10, je***********@yahoo.com wrote:
Dear C++ Users:

I alwasy use std::string and avoid char buffers but last night I
wanted to see if I could make a C style function that would be
thread >safe.

The easiest way to make things thread safe is to avoid shared
data, for functions this usually means static variables, or
parameters passed as pointers.
>
Uh... static variables are bad for thread safety.
That's what he said.

Hmm, I managed to read it as examples of how to avoid shared data,
not examples of shared data.
Ah. Yes, I can see the ambiguity.


Brian
Sep 12 '07 #11

P: n/a
On Sep 13, 4:08 am, "Ivan Vecerina"
<_INVALID_use_webfo...@ivan.vecerina.comwrote:
<jeff_j_dun...@yahoo.comwrote in message
: In the following function, pleas assume that the Date object is well
: written. What I really want to know is if my char buff is being
: handled safely.

: int iMonth=0, iDay=0, iYear=0;
: d.GetDate(iYear, iMonth, iDay);
: sprintf(buff, "%d/%d/%d", iMonth, iDay, iYear);
As you say, this is dreadful code because it will
buffer overflow if unexpected values comes from
GetDate. The buff should be at least 3 * INT_BYTES + 3
in length (where INT_BYTES is the number of characters
needed to print INT_MIN). snprintf is a good solution.
Some ideas for dealing with fixed-size char buffers in C++:

You can take a fixed size char array by reference:
void fbDateToStr( const IBPP::Date &d, char (&buff)[11] )
This will check that the caller provides an array of the
exact desired size.
You can achieve the same thing in C with: char (*buff)[11]
Sep 12 '07 #12

P: n/a
On 2007-09-12 18:44:38 -0400, Old Wolf <ol*****@inspire.net.nzsaid:
On Sep 13, 4:08 am, "Ivan Vecerina"
<_INVALID_use_webfo...@ivan.vecerina.comwrote:
><jeff_j_dun...@yahoo.comwrote in message
: In the following function, pleas assume that the Date object is well
: written. What I really want to know is if my char buff is being
: handled safely.

: int iMonth=0, iDay=0, iYear=0;
: d.GetDate(iYear, iMonth, iDay);
: sprintf(buff, "%d/%d/%d", iMonth, iDay, iYear);

As you say, this is dreadful code because it will
buffer overflow if unexpected values comes from
GetDate.
But the explicit assumption is that GetDate is "well written," which
certainly implies that it doesn't produce unexpected values. If it
does, the problem is in GetDate, not in the code that assumes that it
does what it's supposed to do. If you don't trust GetDate to meet its
contract, what do you trust it to do?

--
Pete
Roundhouse Consulting, Ltd. (www.versatilecoding.com) Author of "The
Standard C++ Library Extensions: a Tutorial and Reference
(www.petebecker.com/tr1book)

Sep 12 '07 #13

P: n/a
On Sep 13, 11:15 am, Pete Becker <p...@versatilecoding.comwrote:
On 2007-09-12 18:44:38 -0400, Old Wolf <oldw...@inspire.net.nzsaid:
<_INVALID_use_webfo...@ivan.vecerina.comwrote:
<jeff_j_dun...@yahoo.comwrote in message
: In the following function, pleas assume that the Date object is well
: written. What I really want to know is if my char buff is being
: handled safely.
: int iMonth=0, iDay=0, iYear=0;
: d.GetDate(iYear, iMonth, iDay);
: sprintf(buff, "%d/%d/%d", iMonth, iDay, iYear);
As you say, this is dreadful code because it will
buffer overflow if unexpected values comes from
GetDate.

But the explicit assumption is that GetDate is "well written," which
certainly implies that it doesn't produce unexpected values. If it
does, the problem is in GetDate, not in the code that assumes that it
does what it's supposed to do. If you don't trust GetDate to meet its
contract, what do you trust it to do?
As little as possible !

What happens when you link against an upgraded version
of the library that has a bug or behaves slightly differently?

IMHO, it is better to make sure that your own code cannot
cause a buffer overflow, even when poked with a large stick.

Sep 13 '07 #14

P: n/a

Andre Kostur wrote:
Erik Wikström <Er***********@telia.comwrote in news:pwVFi.8544$ZA.4322
@newsb.telia.net:
On 2007-09-12 17:10, je***********@yahoo.com wrote:
Dear C++ Users:

I alwasy use std::string and avoid char buffers but last night I
wanted to see if I could make a C style function that would be thread
safe.
The easiest way to make things thread safe is to avoid shared data, for
functions this usually means static variables, or parameters passed as
pointers.

Uh... static variables are bad for thread safety.
I think that is exactly what he meant - to not have static
members or not pass pointers as parameters, for
static members could be touched by various threads
executing the same function simultaneously, and if
you have pointer parameters, it all depends on what
happens on the outside (is the data shared).
(Oh yeah... and threading issues are off-topic for comp.lang.c++ ....)
It is becoming more and more relevant.

Sep 13 '07 #15

P: n/a
On 2007-09-13 00:23:09 -0400, Old Wolf <ol*****@inspire.net.nzsaid:
On Sep 13, 11:15 am, Pete Becker <p...@versatilecoding.comwrote:
>On 2007-09-12 18:44:38 -0400, Old Wolf <oldw...@inspire.net.nzsaid:
>><_INVALID_use_webfo...@ivan.vecerina.comwrote:
<jeff_j_dun...@yahoo.comwrote in message
: In the following function, pleas assume that the Date object is well
: written. What I really want to know is if my char buff is being
: handled safely.
>>>: int iMonth=0, iDay=0, iYear=0;
: d.GetDate(iYear, iMonth, iDay);
: sprintf(buff, "%d/%d/%d", iMonth, iDay, iYear);
>>As you say, this is dreadful code because it will
buffer overflow if unexpected values comes from
GetDate.

But the explicit assumption is that GetDate is "well written," which
certainly implies that it doesn't produce unexpected values. If it
does, the problem is in GetDate, not in the code that assumes that it
does what it's supposed to do. If you don't trust GetDate to meet its
contract, what do you trust it to do?

As little as possible !

What happens when you link against an upgraded version
of the library that has a bug or behaves slightly differently?
What happens whenever code has a bug? You either hide it by protecting
yourself evey place you use that code, or you fix it in one place. The
lattter is clearly preferable.

If the upgraded version "behaves slightly differently" then its
specification has changed. If you can't rely on its original
specification, you have to review the code that uses it to find out
where you need to make changes.
>
IMHO, it is better to make sure that your own code cannot
cause a buffer overflow, even when poked with a large stick.
You recommended using snprintf instead of sprintf. How do you protect
yourself against a bug in snprintf?

--
Pete
Roundhouse Consulting, Ltd. (www.versatilecoding.com) Author of "The
Standard C++ Library Extensions: a Tutorial and Reference
(www.petebecker.com/tr1book)

Sep 13 '07 #16

P: n/a
On Sep 13, 1:15 am, Pete Becker <p...@versatilecoding.comwrote:
On 2007-09-12 18:44:38 -0400, Old Wolf <oldw...@inspire.net.nzsaid:
On Sep 13, 4:08 am, "Ivan Vecerina"
<_INVALID_use_webfo...@ivan.vecerina.comwrote:
<jeff_j_dun...@yahoo.comwrote in message
: In the following function, pleas assume that the Date object is well
: written. What I really want to know is if my char buff is being
: handled safely.
: int iMonth=0, iDay=0, iYear=0;
: d.GetDate(iYear, iMonth, iDay);
: sprintf(buff, "%d/%d/%d", iMonth, iDay, iYear);
As you say, this is dreadful code because it will
buffer overflow if unexpected values comes from
GetDate.
But the explicit assumption is that GetDate is "well written,"
which certainly implies that it doesn't produce unexpected
values.
For what definition of "unexpected". I don't expect dates 10000
years in the future, but a well written GetDate routine might be
capable of generating them.
If it does, the problem is in GetDate, not in the code that
assumes that it does what it's supposed to do. If you don't
trust GetDate to meet its contract, what do you trust it to
do?
The problem here is that we don't know the contract of GetDate.
A priori, I would expect that the contract would restrict the
possible values for month and day, but would not do so for year;
it makes perfect sense to speak of the year 10000, or even of
the year 100000 (although one might not "expect" such values in
a particular application).

The more general problem is that we've been asked to evaluate
the correctness of a function without being told what the
contract for that function is. Which is an exercise in
futility. About all we can say is that "as it stands", the code
is completely broken, since if I pass it a null pointer, or a
buffer of length 2, bad things will happen, and there is nothing
anywhere which says that such arguments are not allowed.

--
James Kanze (GABI Software) email:ja*********@gmail.com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

Sep 14 '07 #17

P: n/a
On Sep 18, 12:20 pm, Pete Becker <p...@versatilecoding.comwrote:
Because it's exactly the kind of thinking that leads to expliotable
errors. If you're worried about exploits, don't assume that code you
haven't seen or tested is correct.
Are you suggesting that one should never use any
library functions provided by a compiler vendor,
in case they have bugs? Do you give the same advice
to purchasers of Dinkumware?

Seems fairly extreme to me; there's no way to access
I/O resources without using the functions provided
by someone else. Testing can only reveal so much;
e.g. who knows if some filesystem functions will fail
under a certain extreme condition that is difficult
to test for?
Sep 18 '07 #18

P: n/a
On 2007-09-18 00:47:44 -0400, Old Wolf <ol*****@inspire.net.nzsaid:
On Sep 18, 12:20 pm, Pete Becker <p...@versatilecoding.comwrote:
>Because it's exactly the kind of thinking that leads to expliotable
errors. If you're worried about exploits, don't assume that code you
haven't seen or tested is correct.

Are you suggesting that one should never use any
library functions provided by a compiler vendor,
in case they have bugs? Do you give the same advice
to purchasers of Dinkumware?
No, you're making that up. I asked you what the basis is for your
distinction between standard library code (which sometimes has bugs)
and other code in the context of exploitable buffer overruns that you
brought into this discussion. You were unwiling to accept user-written
code that is fairly straightforward and easily tested, preferring an
elaborate protective mechanism, but were completely sanguine about the
standard library implementation, without regard to its origin or
quality, or the complexity (and, hence, likelihood of errors) of the
function you recommended using. That logic has a gaping hole, one that
you are apparently unwilling to address.
>
Seems fairly extreme to me; there's no way to access
I/O resources without using the functions provided
by someone else. Testing can only reveal so much;
e.g. who knows if some filesystem functions will fail
under a certain extreme condition that is difficult
to test for?
Err, where did file systems come into this discussion? As I recall, you
recommended snprintf as the solution to buffer overruns.

--
Pete
Roundhouse Consulting, Ltd. (www.versatilecoding.com) Author of "The
Standard C++ Library Extensions: a Tutorial and Reference
(www.petebecker.com/tr1book)

Sep 18 '07 #19

This discussion thread is closed

Replies have been disabled for this discussion.