gh-153578: Fix out-of-bounds write in bytearray.extend() with a reentrant __buffer__ (GH-153579)

serhiy-storchaka <[email protected]>
Newsgroups gmane.comp.python.cvs
Message-ID <[email protected]>
https://github.com/python/cpython/commit/e675e37421357cf0319c5bca2cec533f0909d5b8
commit: e675e37421357cf0319c5bca2cec533f0909d5b8
branch: main
author: tonghuaroot (童话) <[email protected]>
committer: serhiy-storchaka <[email protected]>
date: 2026-08-18T15:31:52+03:00
summary:

gh-153578: Fix out-of-bounds write in bytearray.extend() with a reentrant __buffer__ (GH-153579)

bytearray.extend() clamped only the high bound of the append range to the
current size after acquiring the argument's buffer, so a __buffer__ that shrinks
the bytearray left the low bound past the high bound and ran a negative-size
memmove. Clamp the low bound too, matching bytearray.__iadd__.

files:
A Misc/NEWS.d/next/Core_and_Builtins/2026-07-11-15-45-00.gh-issue-153578.Qm4Zt9.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 720b38cb508cbe..1b9918c6c8f473 100644
--- a/Lib/test/test_bytes.py
+++ b/Lib/test/test_bytes.py
@@ -1828,6 +1828,30 @@ def test_setslice_trap(self):
         b[8:] = b
         self.assertEqual(b, bytearray(list(range(8)) + list(range(256))))
 
+    def test_setslice_reentrant_resize(self):
+        # gh-153578: a buffer argument whose __buffer__ resizes the bytearray
+        # while the buffer is being acquired must not leave the slice bounds
+        # with lo > hi, which drove a negative-size memmove (an out-of-bounds
+        # write) in the setslice path reached through extend().
+        class Evil:
+            def __init__(self, resize):
+                self.resize = resize
+            def __buffer__(self, flags):
+                self.resize()
+                return memoryview(b'ABCDEFGH')
+        # clear() during __buffer__: extend appends to the emptied bytearray.
+        b = bytearray(b'x' * 100)
+        b.extend(Evil(b.clear))
+        self.assertEqual(b, b'ABCDEFGH')
+        # partial shrink during __buffer__.
+        b = bytearray(b'x' * 100)
+        b.extend(Evil(lambda: b.__delitem__(slice(30, None))))
+        self.assertEqual(b, b'x' * 30 + b'ABCDEFGH')
+        # grow during __buffer__: the data lands at the original end.
+        b = bytearray(b'x' * 10)
+        b.extend(Evil(lambda: b.extend(b'y' * 100)))
+        self.assertEqual(b, b'x' * 10 + b'ABCDEFGH' + b'y' * 100)
+
     def test_iconcat(self):
         b = bytearray(b"abc")
         b1 = b
diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-07-11-15-45-00.gh-issue-153578.Qm4Zt9.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-07-11-15-45-00.gh-issue-153578.Qm4Zt9.rst
new file mode 100644
index 00000000000000..2d5d4058a04ef2
--- /dev/null
+++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-07-11-15-45-00.gh-issue-153578.Qm4Zt9.rst
@@ -0,0 +1,3 @@
+Fix an out-of-bounds write in :meth:`bytearray.extend` when the bytearray is
+resized while the argument's :meth:`~object.__buffer__` is being acquired, for
+example by another thread. Patch by tonghuaroot.
diff --git a/Objects/bytearrayobject.c b/Objects/bytearrayobject.c
index d009877dc09fac..055fedc3ddfb03 100644
--- a/Objects/bytearrayobject.c
+++ b/Objects/bytearrayobject.c
@@ -681,8 +681,11 @@ bytearray_setslice(PyByteArrayObject *self, Py_ssize_t lo, Py_ssize_t hi,
         bytes = vbytes.buf;
     }
 
+    // gh-153578: __buffer__() may have resized self; re-clamp both bounds.
     if (lo < 0)
         lo = 0;
+    else if (lo > Py_SIZE(self))
+        lo = Py_SIZE(self);
     if (hi < lo)
         hi = lo;
     if (hi > Py_SIZE(self))

_______________________________________________
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]
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.