From 6122dec47c8611c6692c02681c8de090ec4aa01e Mon Sep 17 00:00:00 2001 From: sobolevn Date: Sat, 14 Nov 2026 13:35:36 +0300 Subject: [PATCH 1/2] gh-101703: Use `PyOS_snprintf` in `bytesobject.c` --- Objects/bytesobject.c | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/Objects/bytesobject.c b/Objects/bytesobject.c index e9264f765a08e6..83d3de46a4d807 100644 --- a/Objects/bytesobject.c +++ b/Objects/bytesobject.c @@ -265,6 +265,9 @@ bytes_fromformat(PyBytesWriter *writer, Py_ssize_t writer_pos, s += _len; \ } while (0) +#define SNPRINTF_BYTES(BUFFER, PATTERN, TYP) \ + PyOS_snprintf(BUFFER, sizeof(BUFFER), PATTERN, va_arg(vargs, TYP)) + for (const char *f = format; *f; f++) { if (*f != '%') { *s++ = *f; @@ -324,13 +327,13 @@ bytes_fromformat(PyBytesWriter *writer, Py_ssize_t writer_pos, case 'd': if (longflag) { - len = sprintf(buffer, "%ld", va_arg(vargs, long)); + len = SNPRINTF_BYTES(buffer, "%ld", long); } else if (size_tflag) { - len = sprintf(buffer, "%zd", va_arg(vargs, Py_ssize_t)); + len = SNPRINTF_BYTES(buffer, "%zd", Py_ssize_t); } else { - len = sprintf(buffer, "%d", va_arg(vargs, int)); + len = SNPRINTF_BYTES(buffer, "%d", int); } assert(1 <= len && len < (Py_ssize_t)sizeof(buffer)); WRITE_BYTES(buffer, len); @@ -338,26 +341,26 @@ bytes_fromformat(PyBytesWriter *writer, Py_ssize_t writer_pos, case 'u': if (longflag) { - len = sprintf(buffer, "%lu", va_arg(vargs, unsigned long)); + len = SNPRINTF_BYTES(buffer, "%lu", unsigned long); } else if (size_tflag) { - len = sprintf(buffer, "%zu", va_arg(vargs, size_t)); + len = SNPRINTF_BYTES(buffer, "%zu", size_t); } else { - len = sprintf(buffer, "%u", va_arg(vargs, unsigned int)); + len = SNPRINTF_BYTES(buffer, "%u", unsigned int); } assert(1 <= len && len < (Py_ssize_t)sizeof(buffer)); WRITE_BYTES(buffer, len); break; case 'i': - len = sprintf(buffer, "%i", va_arg(vargs, int)); + len = SNPRINTF_BYTES(buffer, "%i", int); assert(1 <= len && len < (Py_ssize_t)sizeof(buffer)); WRITE_BYTES(buffer, len); break; case 'x': - len = sprintf(buffer, "%x", va_arg(vargs, int)); + len = SNPRINTF_BYTES(buffer, "%x", int); assert(1 <= len && len < (Py_ssize_t)sizeof(buffer)); WRITE_BYTES(buffer, len); break; @@ -382,7 +385,7 @@ bytes_fromformat(PyBytesWriter *writer, Py_ssize_t writer_pos, } case 'p': - len = sprintf(buffer, "%p", va_arg(vargs, void*)); + len = SNPRINTF_BYTES(buffer, "%p", void*); assert(1 <= len && len < (Py_ssize_t)sizeof(buffer)); /* %p is ill-defined: ensure leading 0x. */ if (buffer[1] == 'X') { @@ -415,6 +418,7 @@ bytes_fromformat(PyBytesWriter *writer, Py_ssize_t writer_pos, return s; #undef WRITE_BYTES +#undef SNPRINTF_BYTES } From 071ea20c68ada1d14047b97972742dd7459f60c2 Mon Sep 17 00:00:00 2001 From: sobolevn Date: Sat, 14 Nov 2026 15:51:45 +0300 Subject: [PATCH 2/2] Address review --- Modules/_datetimemodule.c | 13 ++++++++----- Objects/bytesobject.c | 20 ++++++++++++-------- 2 files changed, 20 insertions(+), 13 deletions(-) diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index 091da059afd34b..43fd98036991e2 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -1870,17 +1870,20 @@ static PyObject * make_freplacement(PyObject *object) { char freplacement[64]; + int len; if (PyTime_Check(object)) { - PyOS_snprintf(freplacement, sizeof(freplacement), - "%06d", TIME_GET_MICROSECOND(object)); + len = PyOS_snprintf(freplacement, sizeof(freplacement), + "%06d", TIME_GET_MICROSECOND(object)); } else if (PyDateTime_Check(object)) { - PyOS_snprintf(freplacement, sizeof(freplacement), - "%06d", DATE_GET_MICROSECOND(object)); + len = PyOS_snprintf(freplacement, sizeof(freplacement), + "%06d", DATE_GET_MICROSECOND(object)); } else { - PyOS_snprintf(freplacement, sizeof(freplacement), "%06d", 0); + len = PyOS_snprintf(freplacement, sizeof(freplacement), "%06d", 0); } + assert(len >= 6); + assert(len < sizeof(freplacement)); return PyUnicode_FromString(freplacement); } diff --git a/Objects/bytesobject.c b/Objects/bytesobject.c index 83d3de46a4d807..c1f9366e1a634e 100644 --- a/Objects/bytesobject.c +++ b/Objects/bytesobject.c @@ -232,6 +232,18 @@ PyBytes_FromString(const char *str) return (PyObject *) op; } +static Py_ALWAYS_INLINE int +_assert_written(int len, Py_ssize_t max_len) +{ + assert(len >= 1); + assert(len < max_len); + return len; +} + +#define SNPRINTF_BYTES(BUFFER, PATTERN, TYP) \ + _assert_written(PyOS_snprintf(BUFFER, sizeof(BUFFER), \ + PATTERN, va_arg(vargs, TYP)), \ + (Py_ssize_t)sizeof(BUFFER)) static char* bytes_fromformat(PyBytesWriter *writer, Py_ssize_t writer_pos, @@ -265,9 +277,6 @@ bytes_fromformat(PyBytesWriter *writer, Py_ssize_t writer_pos, s += _len; \ } while (0) -#define SNPRINTF_BYTES(BUFFER, PATTERN, TYP) \ - PyOS_snprintf(BUFFER, sizeof(BUFFER), PATTERN, va_arg(vargs, TYP)) - for (const char *f = format; *f; f++) { if (*f != '%') { *s++ = *f; @@ -335,7 +344,6 @@ bytes_fromformat(PyBytesWriter *writer, Py_ssize_t writer_pos, else { len = SNPRINTF_BYTES(buffer, "%d", int); } - assert(1 <= len && len < (Py_ssize_t)sizeof(buffer)); WRITE_BYTES(buffer, len); break; @@ -349,19 +357,16 @@ bytes_fromformat(PyBytesWriter *writer, Py_ssize_t writer_pos, else { len = SNPRINTF_BYTES(buffer, "%u", unsigned int); } - assert(1 <= len && len < (Py_ssize_t)sizeof(buffer)); WRITE_BYTES(buffer, len); break; case 'i': len = SNPRINTF_BYTES(buffer, "%i", int); - assert(1 <= len && len < (Py_ssize_t)sizeof(buffer)); WRITE_BYTES(buffer, len); break; case 'x': len = SNPRINTF_BYTES(buffer, "%x", int); - assert(1 <= len && len < (Py_ssize_t)sizeof(buffer)); WRITE_BYTES(buffer, len); break; @@ -386,7 +391,6 @@ bytes_fromformat(PyBytesWriter *writer, Py_ssize_t writer_pos, case 'p': len = SNPRINTF_BYTES(buffer, "%p", void*); - assert(1 <= len && len < (Py_ssize_t)sizeof(buffer)); /* %p is ill-defined: ensure leading 0x. */ if (buffer[1] == 'X') { buffer[1] = 'x';