C99 IDE for windows

A

arnuld

Using snprintf does not guarantee this. You have to use it /properly/.
And if you use sprintf properly, you get the same safety. So snprintf
isn't actually all that big a deal.

I searched the archives and really much confused. "Ben Pfaff" says:

The point is that strncpy() is as dangerous
as strcpy() if it isn't used with care.


and regarding sprintf vs snprint I get this from "Aleksander Nabaglo"

1: Calculate needed buffer size,
2: Use assert: bug will not be hidden.
int nsp=0;

sprintf(buf, " ..... %n", /* args */ , &nsp);
if(buf_size <= nsp) { assert(0 & " buf[] too short ");
exit(SOME_ERROR_CODE);

is that okay ?


I also heard from someone that using assert() in code going to be shipped
is a bad idea. All assert statement, before the delivery, must be removed.

Do you have some code for safe sprintf() ?

But it seems from other stuff you've said that you're only interested in
Linux, which is fine,

I actually thought snprint() will save me from buffer problems and now
after a little search I can see that I was using it blindly. Anyway, I
still like the for( int i = 0;..) localization in C99. I don't like my
programs to be polluted by some index numbers when I know I am on Linux
for next decade, at least.
 
S

santosh

arnuld said:
what about snprintf, which saves from overflowing the array attacks.

If you can get the second parameter of snprintf right, you can construct
a perfectly defined call to sprintf too.
And what about localization of index integers like for( in i = 0...) .

{
int i;
for (...)
...
}
I know you are trying to help me, what I am saying that the softwares
I am paid to write for are designed only to run on Linux and nothing
else.

Fair enough, though if all else is equal, a more portable solution is
always a better investment for the future.
Doesn't -Wextra give access to -Wwrite-strings and -Wfloat-equal ?

Not according to the gcc documentation I have.
 
S

santosh

Richard said:
arnuld said:


Using snprintf does not guarantee this. You have to use it /properly/.
And if you use sprintf properly, you get the same safety. So snprintf
isn't actually all that big a deal.

Well, the difference is that with snprintf, as long as the second
parameter is correct you are guaranteed to avoid a buffer overrun,
while with sprintf you need to be sure in advance that the second
argument will not overrun the first, and this could be quite difficult
in some cases.

<snip>
 
S

santosh

Richard said:
santosh said:


(You mean the second argument expression, presumably.)


No, you're not, because your /first/ argument could be incorrect:

Yes. But when I said "correct" I meant that it was the correct value for
the first argument. I realise in hindsight that I should've explicitly
mentioned this.
#define OOPS 6

char intended[32] = {0};
char actual[OOPS] = {0};

snprintf(actual, sizeof intended, "Hello, world");

The second argument is "wrong", isn't it? :)
or your copying semantics might be screwed:

snprintf(ohdear, sizeof ohdear, "%d: %s", i, ohdear);

(in which case the behaviour is undefined, and one possible outcome of
undefined behaviour is that a buffer overrun occurs).
Agreed.


You need to be sure with snprintf too, otherwise you'll silently lose
data.

But this is, in most cases, better than invoking undefined behaviour
with a buffer overrun, since it's possible to recover and retry.
No, it doesn't have to be silent, because you could be listening
- but by the time you've taken the trouble to add the code to listen
for data loss and take corrective action to ensure that no data loss
occurs after all,
Exactly.

you might as well have used sprintf in the first place.

But the difference is that with sprintf you need to know this in
advance, while with snprintf you can "adapt as you go", so to speak.
This offloads the tricky bit of figuring out exactly how long our
buffer needs to be to snprintf itself.
Arbitrary data loss might be okay for some people, but it's something
I try to avoid if I can.

One can use snprintf *and* avoid arbitrary data loss. You need to cache
all your arguments beyond the first, determine the buffer size needed
by a call to snprintf, malloc the buffer (or perhaps a VLA), and write
to it. I admit that this is more complicated than one would like, but
sprintf, by overrunning the buffer, invokes a problem that cannot be
recovered from with Standard C.

Both are perfectly usable if adequate care is taken, but snprintf does
add a small convenience that I personally like. And it's certainly
among the more widely implemented of C99's many additions.
 
S

s0suk3

You are much more portable relying on the C90 standard. The result
is almost always compatible with C99 (the only exception I know of
has to do with the modulus operator and negative values).

Here are others:

o No implicit function declarations (so you can't use a function
without defining or declaring it first)
o No implicit function return type (so you can't omit the return
type of a function and let it default to int)
o It's illegal to have a return statement with no expression in
a non-void function (in previous versions this would cause
undefined behavior)

Just to name the most obvious ones...
You
can't use the // comments, but that is no loss IMO.

One man's trash is another man's treasure :) That's one of the BIGGEST
losses, IMO (along with being unable to mix statements and
declarations).

