From 1c8742db185e4fb472a99d3c41421f1625deadf3 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Mon, 7 Sep 2026 20:07:42 +0200 Subject: [PATCH 1/3] gh-156939: Fix xmlcharrefreplace() buffer overflow Write into a temporay buffer to not write the trailing NUL byte. --- .../2026-09-07-22-32-49.gh-issue-156939.vXc73v.rst | 4 ++++ Objects/unicodeobject.c | 14 ++++++++++---- 2 files changed, 14 insertions(+), 4 deletions(-) create mode 100644 Misc/NEWS.d/next/Security/2026-09-07-22-32-49.gh-issue-156939.vXc73v.rst diff --git a/Misc/NEWS.d/next/Security/2026-09-07-22-32-49.gh-issue-156939.vXc73v.rst b/Misc/NEWS.d/next/Security/2026-09-07-22-32-49.gh-issue-156939.vXc73v.rst new file mode 100644 index 000000000000000..759250a968cd39a --- /dev/null +++ b/Misc/NEWS.d/next/Security/2026-09-07-22-32-49.gh-issue-156939.vXc73v.rst @@ -0,0 +1,4 @@ +Fix a buffer overflow in the ``xmlcharrefreplace`` error handler of 8-bit +encoding (such as ``ascii`` and ``latin1``). Previously, a buffer overflow +wrote one NUL byte in the stack memory if the output length was exactly 512 +bytes. Patch by Victor Stinner. diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c index 2249280d0af8110..0c46a1759dd2dfc 100644 --- a/Objects/unicodeobject.c +++ b/Objects/unicodeobject.c @@ -879,10 +879,16 @@ xmlcharrefreplace(PyBytesWriter *writer, char *str, /* generate replacement */ for (i = collstart; i < collend; ++i) { - size = sprintf(str, "&#%d;", PyUnicode_READ(kind, data, i)); - if (size < 0) { - return NULL; - } + // Use snprintf() with a temporary buffer to not write the trailing + // NUL byte in the writer buffer. + Py_BUILD_ASSERT(_Py_MAX_UNICODE <= 0x10ffff); + // len('􏿿\0') is 11 bytes. + char buffer[11]; + Py_UCS4 ch = PyUnicode_READ(kind, data, i); + size = snprintf(buffer, sizeof(buffer), "&#%d;", ch); + assert(5 <= size && (size_t)size <= (sizeof(buffer) - 1)); + + memcpy(str, buffer, size); str += size; } return str; From 12f94807a6b6ffe0b025009b5f0512ed03835e32 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 9 Sep 2026 18:32:46 +0200 Subject: [PATCH 2/3] Remove NEWS entry --- .../Security/2026-09-07-22-32-49.gh-issue-156939.vXc73v.rst | 4 ---- 1 file changed, 4 deletions(-) delete mode 100644 Misc/NEWS.d/next/Security/2026-09-07-22-32-49.gh-issue-156939.vXc73v.rst diff --git a/Misc/NEWS.d/next/Security/2026-09-07-22-32-49.gh-issue-156939.vXc73v.rst b/Misc/NEWS.d/next/Security/2026-09-07-22-32-49.gh-issue-156939.vXc73v.rst deleted file mode 100644 index 759250a968cd39a..000000000000000 --- a/Misc/NEWS.d/next/Security/2026-09-07-22-32-49.gh-issue-156939.vXc73v.rst +++ /dev/null @@ -1,4 +0,0 @@ -Fix a buffer overflow in the ``xmlcharrefreplace`` error handler of 8-bit -encoding (such as ``ascii`` and ``latin1``). Previously, a buffer overflow -wrote one NUL byte in the stack memory if the output length was exactly 512 -bytes. Patch by Victor Stinner. From 43830a25bbaf3a76cc22eb4fba52c790e60deb32 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 9 Sep 2026 19:19:58 +0200 Subject: [PATCH 3/3] Fix minimum size len("�") is 4 bytes. --- Objects/unicodeobject.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c index 0c46a1759dd2dfc..e86291347c75be0 100644 --- a/Objects/unicodeobject.c +++ b/Objects/unicodeobject.c @@ -886,7 +886,7 @@ xmlcharrefreplace(PyBytesWriter *writer, char *str, char buffer[11]; Py_UCS4 ch = PyUnicode_READ(kind, data, i); size = snprintf(buffer, sizeof(buffer), "&#%d;", ch); - assert(5 <= size && (size_t)size <= (sizeof(buffer) - 1)); + assert(4 <= size && (size_t)size <= (sizeof(buffer) - 1)); memcpy(str, buffer, size); str += size;