Skip to content

Commit e675e37

Browse files
authored
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__.
1 parent 60dff5a commit e675e37

3 files changed

Lines changed: 30 additions & 0 deletions

File tree

Lib/test/test_bytes.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1828,6 +1828,30 @@ def test_setslice_trap(self):
18281828
b[8:] = b
18291829
self.assertEqual(b, bytearray(list(range(8)) + list(range(256))))
18301830

1831+
def test_setslice_reentrant_resize(self):
1832+
# gh-153578: a buffer argument whose __buffer__ resizes the bytearray
1833+
# while the buffer is being acquired must not leave the slice bounds
1834+
# with lo > hi, which drove a negative-size memmove (an out-of-bounds
1835+
# write) in the setslice path reached through extend().
1836+
class Evil:
1837+
def __init__(self, resize):
1838+
self.resize = resize
1839+
def __buffer__(self, flags):
1840+
self.resize()
1841+
return memoryview(b'ABCDEFGH')
1842+
# clear() during __buffer__: extend appends to the emptied bytearray.
1843+
b = bytearray(b'x' * 100)
1844+
b.extend(Evil(b.clear))
1845+
self.assertEqual(b, b'ABCDEFGH')
1846+
# partial shrink during __buffer__.
1847+
b = bytearray(b'x' * 100)
1848+
b.extend(Evil(lambda: b.__delitem__(slice(30, None))))
1849+
self.assertEqual(b, b'x' * 30 + b'ABCDEFGH')
1850+
# grow during __buffer__: the data lands at the original end.
1851+
b = bytearray(b'x' * 10)
1852+
b.extend(Evil(lambda: b.extend(b'y' * 100)))
1853+
self.assertEqual(b, b'x' * 10 + b'ABCDEFGH' + b'y' * 100)
1854+
18311855
def test_iconcat(self):
18321856
b = bytearray(b"abc")
18331857
b1 = b
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Fix an out-of-bounds write in :meth:`bytearray.extend` when the bytearray is
2+
resized while the argument's :meth:`~object.__buffer__` is being acquired, for
3+
example by another thread. Patch by tonghuaroot.

Objects/bytearrayobject.c

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

684+
// gh-153578: __buffer__() may have resized self; re-clamp both bounds.
684685
if (lo < 0)
685686
lo = 0;
687+
else if (lo > Py_SIZE(self))
688+
lo = Py_SIZE(self);
686689
if (hi < lo)
687690
hi = lo;
688691
if (hi > Py_SIZE(self))

0 commit comments

Comments
 (0)