Skip to content

gh-101819: Explicitly disallow pickle protocols 0 and 1 in _io #104370

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions Lib/test/test_io.py
Original file line number Diff line number Diff line change
Expand Up @@ -4242,6 +4242,7 @@ def test_warn_on_dealloc_fd(self):

def test_pickling(self):
# Pickling file objects is forbidden
msg = "cannot pickle"
for kwargs in [
{"mode": "w"},
{"mode": "wb"},
Expand All @@ -4256,8 +4257,10 @@ def test_pickling(self):
if "b" not in kwargs["mode"]:
kwargs["encoding"] = "utf-8"
for protocol in range(pickle.HIGHEST_PROTOCOL + 1):
with self.open(os_helper.TESTFN, **kwargs) as f:
self.assertRaises(TypeError, pickle.dumps, f, protocol)
with self.subTest(protocol=protocol, kwargs=kwargs):
with self.open(os_helper.TESTFN, **kwargs) as f:
with self.assertRaisesRegex(TypeError, msg):
pickle.dumps(f, protocol)

@unittest.skipIf(
support.is_emscripten, "fstat() of a pipe fd is not supported"
Expand Down
1 change: 1 addition & 0 deletions Modules/_io/_iomodule.h
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,7 @@ find_io_state_by_def(PyTypeObject *type)
}

extern _PyIO_State *_PyIO_get_module_state(void);
extern PyObject *_PyIOBase_cannot_pickle(PyObject *self, PyObject *args);

#ifdef HAVE_WINDOWS_CONSOLE_IO
extern char _PyIO_get_console_type(PyObject *);
Expand Down
9 changes: 9 additions & 0 deletions Modules/_io/bufferedio.c
Original file line number Diff line number Diff line change
Expand Up @@ -2508,6 +2508,9 @@ static PyMethodDef bufferedreader_methods[] = {
_IO__BUFFERED_TELL_METHODDEF
_IO__BUFFERED_TRUNCATE_METHODDEF
_IO__BUFFERED___SIZEOF___METHODDEF

{"__reduce__", _PyIOBase_cannot_pickle, METH_VARARGS},
{"__reduce_ex__", _PyIOBase_cannot_pickle, METH_VARARGS},
{NULL, NULL}
};

Expand Down Expand Up @@ -2565,6 +2568,9 @@ static PyMethodDef bufferedwriter_methods[] = {
_IO__BUFFERED_SEEK_METHODDEF
_IO__BUFFERED_TELL_METHODDEF
_IO__BUFFERED___SIZEOF___METHODDEF

{"__reduce__", _PyIOBase_cannot_pickle, METH_VARARGS},
{"__reduce_ex__", _PyIOBase_cannot_pickle, METH_VARARGS},
{NULL, NULL}
};

Expand Down Expand Up @@ -2680,6 +2686,9 @@ static PyMethodDef bufferedrandom_methods[] = {
_IO__BUFFERED_PEEK_METHODDEF
_IO_BUFFEREDWRITER_WRITE_METHODDEF
_IO__BUFFERED___SIZEOF___METHODDEF

{"__reduce__", _PyIOBase_cannot_pickle, METH_VARARGS},
{"__reduce_ex__", _PyIOBase_cannot_pickle, METH_VARARGS},
{NULL, NULL}
};

Expand Down
3 changes: 3 additions & 0 deletions Modules/_io/fileio.c
Original file line number Diff line number Diff line change
Expand Up @@ -1166,6 +1166,9 @@ static PyMethodDef fileio_methods[] = {
_IO_FILEIO_FILENO_METHODDEF
_IO_FILEIO_ISATTY_METHODDEF
{"_dealloc_warn", (PyCFunction)fileio_dealloc_warn, METH_O, NULL},

{"__reduce__", _PyIOBase_cannot_pickle, METH_VARARGS},
{"__reduce_ex__", _PyIOBase_cannot_pickle, METH_VARARGS},
{NULL, NULL} /* sentinel */
};

Expand Down
7 changes: 7 additions & 0 deletions Modules/_io/iobase.c
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,13 @@ iobase_check_writable(PyObject *self, PyObject *args)
return _PyIOBase_check_writable(state, self, args);
}

PyObject *
_PyIOBase_cannot_pickle(PyObject *self, PyObject *Py_UNUSED(args))
{
return PyErr_Format(PyExc_TypeError, "cannot pickle '%.100s' instances",
_PyType_Name(Py_TYPE(self)));
}

/* XXX: IOBase thinks it has to maintain its own internal state in
`__IOBase_closed` and call flush() by itself, but it is redundant with
whatever behaviour a non-trivial derived class will implement. */
Expand Down