Skip to content

Commit a4d6b90

Browse files
miss-islingtonsobolevnvstinner
authored
[3.12] gh-127182: Fix io.StringIO.__setstate__ crash when None is the first value (GH-127219) (#127263)
gh-127182: Fix `io.StringIO.__setstate__` crash when `None` is the first value (GH-127219) (cherry picked from commit a2ee899) Co-authored-by: sobolevn <[email protected]> Co-authored-by: Victor Stinner <[email protected]>
1 parent f7e587d commit a4d6b90

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
@@ -1154,6 +1154,21 @@ def test_disallow_instantiation(self):
11541154
_io = self._io
11551155
support.check_disallow_instantiation(self, _io._BytesIOBuffer)
11561156

1157+
def test_stringio_setstate(self):
1158+
# gh-127182: Calling __setstate__() with invalid arguments must not crash
1159+
obj = self._io.StringIO()
1160+
with self.assertRaisesRegex(
1161+
TypeError,
1162+
'initial_value must be str or None, not int',
1163+
):
1164+
obj.__setstate__((1, '', 0, {}))
1165+
1166+
obj.__setstate__((None, '', 0, {})) # should not crash
1167+
self.assertEqual(obj.getvalue(), '')
1168+
1169+
obj.__setstate__(('', '', 0, {}))
1170+
self.assertEqual(obj.getvalue(), '')
1171+
11571172
class PyIOTest(IOTest):
11581173
pass
11591174

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
@@ -884,23 +884,25 @@ stringio_setstate(stringio *self, PyObject *state)
884884
once by __init__. So we do not take any chance and replace object's
885885
buffer completely. */
886886
{
887-
PyObject *item;
888-
Py_UCS4 *buf;
889-
Py_ssize_t bufsize;
890-
891-
item = PyTuple_GET_ITEM(state, 0);
892-
buf = PyUnicode_AsUCS4Copy(item);
893-
if (buf == NULL)
894-
return NULL;
895-
bufsize = PyUnicode_GET_LENGTH(item);
887+
PyObject *item = PyTuple_GET_ITEM(state, 0);
888+
if (PyUnicode_Check(item)) {
889+
Py_UCS4 *buf = PyUnicode_AsUCS4Copy(item);
890+
if (buf == NULL)
891+
return NULL;
892+
Py_ssize_t bufsize = PyUnicode_GET_LENGTH(item);
896893

897-
if (resize_buffer(self, bufsize) < 0) {
894+
if (resize_buffer(self, bufsize) < 0) {
895+
PyMem_Free(buf);
896+
return NULL;
897+
}
898+
memcpy(self->buf, buf, bufsize * sizeof(Py_UCS4));
898899
PyMem_Free(buf);
899-
return NULL;
900+
self->string_size = bufsize;
901+
}
902+
else {
903+
assert(item == Py_None);
904+
self->string_size = 0;
900905
}
901-
memcpy(self->buf, buf, bufsize * sizeof(Py_UCS4));
902-
PyMem_Free(buf);
903-
self->string_size = bufsize;
904906
}
905907

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

0 commit comments

Comments
 (0)