From beee2f04f0a9541d6155a005a516f7c074a19aaf Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Tue, 23 Dec 2025 00:13:37 +0200 Subject: [PATCH] gh-143003: Fix possible shared buffer overflow in bytearray.extend() When __length_hint__() returns 0 for non-empty iterator, the data can be written past the shared 0-terminated buffer, corrupting it. --- Lib/test/test_bytes.py | 18 ++++++++++++++++++ ...5-12-23-00-13-02.gh-issue-143003.92g5qW.rst | 2 ++ Objects/bytearrayobject.c | 4 ++-- 3 files changed, 22 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-12-23-00-13-02.gh-issue-143003.92g5qW.rst diff --git a/Lib/test/test_bytes.py b/Lib/test/test_bytes.py index 21be61e4fec720..71ad2ebd7b7cb4 100644 --- a/Lib/test/test_bytes.py +++ b/Lib/test/test_bytes.py @@ -2092,6 +2092,24 @@ def make_case(): with self.assertRaises(BufferError): ba.rsplit(evil) + def test_extend_empty_buffer_overflow(self): + # gh-143003 + class EvilIter: + def __iter__(self): + return self + def __next__(self): + return next(source) + def __length_hint__(self): + return 0 + + # Use ASCII digits so float() takes the fast path that expects a NUL terminator. + source = iter(b'42') + ba = bytearray() + ba.extend(EvilIter()) + + self.assertRaises(ValueError, float, bytearray()) + + class AssortedBytesTest(unittest.TestCase): # # Test various combinations of bytes and bytearray diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-12-23-00-13-02.gh-issue-143003.92g5qW.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-12-23-00-13-02.gh-issue-143003.92g5qW.rst new file mode 100644 index 00000000000000..30df3c53abd29f --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-12-23-00-13-02.gh-issue-143003.92g5qW.rst @@ -0,0 +1,2 @@ +Fix an overflow of the shared empty buffer in :meth:`bytearray.extend` when +``__length_hint__()`` returns 0 for non-empty iterator. diff --git a/Objects/bytearrayobject.c b/Objects/bytearrayobject.c index 338c71ad38f7aa..e45ee15cd16bb6 100644 --- a/Objects/bytearrayobject.c +++ b/Objects/bytearrayobject.c @@ -2223,7 +2223,6 @@ bytearray_extend_impl(PyByteArrayObject *self, PyObject *iterable_of_ints) Py_DECREF(bytearray_obj); return NULL; } - buf[len++] = value; Py_DECREF(item); if (len >= buf_size) { @@ -2233,7 +2232,7 @@ bytearray_extend_impl(PyByteArrayObject *self, PyObject *iterable_of_ints) Py_DECREF(bytearray_obj); return PyErr_NoMemory(); } - addition = len >> 1; + addition = len ? len >> 1 : 1; if (addition > PyByteArray_SIZE_MAX - len) buf_size = PyByteArray_SIZE_MAX; else @@ -2247,6 +2246,7 @@ bytearray_extend_impl(PyByteArrayObject *self, PyObject *iterable_of_ints) have invalidated it. */ buf = PyByteArray_AS_STRING(bytearray_obj); } + buf[len++] = value; } Py_DECREF(it);