| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -607,6 +607,27 @@ def test_args_error(self): | |||
| 607 | 607 | with self.assertRaisesRegex(TypeError, "BufferedReader"): | |
| 608 | 608 | self.tp(self.BytesIO(), 1024, 1024, 1024) | |
| 609 | 609 | ||
| 610 | + def test_readinto_buffer_keeps_reader_alive(self): | ||
| 611 | + # gh-157364: raw.readinto() may store the memoryview it is given, so | ||
| 612 | + # the memoryview must keep the BufferedReader owning its memory alive. | ||
| 613 | + stored = [] | ||
| 614 | + | ||
| 615 | + class StoringRawIO(self.MockRawIO): | ||
| 616 | + def readinto(self, b): | ||
| 617 | + stored.append(b) | ||
| 618 | + return super().readinto(b) | ||
| 619 | + | ||
| 620 | + bufio = self.tp(StoringRawIO([b"abc"])) | ||
| 621 | + self.assertEqual(bufio.read(3), b"abc") | ||
| 622 | + self.assertIs(stored[0].obj, bufio) | ||
| 623 | + wr = weakref.ref(bufio) | ||
| 624 | + del bufio | ||
| 625 | + support.gc_collect() | ||
| 626 | + self.assertIsNotNone(wr()) | ||
| 627 | + stored.clear() | ||
| 628 | + support.gc_collect() | ||
| 629 | + self.assertIsNone(wr()) | ||
| 630 | + | ||
| 610 | 631 | def test_bad_readinto_value(self): | |
| 611 | 632 | rawio = self.tp(self.BytesIO(b"12")) | |
| 612 | 633 | rawio.readinto = lambda buf: -1 | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,2 +1,4 @@ | |||
| 1 | 1 | Fix a use-after-free in :class:`io.TextIOWrapper` when a call to the | |
| 2 | - underlying buffer reentrantly detaches it. Patched by Shamil Abdulaev. | ||
| 2 | + underlying buffer reentrantly detaches it. The :class:`memoryview` that | ||
| 3 | + :class:`io.BufferedReader` passes to the raw stream's ``readinto()`` now keeps | ||
| 4 | + the reader alive. Patched by Shamil Abdulaev. | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -10,6 +10,7 @@ | |||
| 10 | 10 | #include "Python.h" | |
| 11 | 11 | #include "pycore_call.h" // _PyObject_CallNoArgs() | |
| 12 | 12 | #include "pycore_fileutils.h" // _PyFile_Flush | |
| 13 | + #include "pycore_memoryobject.h" // _PyMemoryView_FromBufferProc() | ||
| 13 | 14 | #include "pycore_object.h" // _PyObject_GC_UNTRACK() | |
| 14 | 15 | #include "pycore_pyerrors.h" // _Py_FatalErrorFormat() | |
| 15 | 16 | #include "pycore_pylifecycle.h" // _Py_IsInterpreterFinalizing() | |
@@ -254,6 +255,10 @@ typedef struct { | |||
| 254 | 255 | isn't ready for writing. */ | |
| 255 | 256 | Py_off_t write_end; | |
| 256 | 257 | ||
| 258 | + /* Region handed to the raw stream by _bufferedreader_raw_read(). */ | ||
| 259 | + char *raw_read_start; | ||
| 260 | + Py_ssize_t raw_read_len; | ||
| 261 | + | ||
| 257 | 262 | PyThread_type_lock lock; | |
| 258 | 263 | volatile unsigned long owner; | |
| 259 | 264 | ||
@@ -1619,16 +1624,25 @@ _io_BufferedReader___init___impl(buffered *self, PyObject *raw, | |||
| 1619 | 1624 | return 0; | |
| 1620 | 1625 | } | |
| 1621 | 1626 | ||
| 1627 | + /* The memoryview passed to raw.readinto() owns a reference to the buffered | ||
| 1628 | + object so that storing it in Python code cannot outlive its memory. */ | ||
| 1629 | + static int | ||
| 1630 | + _bufferedreader_raw_read_getbuffer(PyObject *op, Py_buffer *view, int flags) | ||
| 1631 | + { | ||
| 1632 | + buffered *self = buffered_CAST(op); | ||
| 1633 | + return PyBuffer_FillInfo(view, op, self->raw_read_start, | ||
| 1634 | + self->raw_read_len, 0, flags); | ||
| 1635 | + } | ||
| 1636 | + | ||
| 1622 | 1637 | static Py_ssize_t | |
| 1623 | 1638 | _bufferedreader_raw_read(buffered *self, char *start, Py_ssize_t len) | |
| 1624 | 1639 | { | |
| 1625 | - Py_buffer buf; | ||
| 1626 | 1640 | PyObject *memobj, *res; | |
| 1627 | 1641 | Py_ssize_t n; | |
| 1628 | - /* NOTE: the buffer needn't be released as its object is NULL. */ | ||
| 1629 | - if (PyBuffer_FillInfo(&buf, NULL, start, len, 0, PyBUF_CONTIG) == -1) | ||
| 1630 | - return -1; | ||
| 1631 | - memobj = PyMemoryView_FromBuffer(&buf); | ||
| 1642 | + self->raw_read_start = start; | ||
| 1643 | + self->raw_read_len = len; | ||
| 1644 | + memobj = _PyMemoryView_FromBufferProc((PyObject *)self, PyBUF_CONTIG, | ||
| 1645 | + _bufferedreader_raw_read_getbuffer); | ||
| 1632 | 1646 | if (memobj == NULL) | |
| 1633 | 1647 | return -1; | |
| 1634 | 1648 | /* NOTE: PyErr_SetFromErrno() calls PyErr_CheckSignals() when EINTR | |
| Back | FazBrowse Home | New Git URL |
0 commit comments