From 7f21b8ff526fffb5d933189005e3bdc5e1ef8a68 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Thu, 29 Aug 2024 08:26:16 +0300 Subject: [PATCH 1/2] gh-123431: Harmonize extension code checks in pickle (GH-123434) This checks are redundant in normal circumstances and can only work if the extension registry was intentionally broken. * The Python implementation now raises exception for the extension code with false boolean value. * Simplify the C code. RuntimeError is now raised in explicit checks. * Add many tests. (cherry picked from commit 0c3ea3023878f5ad5ca4680d5510da1fe208cbfa) Co-authored-by: Serhiy Storchaka --- Lib/pickle.py | 18 ++++++++------ Lib/test/pickletester.py | 51 ++++++++++++++++++++++++++++++++++++++++ Modules/_pickle.c | 32 +++++++++---------------- 3 files changed, 73 insertions(+), 28 deletions(-) diff --git a/Lib/pickle.py b/Lib/pickle.py index c4d6e658216f17..87ac0a6681f424 100644 --- a/Lib/pickle.py +++ b/Lib/pickle.py @@ -1091,11 +1091,16 @@ def save_global(self, obj, name=None): (obj, module_name, name)) if self.proto >= 2: - code = _extension_registry.get((module_name, name)) - if code: - assert code > 0 + code = _extension_registry.get((module_name, name), _NoValue) + if code is not _NoValue: if code <= 0xff: - write(EXT1 + pack("extension_registry, - extension_key); + if (PyDict_GetItemRef(st->extension_registry, extension_key, &code_obj) < 0) { + Py_DECREF(extension_key); + goto error; + } Py_DECREF(extension_key); - /* The object is not registered in the extension registry. - This is the most likely code path. */ if (code_obj == NULL) { - if (PyErr_Occurred()) { - goto error; - } + /* The object is not registered in the extension registry. + This is the most likely code path. */ goto gen_global; } - /* XXX: pickle.py doesn't check neither the type, nor the range - of the value returned by the extension_registry. It should for - consistency. */ - - /* Verify code_obj has the right type and value. */ - if (!PyLong_Check(code_obj)) { - PyErr_Format(st->PicklingError, - "Can't pickle %R: extension code %R isn't an integer", - obj, code_obj); - goto error; - } - code = PyLong_AS_LONG(code_obj); + code = PyLong_AsLong(code_obj); + Py_DECREF(code_obj); if (code <= 0 || code > 0x7fffffffL) { + /* Should never happen in normal circumstances, since the type and + the value of the code are checked in copyreg.add_extension(). */ if (!PyErr_Occurred()) - PyErr_Format(st->PicklingError, "Can't pickle %R: extension " - "code %ld is out of range", obj, code); + PyErr_Format(PyExc_RuntimeError, "extension code %ld is out of range", code); goto error; } From bc513d368d3474fe365ea608d79a42c6a4dab23b Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Thu, 29 Aug 2024 09:25:51 +0300 Subject: [PATCH 2/2] Do not use PyDict_GetItemRef. Add _NoValue. --- Lib/pickle.py | 2 ++ Modules/_pickle.c | 10 ++++++---- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/Lib/pickle.py b/Lib/pickle.py index 87ac0a6681f424..01c1a102794d57 100644 --- a/Lib/pickle.py +++ b/Lib/pickle.py @@ -397,6 +397,8 @@ def decode_long(data): return int.from_bytes(data, byteorder='little', signed=True) +_NoValue = object() + # Pickling machinery class _Pickler: diff --git a/Modules/_pickle.c b/Modules/_pickle.c index af71b3935a208f..831d53bc82f848 100644 --- a/Modules/_pickle.c +++ b/Modules/_pickle.c @@ -3722,17 +3722,19 @@ save_global(PickleState *st, PicklerObject *self, PyObject *obj, if (extension_key == NULL) { goto error; } - if (PyDict_GetItemRef(st->extension_registry, extension_key, &code_obj) < 0) { - Py_DECREF(extension_key); - goto error; - } + code_obj = PyDict_GetItemWithError(st->extension_registry, + extension_key); Py_DECREF(extension_key); if (code_obj == NULL) { + if (PyErr_Occurred()) { + goto error; + } /* The object is not registered in the extension registry. This is the most likely code path. */ goto gen_global; } + Py_INCREF(code_obj); code = PyLong_AsLong(code_obj); Py_DECREF(code_obj); if (code <= 0 || code > 0x7fffffffL) {