Skip to content

Commit fad1503

Browse files
Fix bytearray.take_bytes() corrupting shared single-byte bytes objects
1 parent e56f86f commit fad1503

3 files changed

Lines changed: 16 additions & 3 deletions

File tree

‎Lib/test/test_bytes.py‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1611,6 +1611,14 @@ def test_take_bytes(self):
16111611
self.assertRaises(BufferError, ba.take_bytes)
16121612
self.assertEqual(ba.take_bytes(), b'abc')
16131613

1614+
# Leaving one byte must not adopt the shared single-byte bytes object
1615+
# as the buffer.
1616+
ba = bytearray(b'abc')
1617+
self.assertEqual(ba.take_bytes(2), b'ab')
1618+
ba[0] = ord('A')
1619+
self.assertEqual(ba, bytearray(b'A'))
1620+
self.assertEqual(ord(b'c'), ord('c'))
1621+
16141622
@support.cpython_only # tests an implementation detail
16151623
def test_take_bytes_optimization(self):
16161624
# Validate optimization around taking lots of little chunks out of a
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Fix :meth:`bytearray.take_bytes` leaving the bytearray with a shared buffer
2+
when exactly one byte remained, so that writing to the bytearray modified
3+
the single-byte :class:`bytes` object with the same value.

‎Objects/bytearrayobject.c‎

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1619,12 +1619,14 @@ bytearray_take_bytes_impl(PyByteArrayObject *self, PyObject *n)
16191619
return ret;
16201620
}
16211621

1622-
// Copy remaining bytes to a new bytes.
1623-
PyObject *remaining = PyBytes_FromStringAndSize(self->ob_start + to_take,
1624-
remaining_length);
1622+
// Copy remaining bytes to a new bytes. Allocate and then copy rather than
1623+
// so we don't get a shared immortal one-character singleton!
1624+
PyObject *remaining = PyBytes_FromStringAndSize(NULL, remaining_length);
16251625
if (remaining == NULL) {
16261626
return NULL;
16271627
}
1628+
memcpy(PyBytes_AS_STRING(remaining), self->ob_start + to_take,
1629+
remaining_length);
16281630

16291631
// If the bytes are offset inside the buffer must first align.
16301632
if (self->ob_start != self->ob_bytes) {

0 commit comments

Comments
 (0)