https://github.com/python/cpython/commit/8bfda5553c0f8ac1163b4ce4ca7b8b632a853f51
commit: 8bfda5553c0f8ac1163b4ce4ca7b8b632a853f51
branch: main
author: Jelle Zijlstra <[email protected]>
committer: JelleZijlstra <[email protected]>
date: 2026-10-22T15:26:56-07:00
summary:

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.

files:
A Misc/NEWS.d/next/Library/2026-10-21-21-26-10.gh-issue-159739.rVWF2a.rst
M Lib/test/test_io/test_memoryio.py
M Modules/_io/bytesio.c

diff --git a/Lib/test/test_io/test_memoryio.py 
b/Lib/test/test_io/test_memoryio.py
index 90bdc791787439..999bd5bc007b2e 100644
--- a/Lib/test/test_io/test_memoryio.py
+++ b/Lib/test/test_io/test_memoryio.py
@@ -962,6 +962,28 @@ class CBytesIOTest(PyBytesIOTest):
     ioclass = io.BytesIO
     UnsupportedOperation = io.UnsupportedOperation
 
+    def test_truncate_size_changes_stream(self):
+        class Size:
+            def __init__(self, action):
+                self.action = action
+
+            def __index__(self):
+                self.result = self.action()
+                return 0
+
+        stream = self.ioclass(b'abc')
+        with self.assertRaises(ValueError):
+            stream.truncate(Size(stream.close))
+
+        with self.ioclass(b'abc') as stream:
+            size = Size(stream.getbuffer)
+            try:
+                with self.assertRaises(BufferError):
+                    stream.truncate(size)
+                self.assertEqual(stream.getvalue(), b'abc')
+            finally:
+                size.result.release()
+
     def test_readinto_overlapping_buffer(self):
         with self.ioclass(b'abcd') as stream:
             with stream.getbuffer()[1:] as target:
diff --git 
a/Misc/NEWS.d/next/Library/2026-10-21-21-26-10.gh-issue-159739.rVWF2a.rst 
b/Misc/NEWS.d/next/Library/2026-10-21-21-26-10.gh-issue-159739.rVWF2a.rst
new file mode 100644
index 00000000000000..f2e13ee926eaa1
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2026-10-21-21-26-10.gh-issue-159739.rVWF2a.rst
@@ -0,0 +1 @@
+Fix the ``truncate`` method on :class:`io.BytesIO` when converting its size 
argument closes the stream or exports its buffer.
diff --git a/Modules/_io/bytesio.c b/Modules/_io/bytesio.c
index 2374a43ef74eab..aa74d9b0c721a4 100644
--- a/Modules/_io/bytesio.c
+++ b/Modules/_io/bytesio.c
@@ -754,6 +754,8 @@ _io_BytesIO_truncate_impl(bytesio *self, PyObject *size)
                          "negative size value %zd", new_size);
             return NULL;
         }
+        CHECK_CLOSED(self);
+        CHECK_EXPORTS(self);
     }
 
     if (new_size < self->string_size) {

_______________________________________________
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]

Reply via email to