snprintf documentation

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

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.

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.