From eb2b3cdffe8bb41e9757121d3fd111b3b6188a83 Mon Sep 17 00:00:00 2001 From: sobolevn Date: Sun, 24 Nov 2024 14:20:55 +0300 Subject: [PATCH 1/3] gh-127182: Fix `io.StringIO.__setstate__` crash when `None` is the first value --- Lib/test/test_io.py | 15 ++++++++++ ...-11-24-14-20-17.gh-issue-127182.WmfY2g.rst | 2 ++ Modules/_io/stringio.c | 30 ++++++++++--------- 3 files changed, 33 insertions(+), 14 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2024-11-24-14-20-17.gh-issue-127182.WmfY2g.rst diff --git a/Lib/test/test_io.py b/Lib/test/test_io.py index aa1b8268592ff7..0a80a0e856447a 100644 --- a/Lib/test/test_io.py +++ b/Lib/test/test_io.py @@ -1148,6 +1148,21 @@ def test_disallow_instantiation(self): _io = self._io support.check_disallow_instantiation(self, _io._BytesIOBuffer) + def test_stringio_setstate(self): + # See https://github.com/python/cpython/issues/127182 + obj = self._io.StringIO() + with self.assertRaisesRegex( + TypeError, + 'initial_value must be str or None, not int', + ): + obj.__setstate__((1, '', 0, {})) + + obj.__setstate__((None, '', 0, {})) # should not crash + self.assertEqual(obj.getvalue(), '') + + obj.__setstate__(('', '', 0, {})) + self.assertEqual(obj.getvalue(), '') + class PyIOTest(IOTest): pass diff --git a/Misc/NEWS.d/next/Library/2024-11-24-14-20-17.gh-issue-127182.WmfY2g.rst b/Misc/NEWS.d/next/Library/2024-11-24-14-20-17.gh-issue-127182.WmfY2g.rst new file mode 100644 index 00000000000000..6a41532d2c45e2 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-11-24-14-20-17.gh-issue-127182.WmfY2g.rst @@ -0,0 +1,2 @@ +Fix :meth:`!io.StringIO.__setstate__` crash, when :const`None` was passed as +the first value. diff --git a/Modules/_io/stringio.c b/Modules/_io/stringio.c index f558613dc6233c..65e8d97aa8ac19 100644 --- a/Modules/_io/stringio.c +++ b/Modules/_io/stringio.c @@ -908,23 +908,25 @@ _io_StringIO___setstate___impl(stringio *self, PyObject *state) once by __init__. So we do not take any chance and replace object's buffer completely. */ { - PyObject *item; - Py_UCS4 *buf; - Py_ssize_t bufsize; - - item = PyTuple_GET_ITEM(state, 0); - buf = PyUnicode_AsUCS4Copy(item); - if (buf == NULL) - return NULL; - bufsize = PyUnicode_GET_LENGTH(item); + PyObject *item = PyTuple_GET_ITEM(state, 0); + if (PyUnicode_Check(item)) { + Py_UCS4 *buf = PyUnicode_AsUCS4Copy(item); + if (buf == NULL) + return NULL; + Py_ssize_t bufsize = PyUnicode_GET_LENGTH(item); - if (resize_buffer(self, bufsize) < 0) { + if (resize_buffer(self, bufsize) < 0) { + PyMem_Free(buf); + return NULL; + } + memcpy(self->buf, buf, bufsize * sizeof(Py_UCS4)); PyMem_Free(buf); - return NULL; + self->string_size = bufsize; + } + else { + assert(item == Py_None); + self->string_size = 0; } - memcpy(self->buf, buf, bufsize * sizeof(Py_UCS4)); - PyMem_Free(buf); - self->string_size = bufsize; } /* Set carefully the position value. Alternatively, we could use the seek From f7c1cce08771b5231fd1458a259aab1d2dcff5be Mon Sep 17 00:00:00 2001 From: sobolevn Date: Sun, 24 Nov 2024 14:30:29 +0300 Subject: [PATCH 2/3] Fix typo --- .../next/Library/2024-11-24-14-20-17.gh-issue-127182.WmfY2g.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Library/2024-11-24-14-20-17.gh-issue-127182.WmfY2g.rst b/Misc/NEWS.d/next/Library/2024-11-24-14-20-17.gh-issue-127182.WmfY2g.rst index 6a41532d2c45e2..2cc46ca3d33977 100644 --- a/Misc/NEWS.d/next/Library/2024-11-24-14-20-17.gh-issue-127182.WmfY2g.rst +++ b/Misc/NEWS.d/next/Library/2024-11-24-14-20-17.gh-issue-127182.WmfY2g.rst @@ -1,2 +1,2 @@ -Fix :meth:`!io.StringIO.__setstate__` crash, when :const`None` was passed as +Fix :meth:`!io.StringIO.__setstate__` crash, when :const:`None` was passed as the first value. From 3ffff8eb6647b991155154fcff1160faa979fddd Mon Sep 17 00:00:00 2001 From: sobolevn Date: Mon, 25 Nov 2024 13:38:16 +0300 Subject: [PATCH 3/3] Update Lib/test/test_io.py Co-authored-by: Victor Stinner --- Lib/test/test_io.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_io.py b/Lib/test/test_io.py index 0a80a0e856447a..f1f8ce57668f3b 100644 --- a/Lib/test/test_io.py +++ b/Lib/test/test_io.py @@ -1149,7 +1149,7 @@ def test_disallow_instantiation(self): support.check_disallow_instantiation(self, _io._BytesIOBuffer) def test_stringio_setstate(self): - # See https://github.com/python/cpython/issues/127182 + # gh-127182: Calling __setstate__() with invalid arguments must not crash obj = self._io.StringIO() with self.assertRaisesRegex( TypeError,