Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 14 Jun 2002 01:40:03 -0700 (PDT)
From:      Bruce Evans <bde@zeta.org.au>
To:        freebsd-standards@FreeBSD.org
Subject:   Re: standards/39256: [v]snprintf aren't POSIX-conformant for strings longer than INT_MAX
Message-ID:  <200206140840.g5E8e3n13098@freefall.freebsd.org>

next in thread | raw e-mail | index | archive | help
The following reply was made to PR standards/39256; it has been noted by GNATS.

From: Bruce Evans <bde@zeta.org.au>
To: Jonathan Lennox <lennox@cs.columbia.edu>
Cc: freebsd-gnats-submit@FreeBSD.ORG
Subject: Re: standards/39256: [v]snprintf aren't POSIX-conformant for strings
 longer than INT_MAX
Date: Fri, 14 Jun 2002 18:42:09 +1000 (EST)

 On Thu, 13 Jun 2002, Jonathan Lennox wrote:
 
 > >Description:
 > POSIX 2001's specification of snprintf <http://www.opengroup.org/onlinepubs/007904975/functions/snprintf.html>; specifies that if either the value of n, or the number of bytes required for the format, are greater than INT_MAX, then snprintf should return -1 and set errno to EOVERFLOW.
 
 Please keep line lengths somwhat less than 284 characters.
 
 This is partly a bug in POSIX.  Buffer lengths larger than INT_MAX
 don't cause any problems unless the string length would be larger than
 INT_MAX.  I think failing for large buffers is incompatible with C99
 and plain POSIX (C99 doesn't specify the error handling explicitly,
 so it is hard to tell).  Fortunately, the bug is only in the XSI
 extensions.
 
 > FreeBSD, by contrast, just "clamps" the value of n at INT_MAX.  On overflow of the bytes to be converted, it does return -1, but doesn't set errno.
 
 Hmm.  This is partly an implementation detail in snprintf.c.  vfprintf.c
 returns EOF if the infinite-precision result would be larger than INT_MAX.
 It doesn't set errno because it was fixed before EOVERFLOW existed and
 there didn't seem to be any other suitable errno.  The clamping is only
 in snprintf.c.  snprintf.c used to have the broken XSI error handling of
 returning EOF when n > INT_MAX, but was "fixed" by changing to clamping
 in rev.1.11.  Clamping is just an implementation detail, to work around
 the bug that _size in struct __sbuf has the wrong type (int; should be
 size_t).  Unfortunately, it has an off by one error: if the buffer has
 size INT_MAX + 1U, then it could hold a string of length INT_MAX, but
 clamping prevents this and behaviour decays to the XSI spec except for
 the setting of errno.  Clamping gives the correct behaviour for strings
 that would have length > INT_MAX -- they don't fit, and clamping just
 results in the overflow being detected one byte earlier than is strictly
 necessary.
 
 > Returning EOVERFLOW is an XSI extension, but, I think, a good one.
 
 I agree, but it's not clear how well plain C99 programs would deal with
 this unexpected errno.
 
 > >How-To-Repeat:
 > char buf[80];
 > int ret = snprintf(buf, (size_t)INT_MAX + 1, "Hello, world!\n");
 >
 > Expected return: -1, errno = EOVERFLOW.
 
 This gives undefined behaviour since your buffer is shorter than n :-).
 
 > Actual return: snprintf fills in the string "properly".  (I didn't actually allocate a buffer of size INT_MAX+1, so this can be demonstrated on mere mortals' computers.)
 >
 > Repeating the problem of the actual buffer is harder, since you need a machine with more than 2 gigabytes of memory.  Assuming you have one, though:
 
 Actually, it is easier, since  large field widths can be used to reach
 INT_MAX in just 2 steps.  I've deleted all my complete examples of this
 since I thought that this bug was completely fixed.  Here is a code
 fragment:
 
 	snprintf(NULL, 0, "%*s1", INT_MAX, "");
 
 This wants to return INT_MAX + 1U, but can't.  snprintf() is so slow that
 you wish that INT_MAX were 32767 if you run this.
 
 The corresponding example for sprintf() needs a real buffer of size
 INT_MAX + 1U + 1 to avoid invoking undefined behaviour.
 
 Bruce
 

To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-standards" in the body of the message




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200206140840.g5E8e3n13098>