From 58235070e77bd9c1df06f531ea59aa26775da433 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Mon, 15 Jun 2020 16:37:38 +0200 Subject: [PATCH 1/2] bpo-36020: Remove snprintf macro in pyerrors.h On Windows, #include "pyerrors.h" no longer defines "snprintf" and "vsnprintf" macros. PyOS_snprintf() and PyOS_vsnprintf() should be used to get portable behavior. Replace snprintf() calls with PyOS_snprintf() and replace vsnprintf() calls with PyOS_vsnprintf(). --- Include/pyerrors.h | 17 ++---------- .../2020-06-15-16-46-01.bpo-36020.djI6jw.rst | 2 ++ Modules/_ctypes/callbacks.c | 2 +- Modules/_decimal/_decimal.c | 4 +-- Modules/socketmodule.c | 7 +++-- Parser/tokenizer.c | 2 +- Python/mysnprintf.c | 27 ++++++++++++------- 7 files changed, 28 insertions(+), 33 deletions(-) create mode 100644 Misc/NEWS.d/next/C API/2020-06-15-16-46-01.bpo-36020.djI6jw.rst diff --git a/Include/pyerrors.h b/Include/pyerrors.h index 399bb7c3a6fac03..979a26ba68a0335 100644 --- a/Include/pyerrors.h +++ b/Include/pyerrors.h @@ -4,6 +4,8 @@ extern "C" { #endif +#include // va_list + /* Error handling definitions */ PyAPI_FUNC(void) PyErr_SetNone(PyObject *); @@ -307,21 +309,6 @@ PyAPI_FUNC(int) PyUnicodeTranslateError_SetReason( const char *reason /* UTF-8 encoded string */ ); -/* These APIs aren't really part of the error implementation, but - often needed to format error messages; the native C lib APIs are - not available on all platforms, which is why we provide emulations - for those platforms in Python/mysnprintf.c, - WARNING: The return value of snprintf varies across platforms; do - not rely on any particular behavior; eventually the C99 defn may - be reliable. -*/ -#if defined(MS_WIN32) && !defined(HAVE_SNPRINTF) -# define HAVE_SNPRINTF -# define snprintf _snprintf -# define vsnprintf _vsnprintf -#endif - -#include PyAPI_FUNC(int) PyOS_snprintf(char *str, size_t size, const char *format, ...) Py_GCC_ATTRIBUTE((format(printf, 3, 4))); PyAPI_FUNC(int) PyOS_vsnprintf(char *str, size_t size, const char *format, va_list va) diff --git a/Misc/NEWS.d/next/C API/2020-06-15-16-46-01.bpo-36020.djI6jw.rst b/Misc/NEWS.d/next/C API/2020-06-15-16-46-01.bpo-36020.djI6jw.rst new file mode 100644 index 000000000000000..1f91dce4608d349 --- /dev/null +++ b/Misc/NEWS.d/next/C API/2020-06-15-16-46-01.bpo-36020.djI6jw.rst @@ -0,0 +1,2 @@ +On Windows, ``#include "pyerrors.h"`` no longer defines ``snprintf`` and +``vsnprintf`` macros. diff --git a/Modules/_ctypes/callbacks.c b/Modules/_ctypes/callbacks.c index 29e8fac8c9496c4..2abfa67cdc06bf7 100644 --- a/Modules/_ctypes/callbacks.c +++ b/Modules/_ctypes/callbacks.c @@ -84,7 +84,7 @@ PrintError(const char *msg, ...) va_list marker; va_start(marker, msg); - vsnprintf(buf, sizeof(buf), msg, marker); + PyOS_vsnprintf(buf, sizeof(buf), msg, marker); va_end(marker); if (f != NULL && f != Py_None) PyFile_WriteString(buf, f); diff --git a/Modules/_decimal/_decimal.c b/Modules/_decimal/_decimal.c index 20ba8fb77ad44f3..62647c06bfc8060 100644 --- a/Modules/_decimal/_decimal.c +++ b/Modules/_decimal/_decimal.c @@ -2500,7 +2500,7 @@ dectuple_as_str(PyObject *dectuple) goto error; } - n = snprintf(cp, mem, "%s", sign_special); + n = PyOS_snprintf(cp, mem, "%s", sign_special); if (n < 0 || n >= mem) { PyErr_SetString(PyExc_RuntimeError, "internal error in dec_sequence_as_str"); @@ -2540,7 +2540,7 @@ dectuple_as_str(PyObject *dectuple) if (sign_special[1] == '\0') { /* not a special number */ *cp++ = 'E'; - n = snprintf(cp, MPD_EXPDIGITS+2, "%" PRI_mpd_ssize_t, exp); + n = PyOS_snprintf(cp, MPD_EXPDIGITS+2, "%" PRI_mpd_ssize_t, exp); if (n < 0 || n >= MPD_EXPDIGITS+2) { PyErr_SetString(PyExc_RuntimeError, "internal error in dec_sequence_as_str"); diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c index f60a27ebe408c9a..db0eeaafeec27c1 100644 --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -436,13 +436,12 @@ remove_unusable_flags(PyObject *m) #endif #ifdef MS_WIN32 -#undef EAFNOSUPPORT -#define EAFNOSUPPORT WSAEAFNOSUPPORT -#define snprintf _snprintf +# undef EAFNOSUPPORT +# define EAFNOSUPPORT WSAEAFNOSUPPORT #endif #ifndef SOCKETCLOSE -#define SOCKETCLOSE close +# define SOCKETCLOSE close #endif #if (defined(HAVE_BLUETOOTH_H) || defined(HAVE_BLUETOOTH_BLUETOOTH_H)) && !defined(__NetBSD__) && !defined(__DragonFly__) diff --git a/Parser/tokenizer.c b/Parser/tokenizer.c index d461e4e24e721cd..f3c1d9b20ade112 100644 --- a/Parser/tokenizer.c +++ b/Parser/tokenizer.c @@ -1133,7 +1133,7 @@ verify_identifier(struct tok_state *tok) Py_DECREF(s); // PyUnicode_FromFormatV() does not support %X char hex[9]; - snprintf(hex, sizeof(hex), "%04X", ch); + (void)PyOS_snprintf(hex, sizeof(hex), "%04X", ch); if (Py_UNICODE_ISPRINTABLE(ch)) { syntaxerror(tok, "invalid character '%c' (U+%s)", ch, hex); } diff --git a/Python/mysnprintf.c b/Python/mysnprintf.c index 945a81abb01c20d..458ca14d5c611e0 100644 --- a/Python/mysnprintf.c +++ b/Python/mysnprintf.c @@ -1,6 +1,8 @@ #include "Python.h" -/* snprintf() wrappers. If the platform has vsnprintf, we use it, else we +/* snprintf() and vsnprintf() wrappers. + + If the platform has vsnprintf, we use it, else we emulate it in a half-hearted way. Even if the platform has it, we wrap it because platforms differ in what vsnprintf does in case the buffer is too small: C99 behavior is to return the number of characters that @@ -52,16 +54,17 @@ PyOS_snprintf(char *str, size_t size, const char *format, ...) int PyOS_vsnprintf(char *str, size_t size, const char *format, va_list va) { + assert(str != NULL); + assert(size > 0); + assert(format != NULL); + int len; /* # bytes written, excluding \0 */ -#ifdef HAVE_SNPRINTF -#define _PyOS_vsnprintf_EXTRA_SPACE 1 +#if defined(_MSC_VER) || defined(HAVE_SNPRINTF) +# define _PyOS_vsnprintf_EXTRA_SPACE 1 #else -#define _PyOS_vsnprintf_EXTRA_SPACE 512 +# define _PyOS_vsnprintf_EXTRA_SPACE 512 char *buffer; #endif - assert(str != NULL); - assert(size > 0); - assert(format != NULL); /* 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 * vsnprintf return value or the buffer malloc size. */ @@ -70,10 +73,12 @@ PyOS_vsnprintf(char *str, size_t size, const char *format, va_list va) goto Done; } -#ifdef HAVE_SNPRINTF +#if defined(_MSC_VER) + len = _vsnprintf(str, size, format, va); +#elif defined(HAVE_SNPRINTF) len = vsnprintf(str, size, format, va); #else - /* Emulate it. */ + /* Emulate vsnprintf(). */ buffer = PyMem_MALLOC(size + _PyOS_vsnprintf_EXTRA_SPACE); if (buffer == NULL) { len = -666; @@ -96,9 +101,11 @@ PyOS_vsnprintf(char *str, size_t size, const char *format, va_list va) } PyMem_FREE(buffer); #endif + Done: - if (size > 0) + if (size > 0) { str[size-1] = '\0'; + } return len; #undef _PyOS_vsnprintf_EXTRA_SPACE } From d0271479088d2ba30db1ce93020af5e05e0fb866 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Mon, 15 Jun 2020 20:50:45 +0200 Subject: [PATCH 2/2] Revert changes in the _decimal module --- Modules/_decimal/_decimal.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Modules/_decimal/_decimal.c b/Modules/_decimal/_decimal.c index 62647c06bfc8060..20ba8fb77ad44f3 100644 --- a/Modules/_decimal/_decimal.c +++ b/Modules/_decimal/_decimal.c @@ -2500,7 +2500,7 @@ dectuple_as_str(PyObject *dectuple) goto error; } - n = PyOS_snprintf(cp, mem, "%s", sign_special); + n = snprintf(cp, mem, "%s", sign_special); if (n < 0 || n >= mem) { PyErr_SetString(PyExc_RuntimeError, "internal error in dec_sequence_as_str"); @@ -2540,7 +2540,7 @@ dectuple_as_str(PyObject *dectuple) if (sign_special[1] == '\0') { /* not a special number */ *cp++ = 'E'; - n = PyOS_snprintf(cp, MPD_EXPDIGITS+2, "%" PRI_mpd_ssize_t, exp); + n = snprintf(cp, MPD_EXPDIGITS+2, "%" PRI_mpd_ssize_t, exp); if (n < 0 || n >= MPD_EXPDIGITS+2) { PyErr_SetString(PyExc_RuntimeError, "internal error in dec_sequence_as_str");