Skip to content

Commit 8bfda55

Browse files
authored
gh-159739: Recheck BytesIO state after converting truncate size (#160190)
A size object's __index__() can close a BytesIO or export its buffer after truncate() checks the stream. Continuing can dereference a cleared buffer or resize storage behind a live view. Recheck the stream state after size conversion. Backport to 3.15 and 3.14; on 3.14, put the regression test in Lib/test/test_memoryio.py.
1 parent a076a55 commit 8bfda55

3 files changed

Lines changed: 25 additions & 0 deletions

File tree

‎Lib/test/test_io/test_memoryio.py‎

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -962,6 +962,28 @@ class CBytesIOTest(PyBytesIOTest):
962962
ioclass = io.BytesIO
963963
UnsupportedOperation = io.UnsupportedOperation
964964

965+
def test_truncate_size_changes_stream(self):
966+
class Size:
967+
def __init__(self, action):
968+
self.action = action
969+
970+
def __index__(self):
971+
self.result = self.action()
972+
return 0
973+
974+
stream = self.ioclass(b'abc')
975+
with self.assertRaises(ValueError):
976+
stream.truncate(Size(stream.close))
977+
978+
with self.ioclass(b'abc') as stream:
979+
size = Size(stream.getbuffer)
980+
try:
981+
with self.assertRaises(BufferError):
982+
stream.truncate(size)
983+
self.assertEqual(stream.getvalue(), b'abc')
984+
finally:
985+
size.result.release()
986+
965987
def test_readinto_overlapping_buffer(self):
966988
with self.ioclass(b'abcd') as stream:
967989
with stream.getbuffer()[1:] as target:
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix the ``truncate`` method on :class:`io.BytesIO` when converting its size argument closes the stream or exports its buffer.

‎Modules/_io/bytesio.c‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -754,6 +754,8 @@ _io_BytesIO_truncate_impl(bytesio *self, PyObject *size)
754754
"negative size value %zd", new_size);
755755
return NULL;
756756
}
757+
CHECK_CLOSED(self);
758+
CHECK_EXPORTS(self);
757759
}
758760

759761
if (new_size < self->string_size) {

0 commit comments

Comments
 (0)