https://github.com/python/cpython/commit/6d54b6ac7d5744e1f59d784c8e020d632d2959a3
commit: 6d54b6ac7d5744e1f59d784c8e020d632d2959a3
branch: main
author: zhong <[email protected]>
committer: serhiy-storchaka <[email protected]>
date: 2026-01-09T13:50:56+02:00
summary:
gh-143378: Fix use-after-free when BytesIO is concurrently mutated during write
operations (GH-143408)
PyObject_GetBuffer() can execute user code (e.g. via __buffer__), which may
close or otherwise mutate a BytesIO object while write() or writelines()
is in progress. This could invalidate the internal buffer and lead to a
use-after-free.
Ensure that PyObject_GetBuffer() is called before validation checks.
files:
A Misc/NEWS.d/next/Library/2026-01-03-19-41-36.gh-issue-143378.29AvE7.rst
M Lib/_pyio.py
M Lib/test/test_io/test_memoryio.py
M Modules/_io/bytesio.c
diff --git a/Lib/_pyio.py b/Lib/_pyio.py
index 69a088df8fc987..77c44addabf225 100644
--- a/Lib/_pyio.py
+++ b/Lib/_pyio.py
@@ -949,12 +949,12 @@ def read1(self, size=-1):
return self.read(size)
def write(self, b):
- if self.closed:
- raise ValueError("write to closed file")
if isinstance(b, str):
raise TypeError("can't write str to binary stream")
with memoryview(b) as view:
n = view.nbytes # Size of any bytes-like object
+ if self.closed:
+ raise ValueError("write to closed file")
if n == 0:
return 0
diff --git a/Lib/test/test_io/test_memoryio.py
b/Lib/test/test_io/test_memoryio.py
index bb023735e21398..f730e38a5d6485 100644
--- a/Lib/test/test_io/test_memoryio.py
+++ b/Lib/test/test_io/test_memoryio.py
@@ -587,6 +587,48 @@ def test_issue5449(self):
self.ioclass(initial_bytes=buf)
self.assertRaises(TypeError, self.ioclass, buf, foo=None)
+ def test_write_concurrent_close(self):
+ class B:
+ def __buffer__(self, flags):
+ memio.close()
+ return memoryview(b"A")
+
+ memio = self.ioclass()
+ self.assertRaises(ValueError, memio.write, B())
+
+ # Prevent crashes when memio.write() or memio.writelines()
+ # concurrently mutates (e.g., closes or exports) 'memio'.
+ # See: https://github.com/python/cpython/issues/143378.
+
+ def test_writelines_concurrent_close(self):
+ class B:
+ def __buffer__(self, flags):
+ memio.close()
+ return memoryview(b"A")
+
+ memio = self.ioclass()
+ self.assertRaises(ValueError, memio.writelines, [B()])
+
+ def test_write_concurrent_export(self):
+ class B:
+ buf = None
+ def __buffer__(self, flags):
+ self.buf = memio.getbuffer()
+ return memoryview(b"A")
+
+ memio = self.ioclass()
+ self.assertRaises(BufferError, memio.write, B())
+
+ def test_writelines_concurrent_export(self):
+ class B:
+ buf = None
+ def __buffer__(self, flags):
+ self.buf = memio.getbuffer()
+ return memoryview(b"A")
+
+ memio = self.ioclass()
+ self.assertRaises(BufferError, memio.writelines, [B()])
+
class TextIOTestMixin:
diff --git
a/Misc/NEWS.d/next/Library/2026-01-03-19-41-36.gh-issue-143378.29AvE7.rst
b/Misc/NEWS.d/next/Library/2026-01-03-19-41-36.gh-issue-143378.29AvE7.rst
new file mode 100644
index 00000000000000..57bbb4d0a1399c
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2026-01-03-19-41-36.gh-issue-143378.29AvE7.rst
@@ -0,0 +1 @@
+Fix use-after-free crashes when a :class:`~io.BytesIO` object is concurrently
mutated during :meth:`~io.RawIOBase.write` or :meth:`~io.IOBase.writelines`.
diff --git a/Modules/_io/bytesio.c b/Modules/_io/bytesio.c
index 96611823ab6b45..d088bb0efac797 100644
--- a/Modules/_io/bytesio.c
+++ b/Modules/_io/bytesio.c
@@ -194,18 +194,18 @@ write_bytes_lock_held(bytesio *self, PyObject *b)
{
_Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(self);
- if (check_closed(self)) {
- return -1;
- }
- if (check_exports(self)) {
- return -1;
- }
-
Py_buffer buf;
+ Py_ssize_t len;
if (PyObject_GetBuffer(b, &buf, PyBUF_CONTIG_RO) < 0) {
return -1;
}
- Py_ssize_t len = buf.len;
+
+ if (check_closed(self) || check_exports(self)) {
+ len = -1;
+ goto done;
+ }
+
+ len = buf.len;
if (len == 0) {
goto done;
}
_______________________________________________
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]