<snip>

Anyway, I don't see any portability losses if you're working on common
platforms, let alone if you're working on a *single* platform, not to
mention if that platform happens to be Linux!

Sebastian
 
S

santosh

arnuld said:
I searched the archives and really much confused. "Ben Pfaff" says:

The point is that strncpy() is as dangerous
as strcpy() if it isn't used with care.

This is true. In particular you can cause a buffer overrun just as
easily with strncpy as you can with strcpy.

In fact you cause a buffer overrun with just about every Standard C
function that takes a pointer to one. The only solution to this would
be either interpreting the code or adding runtime checks for all
pointer operations.
and regarding sprintf vs snprint I get this from "Aleksander Nabaglo"

1: Calculate needed buffer size,
2: Use assert: bug will not be hidden.
int nsp=0;

sprintf(buf, " ..... %n", /* args */ , &nsp);
if(buf_size <= nsp) { assert(0 & " buf[] too short ");
exit(SOME_ERROR_CODE);

is that okay ?

No, it won't compile. Besides a buffer overrun has already triggered
undefined behaviour by the time the if statement is executed, so we
cannot rely on it's behaviour either.
I also heard from someone that using assert() in code going to be
shipped is a bad idea. All assert statement, before the delivery, must
be removed.

It's poor programming to depend on assert for error checking. Assert is
therefore internal consistency verification during development. It's
always possible for a customer to compile your program with NDEBUG
defined, and if your program does error or validity checking with
assert, then it's rendered very fragile.
Do you have some code for safe sprintf() ?

Why? It's easy enough to write your own. Here is it's description from
the Standard:

7.19.6.6 The sprintf function
Synopsis
1 #include <stdio.h>
int sprintf(char * restrict s,
const char * restrict format, ...);

Description
2 The sprintf function is equivalent to fprintf, except that the output
is written into an array (specified by the argument s) rather than to
a stream. A null character is written at the end of the characters
written; it is not counted as part of the returned value. If copying
takes place between objects that overlap, the behavior is undefined.

Returns
3 The sprintf function returns the number of characters written in the
array, not counting the terminating null character, or a negative
value if an encoding error occurred.
I actually thought snprint() will save me from buffer problems and now
after a little search I can see that I was using it blindly.

As I said elsewhere, provided the second argument is indeed the size of
the first, then buffer overrun *will* be prevented, though data loss
will occur.

One solution is to use snprintf itself to determine the minimum size of
the buffer that will be required and use a sufficiently large one.
Anyway, I
still like the for( int i = 0;..) localization in C99. I don't like my
programs to be polluted by some index numbers when I know I am on
Linux for next decade, at least.

If your functions are relatively short then there won't be
much "pollution" as you call it. You could also introduce a block,
though that's a very ugly solution, IMO.

PS. "Knowing" things for as much as a decade in advance is very risky in
computing (not the theoretical side.) If I were you, I'd still try to
keep my routines as portable as possible if the costs are not too high.
 
B

Ben Bacarisse

Mostly you do know in advance. On the rare occasions when you don't, it's
generally easy enough to find out.

Generally, yes, but how do you find out how much storage you need for
%p? I don't know any reliable way.


Just as with strncpy there are rare times when snprintf is exactly
what you want. I once worked with a network event logging system that
gave you one packet to say what you wanted and truncation was the only
option. Life being what it is, that was before the snprintf family
had come into being, so I basically had to write it (simplified for
the cases that were needed) and I've never actually wanted this
truncation semantics since.
 
S

santosh

Ben said:
Generally, yes, but how do you find out how much storage you need for
%p? I don't know any reliable way.

char *str;
FILE *tmpf = tmpfile();

if (tmpf)
int n = fprintf(tmpf, "%p", (void*)ptr);
if (n < 0) /* error */
else str = malloc(n + 1);

if (str) /* use sprintf */
fclose(tmpf);

<snip>
 
B

Ben Bacarisse

Richard Heathfield said:
arnuld said:
and regarding sprintf vs snprint I get this from "Aleksander Nabaglo"

1: Calculate needed buffer size,
2: Use assert: bug will not be hidden.
int nsp=0;

sprintf(buf, " ..... %n", /* args */ , &nsp);
if(buf_size <= nsp) { assert(0 & " buf[] too short ");
exit(SOME_ERROR_CODE);

is that okay ?

It's bizarre. The assert is meaningless, for a start. Secondly, if the idea
is to calculate whether a buffer overrun /has occurred/, then it's too
little too late.

It looks like the assert is there so that you don't get a *silent*
buffer overflow. The advice given is to calculate the size needed
*and* to check that you did not need more. So this assert is of the
right sort -- it checks internal program logic to spot, during
development, a logic error that might otherwise slip past.
 
S

santosh

santosh said:
char *str;
FILE *tmpf = tmpfile();

if (tmpf)
int n = fprintf(tmpf, "%p", (void*)ptr);
if (n < 0) /* error */
else str = malloc(n + 1);

if (str) /* use sprintf */
fclose(tmpf);

<snip>

Oops sorry about all the syntax errors in that code.
 
B

Ben Bacarisse

Richard Heathfield said:
Ben Bacarisse said:
Richard Heathfield said:
arnuld said:
and regarding sprintf vs snprint I get this from "Aleksander Nabaglo"

1: Calculate needed buffer size,
2: Use assert: bug will not be hidden.
int nsp=0;

sprintf(buf, " ..... %n", /* args */ , &nsp);
if(buf_size <= nsp) { assert(0 & " buf[] too short ");
exit(SOME_ERROR_CODE);

is that okay ?

It's bizarre. The assert is meaningless, for a start. Secondly, if the
idea is to calculate whether a buffer overrun /has occurred/, then it's
too little too late.

It looks like the assert is there so that you don't get a *silent*
buffer overflow.

Firstly, by the time the assertion happens (IF it happens), the overrun has
already occurred, and that's UB, so all bets are off.

Don't be so pessimistic -- the great thing about UB is that it can't
get any worse! The assert can't make the program any less
well-defined UB, and there just the chance that something good happens
as if by magic.
Secondly, whether the buffer is big enough is very likely to be dependent
on runtime data, in which case an assertion is effectively being used for
data validation - never a good idea.

I don't get this. They seem to be suggesting that the calculation
be checked via another route and that seems to be what assert is for,
surely? It would be clearer if the assert was for the condition they
test in the 'if', but...
(Thirdly, the assertion might not happen at all, because NDEBUG might be
defined. Included only for completeness, since it's true of all
assertions.)

Which, I think, explains why the assert is written the way it is. The
author wants the exit to happen, even in production code. Now that is
odd, even to me, but all I am trying to do in see what the thinking was.
I'd either just assert(buf_size > nsp) or keep the "if" test and
replace the assert with an error message.
I see your point, and I'm prepared to meet it about halfway. It's still
broken code, IMHO. It's like having a safety net laid out flat on the
concrete.

A better analogy would be a safety net over a hole in the floor.
The idea that it might catch you before you hit the basement.
 
C

CBFalconer

arnuld said:
.... snip ...

I actually thought snprint() will save me from buffer problems and
now after a little search I can see that I was using it blindly.
Anyway, I still like the for( int i = 0;..) localization in C99. I
don't like my programs to be polluted by some index numbers when I
know I am on Linux for next decade, at least.

Bear in mind that everything you have been told, or advised, here
has nothing to do with Linux. It applies to all C programs. The
only requirement is that the compiler meet the demands of the
standard, and most do (at least to the C90 standard). However most
also require care in usage to keep them compatible. Look at your
compiler manuals.

For gcc the essential steps are to run it with:

gcc -W -Wall -ansi -pedantic
 
U

user923005

I also heard from someone that using assert() in code going to be shipped
is a bad idea. All assert statement, before the delivery, must be removed..

Using assert() is strictly a debugging tool. The meaning of:
assert(foo);
is "I am absolutely sure that foo is true. I would be utterly
astonished if foo were *ever* false."

You cannot rely on assert() as a runtime correction to anything
because release builds typically #undef it. If you rely on assert()
to catch problems, the behavior can change when you recompile. And it
leaves a *very* bad impression when an assert() fires on a customer
site.

If you think that there is even a remote chance that boolean foo will
ever be false, then you need to do this:
if (foo)
perform_normal_stuff();
else
handle_panic_situation();

If you want to see a masterful use of assert [there are 1980 well done
asserts in the code], I highly recommend this chess program for study:
Look here:
http://arctrix.com/nas/chess/fruit/
Alternative here:
http://wbec-ridderkerk.nl/html/download.htm

[snip]
 
A

Antoninus Twink

For gcc the essential steps are to run it with:

gcc -W -Wall -ansi -pedantic

Interesting. So you've silently dropped the extra flags that force gcc
to be non-conforming, despite swearing blind for post after post in a
recent thread that they didn't, in the face of clear evidence to the
contrary? At least be grown up enough to admit your mistakes.
 
K

Kenny McCormack

Interesting. So you've silently dropped the extra flags that force gcc
to be non-conforming, despite swearing blind for post after post in a
recent thread that they didn't, in the face of clear evidence to the
contrary? At least be grown up enough to admit your mistakes.

Grown up? CBF? What planet are you posting from?

Actually, in terms of physical (not mental) age, CBF is, I believe,
about as old as dirt.
 
A

arnuld

I have a safe /strategy/ for sprintf:

1) find out how much storage you need for the string;
2) allocate that much;
3) call sprintf to build the string.


