Skip to content

Commit 0bb3bef

Browse files
committed
gh-153578: Fix out-of-bounds write in bytearray.extend() with a reentrant __buffer__
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__.
1 parent c22e9c9 commit 0bb3bef

3 files changed

Lines changed: 25 additions & 0 deletions

File tree

Lib/test/test_bytes.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1799,6 +1799,26 @@ def test_setslice_trap(self):
17991799
b[8:] = b
18001800
self.assertEqual(b, bytearray(list(range(8)) + list(range(256))))
18011801

1802+
def test_setslice_reentrant_resize(self):
1803+
# gh-153578: a buffer argument whose __buffer__ resizes the bytearray
1804+
# while the buffer is being acquired must not leave the slice bounds
1805+
# with lo > hi, which drove a negative-size memmove (an out-of-bounds
1806+
# write) in the setslice path reached through extend().
1807+
class Evil:
1808+
def __init__(self, resize):
1809+
self.resize = resize
1810+
def __buffer__(self, flags):
1811+
self.resize()
1812+
return memoryview(b'ABCDEFGH')
1813+
# clear() during __buffer__: extend appends to the emptied bytearray.
1814+
b = bytearray(b'x' * 100)
1815+
b.extend(Evil(b.clear))
1816+
self.assertEqual(b, b'ABCDEFGH')
1817+
# partial shrink during __buffer__.
1818+
b = bytearray(b'x' * 100)
1819+
b.extend(Evil(lambda: b.__delitem__(slice(30, None))))
1820+
self.assertEqual(b, b'x' * 30 + b'ABCDEFGH')
1821+
18021822
def test_iconcat(self):
18031823
b = bytearray(b"abc")
18041824
b1 = b
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix a crash in :meth:`bytearray.extend` when the argument's
2+
:meth:`~object.__buffer__` method resizes the bytearray. Patch by tonghuaroot.

Objects/bytearrayobject.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -670,8 +670,11 @@ bytearray_setslice(PyByteArrayObject *self, Py_ssize_t lo, Py_ssize_t hi,
670670
bytes = vbytes.buf;
671671
}
672672

673+
// gh-153578: __buffer__() may have resized self; re-clamp both bounds.
673674
if (lo < 0)
674675
lo = 0;
676+
else if (lo > Py_SIZE(self))
677+
lo = Py_SIZE(self);
675678
if (hi < lo)
676679
hi = lo;
677680
if (hi > Py_SIZE(self))

0 commit comments

Comments
 (0)