mirror of
https://github.com/python/cpython.git
synced 2026-06-17 15:16:42 +00:00
[3.14] gh-143378: Fix use-after-free when BytesIO is concurrently mutated during write operations (GH-143408) (GH-143599)
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.
(cherry picked from commit 6d54b6ac7d)
Co-authored-by: zhong <60600792+superboy-zjc@users.noreply.github.com>
This commit is contained in:
parent
a299c1b185
commit
f264f103d4
4 changed files with 53 additions and 10 deletions
|
|
@ -935,12 +935,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
|
||||
pos = self._pos
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
||||
|
|
|
|||
|
|
@ -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`.
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue