diff --git a/Lib/test/test_bytes.py b/Lib/test/test_bytes.py index 96190a7f5821709..4d0c615fd09b115 100644 --- a/Lib/test/test_bytes.py +++ b/Lib/test/test_bytes.py @@ -2085,6 +2085,22 @@ def g(): alloc = b.__alloc__() self.assertGreater(alloc, len(b)) + def test_init_from_iterator_with_offset(self): + # gh-158928: Inserting form 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 000000000000000..2b9597c5ed94f32 --- /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 496d04a1704d46d..418f37e861e9a5a 100644 --- a/Objects/bytearrayobject.c +++ b/Objects/bytearrayobject.c @@ -1158,17 +1158,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); bytearray_write_trailing_null_byte(self); } - 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 */