From 426d4957f5d79e468b192b17cc7686f68079f7f3 Mon Sep 17 00:00:00 2001 From: furkanonder Date: Sun, 4 Jun 2023 23:56:09 +0300 Subject: [PATCH 1/6] Prevent memoryview points to freed heap memory. --- Modules/_io/bufferedio.c | 23 +++++++++++++++++++++-- Objects/unicodeobject.c | 9 +++++++++ 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/Modules/_io/bufferedio.c b/Modules/_io/bufferedio.c index f30d54a5e11b0a..97e98e4be7f6f1 100644 --- a/Modules/_io/bufferedio.c +++ b/Modules/_io/bufferedio.c @@ -1544,7 +1544,8 @@ _bufferedreader_raw_read(buffered *self, char *start, Py_ssize_t len) Py_buffer buf; PyObject *memobj, *res; Py_ssize_t n; - /* NOTE: the buffer needn't be released as its object is NULL. */ + PyObject *exc, *val, *tb, *release_res; + /* The buffer will be released when raw.readinto() returns. */ if (PyBuffer_FillInfo(&buf, NULL, start, len, 0, PyBUF_CONTIG) == -1) return -1; memobj = PyMemoryView_FromBuffer(&buf); @@ -1558,7 +1559,15 @@ _bufferedreader_raw_read(buffered *self, char *start, Py_ssize_t len) do { res = PyObject_CallMethodOneArg(self->raw, &_Py_ID(readinto), memobj); } while (res == NULL && _PyIO_trap_eintr()); + PyErr_Fetch(&exc, &val, &tb); + release_res = PyObject_CallMethod(memobj, "release", NULL); + _PyErr_ChainExceptions(exc, val, tb); Py_DECREF(memobj); + if (release_res == NULL) { + Py_XDECREF(res); + return -1; + } + Py_DECREF(release_res); if (res == NULL) return -1; if (res == Py_None) { @@ -1903,7 +1912,8 @@ _bufferedwriter_raw_write(buffered *self, char *start, Py_ssize_t len) PyObject *memobj, *res; Py_ssize_t n; int errnum; - /* NOTE: the buffer needn't be released as its object is NULL. */ + PyObject *exc, *val, *tb, *release_res; + /* The buffer will be released when raw.write() returns. */ if (PyBuffer_FillInfo(&buf, NULL, start, len, 1, PyBUF_CONTIG_RO) == -1) return -1; memobj = PyMemoryView_FromBuffer(&buf); @@ -1919,6 +1929,15 @@ _bufferedwriter_raw_write(buffered *self, char *start, Py_ssize_t len) res = PyObject_CallMethodOneArg(self->raw, &_Py_ID(write), memobj); errnum = errno; } while (res == NULL && _PyIO_trap_eintr()); + PyErr_Fetch(&exc, &val, &tb); + release_res = PyObject_CallMethod(memobj, "release", NULL); + _PyErr_ChainExceptions(exc, val, tb); + Py_DECREF(memobj); + if (release_res == NULL) { + Py_XDECREF(res); + return -1; + } + Py_DECREF(release_res); Py_DECREF(memobj); if (res == NULL) return -1; diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c index ffb4a87d4b9265..a82b03cad30981 100644 --- a/Objects/unicodeobject.c +++ b/Objects/unicodeobject.c @@ -3189,6 +3189,7 @@ PyUnicode_Decode(const char *s, PyObject *buffer = NULL, *unicode; Py_buffer info; char buflower[11]; /* strlen("iso-8859-1\0") == 11, longest shortcut */ + PyObject *exc, *val, *tb, *res; if (unicode_check_encoding_errors(encoding, errors) < 0) { return NULL; @@ -3251,6 +3252,14 @@ PyUnicode_Decode(const char *s, if (buffer == NULL) goto onError; unicode = _PyCodec_DecodeText(buffer, encoding, errors); + PyErr_Fetch(&exc, &val, &tb); + res = PyObject_CallMethod(buffer, "release", NULL); + _PyErr_ChainExceptions(exc, val, tb); + if (res == NULL) { + Py_XDECREF(unicode); + goto onError; + } + Py_DECREF(res); if (unicode == NULL) goto onError; if (!PyUnicode_Check(unicode)) { From 1809a2ac371726d24a522e948f51727fc9dca8fd Mon Sep 17 00:00:00 2001 From: furkanonder Date: Sun, 23 Jul 2023 00:37:27 +0300 Subject: [PATCH 2/6] Don't reduce ref count for memobj in _bufferedwriter_raw_write --- Modules/_io/bufferedio.c | 1 - 1 file changed, 1 deletion(-) diff --git a/Modules/_io/bufferedio.c b/Modules/_io/bufferedio.c index b1cd3de90805e9..33c0d6f2fc54a1 100644 --- a/Modules/_io/bufferedio.c +++ b/Modules/_io/bufferedio.c @@ -1933,7 +1933,6 @@ _bufferedwriter_raw_write(buffered *self, char *start, Py_ssize_t len) PyErr_Fetch(&exc, &val, &tb); release_res = PyObject_CallMethod(memobj, "release", NULL); _PyErr_ChainExceptions(exc, val, tb); - Py_DECREF(memobj); if (release_res == NULL) { Py_XDECREF(res); return -1; From 7b2cddad8200164be959dd9b2372fe3d2cc063d6 Mon Sep 17 00:00:00 2001 From: furkanonder Date: Sun, 23 Jul 2023 01:06:56 +0300 Subject: [PATCH 3/6] Use _PyErr_ChainExceptions1 instead of _PyErr_ChainExceptions --- Modules/_io/bufferedio.c | 12 ++++++------ Objects/unicodeobject.c | 6 +++--- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Modules/_io/bufferedio.c b/Modules/_io/bufferedio.c index 33c0d6f2fc54a1..d32941ee2f0e2d 100644 --- a/Modules/_io/bufferedio.c +++ b/Modules/_io/bufferedio.c @@ -1545,7 +1545,7 @@ _bufferedreader_raw_read(buffered *self, char *start, Py_ssize_t len) Py_buffer buf; PyObject *memobj, *res; Py_ssize_t n; - PyObject *exc, *val, *tb, *release_res; + PyObject *release_res; /* The buffer will be released when raw.readinto() returns. */ if (PyBuffer_FillInfo(&buf, NULL, start, len, 0, PyBUF_CONTIG) == -1) return -1; @@ -1560,9 +1560,9 @@ _bufferedreader_raw_read(buffered *self, char *start, Py_ssize_t len) do { res = PyObject_CallMethodOneArg(self->raw, &_Py_ID(readinto), memobj); } while (res == NULL && _PyIO_trap_eintr()); - PyErr_Fetch(&exc, &val, &tb); + PyObject *exc = PyErr_GetRaisedException(); release_res = PyObject_CallMethod(memobj, "release", NULL); - _PyErr_ChainExceptions(exc, val, tb); + _PyErr_ChainExceptions1(exc); Py_DECREF(memobj); if (release_res == NULL) { Py_XDECREF(res); @@ -1913,7 +1913,7 @@ _bufferedwriter_raw_write(buffered *self, char *start, Py_ssize_t len) PyObject *memobj, *res; Py_ssize_t n; int errnum; - PyObject *exc, *val, *tb, *release_res; + PyObject *release_res; /* The buffer will be released when raw.write() returns. */ if (PyBuffer_FillInfo(&buf, NULL, start, len, 1, PyBUF_CONTIG_RO) == -1) return -1; @@ -1930,9 +1930,9 @@ _bufferedwriter_raw_write(buffered *self, char *start, Py_ssize_t len) res = PyObject_CallMethodOneArg(self->raw, &_Py_ID(write), memobj); errnum = errno; } while (res == NULL && _PyIO_trap_eintr()); - PyErr_Fetch(&exc, &val, &tb); + PyObject *exc = PyErr_GetRaisedException(); release_res = PyObject_CallMethod(memobj, "release", NULL); - _PyErr_ChainExceptions(exc, val, tb); + _PyErr_ChainExceptions1(exc); if (release_res == NULL) { Py_XDECREF(res); return -1; diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c index fede3edea755b9..237ecfffe7680b 100644 --- a/Objects/unicodeobject.c +++ b/Objects/unicodeobject.c @@ -3190,7 +3190,7 @@ PyUnicode_Decode(const char *s, PyObject *buffer = NULL, *unicode; Py_buffer info; char buflower[11]; /* strlen("iso-8859-1\0") == 11, longest shortcut */ - PyObject *exc, *val, *tb, *res; + PyObject *res; if (unicode_check_encoding_errors(encoding, errors) < 0) { return NULL; @@ -3253,9 +3253,9 @@ PyUnicode_Decode(const char *s, if (buffer == NULL) goto onError; unicode = _PyCodec_DecodeText(buffer, encoding, errors); - PyErr_Fetch(&exc, &val, &tb); + PyObject *exc = PyErr_GetRaisedException(); res = PyObject_CallMethod(buffer, "release", NULL); - _PyErr_ChainExceptions(exc, val, tb); + _PyErr_ChainExceptions1(exc); if (res == NULL) { Py_XDECREF(unicode); goto onError; From 83a4bf4da1612b1290392713e6105c885fd188d1 Mon Sep 17 00:00:00 2001 From: "blurb-it[bot]" <43283697+blurb-it[bot]@users.noreply.github.com> Date: Sun, 23 Jul 2023 07:44:01 +0000 Subject: [PATCH 4/6] =?UTF-8?q?=F0=9F=93=9C=F0=9F=A4=96=20Added=20by=20blu?= =?UTF-8?q?rb=5Fit.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../2023-07-23-07-43-57.gh-issue-60198.fysv2O.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-07-23-07-43-57.gh-issue-60198.fysv2O.rst diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-07-23-07-43-57.gh-issue-60198.fysv2O.rst b/Misc/NEWS.d/next/Core and Builtins/2023-07-23-07-43-57.gh-issue-60198.fysv2O.rst new file mode 100644 index 00000000000000..a42a6b3881ac82 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-07-23-07-43-57.gh-issue-60198.fysv2O.rst @@ -0,0 +1 @@ +Prevent :class:`memoryview` objects from pointing to free heap memory. From ad03e4a718ada2741d94d3164c0ee6469f041ecc Mon Sep 17 00:00:00 2001 From: furkanonder Date: Sun, 23 Jul 2023 10:49:33 +0300 Subject: [PATCH 5/6] Mention Co-Author in the News entry Co-authored-by: Martin Panter vadmium+py@gmail.com --- .../2023-07-23-07-43-57.gh-issue-60198.fysv2O.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-07-23-07-43-57.gh-issue-60198.fysv2O.rst b/Misc/NEWS.d/next/Core and Builtins/2023-07-23-07-43-57.gh-issue-60198.fysv2O.rst index a42a6b3881ac82..bfb5fdcd241cd3 100644 --- a/Misc/NEWS.d/next/Core and Builtins/2023-07-23-07-43-57.gh-issue-60198.fysv2O.rst +++ b/Misc/NEWS.d/next/Core and Builtins/2023-07-23-07-43-57.gh-issue-60198.fysv2O.rst @@ -1 +1 @@ -Prevent :class:`memoryview` objects from pointing to free heap memory. +Prevent :class:`memoryview` objects from pointing to free heap memory. Patch by Martin Panter and Furkan Onder. From 7a81f608cea4af6d0c200b8039f96489bfaab69e Mon Sep 17 00:00:00 2001 From: furkanonder Date: Sun, 23 Jul 2023 18:14:04 +0300 Subject: [PATCH 6/6] Add test case to reproduce issue --- Lib/test/test_memoryview.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/Lib/test/test_memoryview.py b/Lib/test/test_memoryview.py index 0eb2a367603cfc..0eac7bc06adf71 100644 --- a/Lib/test/test_memoryview.py +++ b/Lib/test/test_memoryview.py @@ -656,5 +656,30 @@ def __bool__(self): m[0] = MyBool() self.assertEqual(ba[:8], b'\0'*8) + + def test_gh60198(self): + global view + + class File(io.RawIOBase): + def readinto(self, buf): + global view + view = buf + + def readable(self): + return True + + f = io.BufferedReader(File()) + # get view of buffer used by BufferedReader + f.read(1) + # deallocate buffer + del f + + with self.assertRaises(ValueError): + view = view.cast('P') + L = [None] * len(view) + # overwrite first item with NULL + view[0] = 0 + print(L[0]) + if __name__ == "__main__": unittest.main()