bpo-36020: Require vsnprintf() to build Python (GH-20899)

The C99 functions snprintf() and vsnprintf() are now required
to build Python.

PyOS_snprintf() and PyOS_vsnprintf() no longer call Py_FatalError().
Previously, they called Py_FatalError() on a buffer overflow on platforms
which don't provide vsnprintf().
This commit is contained in:
Victor Stinner 2020-06-16 00:54:44 +02:00 committed by GitHub
parent e822e37946
commit 7ab92d54b5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 14 additions and 44 deletions

View File

@ -27,12 +27,8 @@ not.
The wrappers ensure that *str*[*size*-1] is always ``'\0'`` upon return. They The wrappers ensure that *str*[*size*-1] is always ``'\0'`` upon return. They
never write more than *size* bytes (including the trailing ``'\0'``) into str. never write more than *size* bytes (including the trailing ``'\0'``) into str.
Both functions require that ``str != NULL``, ``size > 0`` and ``format != Both functions require that ``str != NULL``, ``size > 0``, ``format != NULL``
NULL``. and ``size < INT_MAX``.
If the platform doesn't have :c:func:`vsnprintf` and the buffer size needed to
avoid truncation exceeds *size* by more than 512 bytes, Python aborts with a
:c:func:`Py_FatalError`.
The return value (*rv*) for these functions should be interpreted as follows: The return value (*rv*) for these functions should be interpreted as follows:
@ -48,8 +44,8 @@ The return value (*rv*) for these functions should be interpreted as follows:
this case too, but the rest of *str* is undefined. The exact cause of the error this case too, but the rest of *str* is undefined. The exact cause of the error
depends on the underlying platform. depends on the underlying platform.
The following functions provide locale-independent string to number conversions.
The following functions provide locale-independent string to number conversions.
.. c:function:: double PyOS_string_to_double(const char *s, char **endptr, PyObject *overflow_exception) .. c:function:: double PyOS_string_to_double(const char *s, char **endptr, PyObject *overflow_exception)

View File

@ -123,6 +123,10 @@ that may require changes to your code.
Build Changes Build Changes
============= =============
* The C99 functions :c:func:`snprintf` and :c:func:`vsnprintf` are now required
to build Python.
(Contributed by Victor Stinner in :issue:`36020`.)
C API Changes C API Changes
============= =============

View File

@ -0,0 +1,2 @@
The C99 functions :c:func:`snprintf` and :c:func:`vsnprintf` are now required
to build Python.

View File

@ -15,10 +15,6 @@
PyOS_snprintf and PyOS_vsnprintf never write more than size bytes PyOS_snprintf and PyOS_vsnprintf never write more than size bytes
(including the trailing '\0') into str. (including the trailing '\0') into str.
If the platform doesn't have vsnprintf, and the buffer size needed to
avoid truncation exceeds size by more than 512, Python aborts with a
Py_FatalError.
Return value (rv): Return value (rv):
When 0 <= rv < size, the output conversion was unexceptional, and When 0 <= rv < size, the output conversion was unexceptional, and
@ -37,6 +33,7 @@
PyMem_Malloc couldn't obtain space for a temp buffer. PyMem_Malloc couldn't obtain space for a temp buffer.
CAUTION: Unlike C99, str != NULL and size > 0 are required. CAUTION: Unlike C99, str != NULL and size > 0 are required.
Also, size must be smaller than INT_MAX.
*/ */
int int
@ -56,50 +53,22 @@ PyOS_vsnprintf(char *str, size_t size, const char *format, va_list va)
{ {
assert(str != NULL); assert(str != NULL);
assert(size > 0); assert(size > 0);
assert(size <= (INT_MAX - 1));
assert(format != NULL); assert(format != NULL);
int len; /* # bytes written, excluding \0 */ int len; /* # bytes written, excluding \0 */
#if defined(_MSC_VER) || defined(HAVE_SNPRINTF)
# define _PyOS_vsnprintf_EXTRA_SPACE 1
#else
# define _PyOS_vsnprintf_EXTRA_SPACE 512
char *buffer;
#endif
/* We take a size_t as input but return an int. Sanity check /* We take a size_t as input but return an int. Sanity check
* our input so that it won't cause an overflow in the * our input so that it won't cause an overflow in the
* vsnprintf return value or the buffer malloc size. */ * vsnprintf return value. */
if (size > INT_MAX - _PyOS_vsnprintf_EXTRA_SPACE) { if (size > INT_MAX - 1) {
len = -666; len = -666;
goto Done; goto Done;
} }
#if defined(_MSC_VER) #if defined(_MSC_VER)
len = _vsnprintf(str, size, format, va); len = _vsnprintf(str, size, format, va);
#elif defined(HAVE_SNPRINTF)
len = vsnprintf(str, size, format, va);
#else #else
/* Emulate vsnprintf(). */ len = vsnprintf(str, size, format, va);
buffer = PyMem_MALLOC(size + _PyOS_vsnprintf_EXTRA_SPACE);
if (buffer == NULL) {
len = -666;
goto Done;
}
len = vsprintf(buffer, format, va);
if (len < 0) {
/* ignore the error */;
}
else if ((size_t)len >= size + _PyOS_vsnprintf_EXTRA_SPACE) {
_Py_FatalErrorFunc(__func__, "Buffer overflow");
}
else {
const size_t to_copy = (size_t)len < size ?
(size_t)len : size - 1;
assert(to_copy < size);
memcpy(str, buffer, to_copy);
str[to_copy] = '\0';
}
PyMem_FREE(buffer);
#endif #endif
Done: Done:
@ -107,5 +76,4 @@ Done:
str[size-1] = '\0'; str[size-1] = '\0';
} }
return len; return len;
#undef _PyOS_vsnprintf_EXTRA_SPACE
} }