you mean you missed the 4th step ;)

1) find out how much storage you need for the string
2) malloc() that much
3) call sprintf to build the string
4) don't forget to free() the memory
 
A

arnuld

Only if you've allocated the memory dynamically, which isn't necessarily
the case.


you mean I can simply do:

1) enum { ARRSIZE = 101 }
2) char array[ARRSIZE]
3) sprintf( ..... )
 
S

santosh

arnuld said:
Only if you've allocated the memory dynamically, which isn't
necessarily the case.


you mean I can simply do:

1) enum { ARRSIZE = 101 }
2) char array[ARRSIZE]
3) sprintf( ..... )

You can, but if array is not large enough a buffer overrun will occur,
while with snprintf you can prevent that at the cost of data loss.

An exercise: Consider the format specifier "%+0#*.*LG\t%0#p\n%ls\n"
How many characters does a buffer need to be to prevent a overrun? Can
you find out without using *printf function?
 
A

arnuld

I mean it depends on the circumstances. For example, if you want to
"sprint" three possibly-negative ints and a ten-byte string, separated by
spaces, you know at compile time that you need

3 * /* three */
((sizeof(int) * CHAR_BIT + 2) / 3) + /* ints, */
1) + /* possibly negative, */
10 + /* and a ten-byte string, */
3 + /* and three separating spaces, */
1 /* and a null terminator */
... SNIP..


