From owner-freebsd-standards Fri Jun 14 1:40:32 2002 Delivered-To: freebsd-standards@hub.freebsd.org Received: from freefall.freebsd.org (freefall.FreeBSD.org [216.136.204.21]) by hub.freebsd.org (Postfix) with ESMTP id C10FB37B440 for ; Fri, 14 Jun 2002 01:40:06 -0700 (PDT) Received: (from gnats@localhost) by freefall.freebsd.org (8.11.6/8.11.6) id g5E8e3n13098; Fri, 14 Jun 2002 01:40:03 -0700 (PDT) (envelope-from gnats) Date: Fri, 14 Jun 2002 01:40:03 -0700 (PDT) Message-Id: <200206140840.g5E8e3n13098@freefall.freebsd.org> To: freebsd-standards@FreeBSD.org Cc: From: Bruce Evans Subject: Re: standards/39256: [v]snprintf aren't POSIX-conformant for strings longer than INT_MAX Reply-To: Bruce Evans Sender: owner-freebsd-standards@FreeBSD.ORG Precedence: bulk List-ID: List-Archive: (Web Archive) List-Help: (List Instructions) List-Subscribe: List-Unsubscribe: X-Loop: FreeBSD.ORG The following reply was made to PR standards/39256; it has been noted by GNATS. From: Bruce Evans To: Jonathan Lennox 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 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