The sprintf-posix, vsprintf-posix modules need to "estimate" the size of a given buffer, into which sprintf is allowed to write any amount of result. The current code uses min (SIZE_MAX, INT_MAX) but this is wrong: Some system functions, such as glibc's iconv(), fail if you pass them a buffer and a length such that buffer + length < buffer (wrap around in unsigned arithmetic).
The case where it happened to me was a buffer allocated on the stack (buffer = 0xbff....) and length = 0x7fffffff. This fixes it. 2007-07-01 Bruno Haible <[EMAIL PROTECTED]> * lib/sprintf.c (sprintf): Limit the available length estimation, to avoid address wraparound. * lib/vsprintf.c (vsprintf): Likewise. * modules/sprintf-posix (Dependencies): Add stdint. * modules/vsprintf-posix (Dependencies): Likewise. *** lib/sprintf.c 18 Mar 2007 00:31:50 -0000 1.3 --- lib/sprintf.c 2 Jul 2007 00:48:09 -0000 *************** *** 25,30 **** --- 25,31 ---- #include <errno.h> #include <limits.h> #include <stdarg.h> + #include <stdint.h> #include <stdlib.h> #include "vasnprintf.h" *************** *** 46,56 **** { char *output; size_t len; ! /* vasnprintf fails with EOVERFLOW when the buffer size argument is larger ! than INT_MAX (if that fits into a 'size_t' at all). */ ! size_t lenbuf = (SIZE_MAX < INT_MAX ? SIZE_MAX : INT_MAX); va_list args; va_start (args, format); output = vasnprintf (str, &lenbuf, format, args); len = lenbuf; --- 47,65 ---- { char *output; size_t len; ! size_t lenbuf; va_list args; + /* vasnprintf fails with EOVERFLOW when the buffer size argument is larger + than INT_MAX (if that fits into a 'size_t' at all). + Also note that glibc's iconv fails with E2BIG when we pass a length that + is so large that str + lenbuf wraps around, i.e. + (uintptr_t) (str + lenbuf) < (uintptr_t) str. + Therefore set lenbuf = min (SIZE_MAX, INT_MAX, - (uintptr_t) str - 1). */ + lenbuf = (SIZE_MAX < INT_MAX ? SIZE_MAX : INT_MAX); + if (lenbuf > ~ (uintptr_t) str) + lenbuf = ~ (uintptr_t) str; + va_start (args, format); output = vasnprintf (str, &lenbuf, format, args); len = lenbuf; *** lib/vsprintf.c 18 Mar 2007 00:31:50 -0000 1.3 --- lib/vsprintf.c 2 Jul 2007 00:48:09 -0000 *************** *** 25,30 **** --- 25,31 ---- #include <errno.h> #include <limits.h> #include <stdarg.h> + #include <stdint.h> #include <stdlib.h> #include "vasnprintf.h" *************** *** 46,54 **** { char *output; size_t len; /* vasnprintf fails with EOVERFLOW when the buffer size argument is larger ! than INT_MAX (if that fits into a 'size_t' at all). */ ! size_t lenbuf = (SIZE_MAX < INT_MAX ? SIZE_MAX : INT_MAX); output = vasnprintf (str, &lenbuf, format, args); len = lenbuf; --- 47,63 ---- { char *output; size_t len; + size_t lenbuf; + /* vasnprintf fails with EOVERFLOW when the buffer size argument is larger ! than INT_MAX (if that fits into a 'size_t' at all). ! Also note that glibc's iconv fails with E2BIG when we pass a length that ! is so large that str + lenbuf wraps around, i.e. ! (uintptr_t) (str + lenbuf) < (uintptr_t) str. ! Therefore set lenbuf = min (SIZE_MAX, INT_MAX, - (uintptr_t) str - 1). */ ! lenbuf = (SIZE_MAX < INT_MAX ? SIZE_MAX : INT_MAX); ! if (lenbuf > ~ (uintptr_t) str) ! lenbuf = ~ (uintptr_t) str; output = vasnprintf (str, &lenbuf, format, args); len = lenbuf; *** modules/sprintf-posix 6 Jun 2007 02:20:57 -0000 1.5 --- modules/sprintf-posix 2 Jul 2007 00:48:09 -0000 *************** *** 17,22 **** --- 17,23 ---- signbit fpucw printf-safe + stdint configure.ac: gl_FUNC_SPRINTF_POSIX *** modules/vsprintf-posix 6 Jun 2007 02:20:57 -0000 1.5 --- modules/vsprintf-posix 2 Jul 2007 00:48:09 -0000 *************** *** 17,22 **** --- 17,23 ---- signbit fpucw printf-safe + stdint configure.ac: gl_FUNC_VSPRINTF_POSIX