I really did not understand the most of it ;) but I got the idea that you
must know your input size in advance which I already know. e.g if I am
sure that my input is no more than 10 bytes then:

char arrc[10];
sprintf(arrc, "This input is definitely more than 10 bytes");

what will it do. In curiosity I have played with this code:


#include <stdio.h>

int main( void )
{
char arrc[10];

sprintf(arrc, "This is more than 10 bytes, of course");
printf("-------------------------------\n");
printf("%s", arrc);

return 0;
}

=============== OUTPUT =================
[arnuld@dune ztest]$ gcc -std=c99 -pedantic -Wall -Wextra test.c
[arnuld@dune ztest]$ ./a.out
-------------------------------
Segmentation fault
[arnuld@dune ztest]$



Now if I add a '\n'

1.) after the %s in last printf.
2.) or in the end of the string literal (the 2nd argument to sprintf)


then the the output is this:

[arnuld@dune ztest]$ ./a.out
-------------------------------
This is more than 10 bytes, of course
Segmentation fault
[arnuld@dune ztest]$


It prints the message though Segfaults. I don't understand it.
 
S

santosh

Richard said:
santosh said:



An exercise: Consider shooting the analyst. :)


No amount will be sufficient, since an overrun is always one possible
result of undefined behaviour - and that format specifier *will*
invoke undefined behaviour if you pass it to *printf.


Simply looking it up in the Standard was sufficient.

Yes, it does seem rather silly. However barring the '0' and '#' before
the 'p' specifier, the rest is correct, and it emphasises my main
point, which is that with floating point values, it's not as easy to
pre-calculate the buffer size needed, as it is with integers. Of course
the 'p' specifier throws an additional spanner into the works.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,777
Messages
2,569,604
Members
45,230
Latest member
LifeBoostCBD

Latest Threads

Top