snprintf documentation

bridged with qdn.public.qnxrtp.os
Post Reply
John A. Murphy

snprintf documentation

Post by John A. Murphy » Tue Dec 23, 2003 10:47 pm

The function snprintf appears to behave in a manner that is both
unexpected and undocumented.

For one thing, the documentation suggests that you can determine the
space required for the generated output by calling snprintf(NULL, 0,
....); this will definitely NOT work! A terminating zero is always
stored.

For another thing, the documentation doesn't mention that calling
snprintf(p, 0, ...) results in a zero being stored at p-1. That can be
handy if you know about it, or very nasty if you don't.

And finally, although the documentation is clear on this point, it might
be a good idea to include a warning about the use of snprintf() to build
up a message a piece at a time. A fairly common idiom is typified by
this code fragment taken from the netsdk:

len += snprintf(&buf[len], RECSIZE - 1 - len, ...);

Without a separate test to compare len with RECSIZE, this code does NOT
provide the buffer overflow protection that the its author probably
intended. After the call that truncates output, len will be left larger
than RECSIZE, and "RECSIZE - 1 - len" will be a very large (unsigned)
number; the next call will generate unlimited output somewhere beyond
the buffer.


Murf

David Gibbs

Re: snprintf documentation

Post by David Gibbs » Mon Jan 05, 2004 9:02 pm

John A. Murphy <murf@perftech.com> wrote:
The function snprintf appears to behave in a manner that is both
unexpected and undocumented.

For one thing, the documentation suggests that you can determine the
space required for the generated output by calling snprintf(NULL, 0,
...); this will definitely NOT work! A terminating zero is always
stored.
With an x86 6.3 (test) version, I tried:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>

int main()
{
int ret;

ret = snprintf(NULL, 0, "%s", "hello");
printf("ret is %d, errno %d\n", ret, errno );
}

Output was:

ret is 5, errno 0

So, it looks like this is (will be) fixed with 6.3.

Or, do you have some other test-case that demonstrates the
problem?
For another thing, the documentation doesn't mention that calling
snprintf(p, 0, ...) results in a zero being stored at p-1. That can be
handy if you know about it, or very nasty if you don't.
The following:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>

int main()
{
int ret;
char p[10] = "123456789";

printf("p is '%s'\n", p );

ret = snprintf(&(p[1]), 0, "%s", "hello");
printf("p[0] = %d, p[1] = %d, p[2] = %d\n", p[0], p[1], p[2] );
printf("ret is %d\n", ret );
}

Gives:

p is '123456789'
p[0] = 49, p[1] = 50, p[2] = 51
ret is 5

So, again, that looks fixed for 6.3.
And finally, although the documentation is clear on this point, it might
be a good idea to include a warning about the use of snprintf() to build
up a message a piece at a time. A fairly common idiom is typified by
this code fragment taken from the netsdk:

len += snprintf(&buf[len], RECSIZE - 1 - len, ...);

Without a separate test to compare len with RECSIZE, this code does NOT
provide the buffer overflow protection that the its author probably
intended. After the call that truncates output, len will be left larger
than RECSIZE, and "RECSIZE - 1 - len" will be a very large (unsigned)
number; the next call will generate unlimited output somewhere beyond
the buffer.
Good tip, but I think it might be beyond the purview of our library
reference.

-David
--
QNX Training Services
http://www.qnx.com/support/training/
Please followup in this newsgroup if you have further questions.

John A. Murphy

Re: snprintf documentation

Post by John A. Murphy » Mon Jan 05, 2004 10:27 pm

David Gibbs wrote:
John A. Murphy <murf@perftech.com> wrote:
The function snprintf appears to behave in a manner that is both
unexpected and undocumented.

For one thing, the documentation suggests that you can determine the
space required for the generated output by calling snprintf(NULL, 0,
...); this will definitely NOT work! A terminating zero is always
stored.

With an x86 6.3 (test) version, I tried:

#include <stdio.h
#include <stdlib.h
#include <unistd.h
#include <errno.h

int main()
{
int ret;

ret = snprintf(NULL, 0, "%s", "hello");
printf("ret is %d, errno %d\n", ret, errno );
}

Output was:

ret is 5, errno 0

So, it looks like this is (will be) fixed with 6.3.

Or, do you have some other test-case that demonstrates the
problem?
With 6.2.0 that code memory faults, so it does indeed appear that it's
been fixed.
For another thing, the documentation doesn't mention that calling
snprintf(p, 0, ...) results in a zero being stored at p-1. That can be
handy if you know about it, or very nasty if you don't.

The following:

#include <stdio.h
#include <stdlib.h
#include <unistd.h
#include <errno.h

int main()
{
int ret;
char p[10] = "123456789";

printf("p is '%s'\n", p );

ret = snprintf(&(p[1]), 0, "%s", "hello");
printf("p[0] = %d, p[1] = %d, p[2] = %d\n", p[0], p[1], p[2] );
printf("ret is %d\n", ret );
}

Gives:

p is '123456789'
p[0] = 49, p[1] = 50, p[2] = 51
ret is 5

So, again, that looks fixed for 6.3.
Agreed. With 6.2.0 the output is:
p is '123456789'
p[0] = 0, p[1] = 50, p[2] = 51
ret is 5
And finally, although the documentation is clear on this point, it might
be a good idea to include a warning about the use of snprintf() to build
up a message a piece at a time. A fairly common idiom is typified by
this code fragment taken from the netsdk:

len += snprintf(&buf[len], RECSIZE - 1 - len, ...);

Without a separate test to compare len with RECSIZE, this code does NOT
provide the buffer overflow protection that the its author probably
intended. After the call that truncates output, len will be left larger
than RECSIZE, and "RECSIZE - 1 - len" will be a very large (unsigned)
number; the next call will generate unlimited output somewhere beyond
the buffer.

Good tip, but I think it might be beyond the purview of our library
reference.
Perhaps so, but the fact that it is so misunderstood, even by your own
programmers, suggests that a good example might be in order.

Thanks for looking into it!

Murf
-David
--
QNX Training Services
http://www.qnx.com/support/training/
Please followup in this newsgroup if you have further questions.

Post Reply

Return to “qdn.public.qnxrtp.os”