https://github.com/python/cpython/commit/7ed3bb8cbeb56e035d266970ae75f5d6bcbbcca3
commit: 7ed3bb8cbeb56e035d266970ae75f5d6bcbbcca3
branch: 3.14
author: Miss Islington (bot) <[email protected]>
committer: cmaloney <[email protected]>
date: 2026-11-05T14:14:51-08:00
summary:

[3.14] gh-158928: Fix overflow in bytearray.__init__ from an iterator 
(GH-158947) (#161447)

Passing an iterator to `bytearray.__init__` didn't take into account
that iterating that iterator could modify the bytearray position and
`ob_start`. When that happened the iterator would write beyond the end
of the allocation.

Update the "enough space" check to account for `ob_start`. The
assignment always included it. Add an assert after append that the
offsets line up as expected.
(cherry picked from commit 25181cee14359a0fbdbe54c83ab3ac44147d22ed)

Co-authored-by: Cody Maloney <[email protected]>
Co-authored-by: Claude Opus 4.8 <[email protected]>

files:
A Misc/NEWS.d/next/Security/2026-10-06-12-30-00.gh-issue-158928.Zd9Ybv.rst
M Lib/test/test_bytes.py
M Objects/bytearrayobject.c

diff --git a/Lib/test/test_bytes.py b/Lib/test/test_bytes.py
index d180de1884a2f6..110690b573829b 100644
--- a/Lib/test/test_bytes.py
+++ b/Lib/test/test_bytes.py
@@ -1814,6 +1814,22 @@ def g():
         alloc = b.__alloc__()
         self.assertGreater(alloc, len(b))
 
+    def test_init_from_iterator_with_offset(self):
+        # gh-158928: Inserting from an iterator in __init__ needs to take into
+        # account if there is a start offset.
+        b = bytearray()
+        def iterator_which_resets():
+            # __init__ reset ob_start. Make it an offset inside by allocating
+            # then doing fast prefix delete.
+            nonlocal b
+            b.resize(200)
+            del b[:100]
+            # Fill remaining already allocated space
+            yield from b'A' * 100
+        # Fill to end. Used to land out of bounds and crash.
+        b.__init__(iterator_which_resets())
+        self.assertEqual(b, bytes(100) + b'A' * 100)
+
     def test_extend(self):
         orig = b'hello'
         a = bytearray(orig)
diff --git 
a/Misc/NEWS.d/next/Security/2026-10-06-12-30-00.gh-issue-158928.Zd9Ybv.rst 
b/Misc/NEWS.d/next/Security/2026-10-06-12-30-00.gh-issue-158928.Zd9Ybv.rst
new file mode 100644
index 00000000000000..2b9597c5ed94f3
--- /dev/null
+++ b/Misc/NEWS.d/next/Security/2026-10-06-12-30-00.gh-issue-158928.Zd9Ybv.rst
@@ -0,0 +1,2 @@
+Fix a buffer overflow when initializing a :class:`bytearray` from an
+iterator.
diff --git a/Objects/bytearrayobject.c b/Objects/bytearrayobject.c
index 5486c0353d7173..cc519962052e7e 100644
--- a/Objects/bytearrayobject.c
+++ b/Objects/bytearrayobject.c
@@ -1072,17 +1072,26 @@ bytearray___init___impl(PyByteArrayObject *self, 
PyObject *arg,
         /* Interpret it as an int (__index__) */
         rc = _getbytevalue(item, &value);
         Py_DECREF(item);
-        if (!rc)
+        if (!rc) {
             goto error;
+        }
+
+        /* Append the byte.
 
-        /* Append the byte */
-        if (Py_SIZE(self) + 1 < self->ob_alloc) {
+           gh-158928: Iterators are arbitrary code which could modify the
+           bytearray so this must re-calculate if there is enough space(). */
+        Py_ssize_t needed =
+            self->ob_start - self->ob_bytes + Py_SIZE(self) + 1;
+        if (needed < self->ob_alloc) {
             Py_SET_SIZE(self, Py_SIZE(self) + 1);
             PyByteArray_AS_STRING(self)[Py_SIZE(self)] = '\0';
         }
-        else if (PyByteArray_Resize((PyObject *)self, Py_SIZE(self)+1) < 0)
+        else if (PyByteArray_Resize((PyObject *)self, Py_SIZE(self)+1) < 0) {
             goto error;
+        }
         PyByteArray_AS_STRING(self)[Py_SIZE(self)-1] = value;
+        assert(self->ob_start - self->ob_bytes + Py_SIZE(self) <=
+               self->ob_alloc);
     }
 
     /* Clean up and return success */

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]

Reply via email to