From e11489eddefa46163286fc0dced59a502677292c Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Wed, 10 May 2023 23:48:11 +0200 Subject: [PATCH 1/3] gh-101819: Explicitly disallow pickle protocols 0 and 1 in _io This is part of the preparations for isolating the _io module. --- Lib/test/test_io.py | 7 +++++-- Modules/_io/_iomodule.h | 1 + Modules/_io/bufferedio.c | 9 +++++++++ Modules/_io/fileio.c | 3 +++ Modules/_io/iobase.c | 7 +++++++ 5 files changed, 25 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_io.py b/Lib/test/test_io.py index 9bcc92a40c61df..678e3657c771bc 100644 --- a/Lib/test/test_io.py +++ b/Lib/test/test_io.py @@ -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"}, @@ -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" diff --git a/Modules/_io/_iomodule.h b/Modules/_io/_iomodule.h index 44d651338e6975..3f543e6110f891 100644 --- a/Modules/_io/_iomodule.h +++ b/Modules/_io/_iomodule.h @@ -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 *); diff --git a/Modules/_io/bufferedio.c b/Modules/_io/bufferedio.c index c7ae60281c2f58..dcb54a223eeb1a 100644 --- a/Modules/_io/bufferedio.c +++ b/Modules/_io/bufferedio.c @@ -2446,6 +2446,9 @@ static PyMethodDef bufferedreader_methods[] = { {"tell", (PyCFunction)buffered_tell, METH_NOARGS}, _IO__BUFFERED_TRUNCATE_METHODDEF {"__sizeof__", (PyCFunction)buffered_sizeof, METH_NOARGS}, + + {"__reduce__", _PyIOBase_cannot_pickle, METH_VARARGS}, + {"__reduce_ex__", _PyIOBase_cannot_pickle, METH_VARARGS}, {NULL, NULL} }; @@ -2503,6 +2506,9 @@ static PyMethodDef bufferedwriter_methods[] = { _IO__BUFFERED_SEEK_METHODDEF {"tell", (PyCFunction)buffered_tell, METH_NOARGS}, {"__sizeof__", (PyCFunction)buffered_sizeof, METH_NOARGS}, + + {"__reduce__", _PyIOBase_cannot_pickle, METH_VARARGS}, + {"__reduce_ex__", _PyIOBase_cannot_pickle, METH_VARARGS}, {NULL, NULL} }; @@ -2618,6 +2624,9 @@ static PyMethodDef bufferedrandom_methods[] = { _IO__BUFFERED_PEEK_METHODDEF _IO_BUFFEREDWRITER_WRITE_METHODDEF {"__sizeof__", (PyCFunction)buffered_sizeof, METH_NOARGS}, + + {"__reduce__", _PyIOBase_cannot_pickle, METH_VARARGS}, + {"__reduce_ex__", _PyIOBase_cannot_pickle, METH_VARARGS}, {NULL, NULL} }; diff --git a/Modules/_io/fileio.c b/Modules/_io/fileio.c index cc0e7307b9da77..36a098bab97a02 100644 --- a/Modules/_io/fileio.c +++ b/Modules/_io/fileio.c @@ -1157,6 +1157,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 */ }; diff --git a/Modules/_io/iobase.c b/Modules/_io/iobase.c index ca13866c33fba6..f86f71add6b4cc 100644 --- a/Modules/_io/iobase.c +++ b/Modules/_io/iobase.c @@ -225,6 +225,13 @@ iobase_check_writable(PyObject *self, PyObject *args) return _PyIOBase_check_writable(state, self, args); } +PyObject * +_PyIOBase_cannot_pickle(PyObject *self, PyObject *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. */ From 5cbffed7ecf921d02f2d3d6b36bbc2cd576510f8 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Thu, 11 May 2023 09:46:14 +0200 Subject: [PATCH 2/3] Fix indentation (patchcheck) --- Lib/test/test_io.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Lib/test/test_io.py b/Lib/test/test_io.py index 678e3657c771bc..cc16804fe21829 100644 --- a/Lib/test/test_io.py +++ b/Lib/test/test_io.py @@ -4258,9 +4258,9 @@ def test_pickling(self): kwargs["encoding"] = "utf-8" for protocol in range(pickle.HIGHEST_PROTOCOL + 1): 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) + 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" From e3e5442e543076f487407f95b46c88eb52c52874 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Thu, 11 May 2023 09:49:34 +0200 Subject: [PATCH 3/3] Mark 'args' as unused --- Modules/_io/iobase.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/_io/iobase.c b/Modules/_io/iobase.c index f86f71add6b4cc..b8eed0034fb6d6 100644 --- a/Modules/_io/iobase.c +++ b/Modules/_io/iobase.c @@ -226,7 +226,7 @@ iobase_check_writable(PyObject *self, PyObject *args) } PyObject * -_PyIOBase_cannot_pickle(PyObject *self, PyObject *args) +_PyIOBase_cannot_pickle(PyObject *self, PyObject *Py_UNUSED(args)) { return PyErr_Format(PyExc_TypeError, "cannot pickle '%.100s' instances", _PyType_Name(Py_TYPE(self)));