Skip to content

Commit 49a5468

Browse files
sobolevnvstinner
authored andcommitted
pythongh-127182: Fix io.StringIO.__setstate__ crash when None is the first value (python#127219)
Co-authored-by: Victor Stinner <[email protected]>
1 parent ee57e6e commit 49a5468

File tree

3 files changed

+33
-14
lines changed

3 files changed

+33
-14
lines changed

Lib/test/test_io.py

+15
Original file line numberDiff line numberDiff line change
@@ -1148,6 +1148,21 @@ def test_disallow_instantiation(self):
11481148
_io = self._io
11491149
support.check_disallow_instantiation(self, _io._BytesIOBuffer)
11501150

1151+
def test_stringio_setstate(self):
1152+
# gh-127182: Calling __setstate__() with invalid arguments must not crash
1153+
obj = self._io.StringIO()
1154+
with self.assertRaisesRegex(
1155+
TypeError,
1156+
'initial_value must be str or None, not int',
1157+
):
1158+
obj.__setstate__((1, '', 0, {}))
1159+
1160+
obj.__setstate__((None, '', 0, {})) # should not crash
1161+
self.assertEqual(obj.getvalue(), '')
1162+
1163+
obj.__setstate__(('', '', 0, {}))
1164+
self.assertEqual(obj.getvalue(), '')
1165+
11511166
class PyIOTest(IOTest):
11521167
pass
11531168

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix :meth:`!io.StringIO.__setstate__` crash, when :const:`None` was passed as
2+
the first value.

Modules/_io/stringio.c

+16-14
Original file line numberDiff line numberDiff line change
@@ -908,23 +908,25 @@ _io_StringIO___setstate___impl(stringio *self, PyObject *state)
908908
once by __init__. So we do not take any chance and replace object's
909909
buffer completely. */
910910
{
911-
PyObject *item;
912-
Py_UCS4 *buf;
913-
Py_ssize_t bufsize;
914-
915-
item = PyTuple_GET_ITEM(state, 0);
916-
buf = PyUnicode_AsUCS4Copy(item);
917-
if (buf == NULL)
918-
return NULL;
919-
bufsize = PyUnicode_GET_LENGTH(item);
911+
PyObject *item = PyTuple_GET_ITEM(state, 0);
912+
if (PyUnicode_Check(item)) {
913+
Py_UCS4 *buf = PyUnicode_AsUCS4Copy(item);
914+
if (buf == NULL)
915+
return NULL;
916+
Py_ssize_t bufsize = PyUnicode_GET_LENGTH(item);
920917

921-
if (resize_buffer(self, bufsize) < 0) {
918+
if (resize_buffer(self, bufsize) < 0) {
919+
PyMem_Free(buf);
920+
return NULL;
921+
}
922+
memcpy(self->buf, buf, bufsize * sizeof(Py_UCS4));
922923
PyMem_Free(buf);
923-
return NULL;
924+
self->string_size = bufsize;
925+
}
926+
else {
927+
assert(item == Py_None);
928+
self->string_size = 0;
924929
}
925-
memcpy(self->buf, buf, bufsize * sizeof(Py_UCS4));
926-
PyMem_Free(buf);
927-
self->string_size = bufsize;
928930
}
929931

930932
/* Set carefully the position value. Alternatively, we could use the seek

0 commit comments

Comments
 (0)