From e8c541b7302acc1f0754910905e4f70e8c4c7e53 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Thu, 14 Dec 2023 14:24:24 +0200 Subject: [PATCH] [3.11] gh-112716: Fix SystemError when __builtins__ is not a dict (GH-112770) It was raised in two cases: * in the import statement when looking up __import__ * in pickling some builtin type when looking up built-ins iter, getattr, etc. (cherry picked from commit 1161c14e8c68296fc465cd48970b32be9bee012e) Co-authored-by: Serhiy Storchaka --- Lib/test/test_builtin.py | 26 +++++++++++++++++++ ...-12-05-20-41-58.gh-issue-112716.hOcx0Y.rst | 2 ++ Python/ceval.c | 14 ++++------ 3 files changed, 33 insertions(+), 9 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-12-05-20-41-58.gh-issue-112716.hOcx0Y.rst diff --git a/Lib/test/test_builtin.py b/Lib/test/test_builtin.py index a34f6daaad6923..a8f5b76d1bd6bf 100644 --- a/Lib/test/test_builtin.py +++ b/Lib/test/test_builtin.py @@ -798,6 +798,32 @@ class customdict(dict): # this one should not do anything fancy self.assertRaisesRegex(NameError, "name 'superglobal' is not defined", exec, code, {'__builtins__': customdict()}) + def test_eval_builtins_mapping(self): + code = compile("superglobal", "test", "eval") + # works correctly + ns = {'__builtins__': types.MappingProxyType({'superglobal': 1})} + self.assertEqual(eval(code, ns), 1) + # custom builtins mapping is missing key + ns = {'__builtins__': types.MappingProxyType({})} + self.assertRaisesRegex(NameError, "name 'superglobal' is not defined", + eval, code, ns) + + def test_exec_builtins_mapping_import(self): + code = compile("import foo.bar", "test", "exec") + ns = {'__builtins__': types.MappingProxyType({})} + self.assertRaisesRegex(ImportError, "__import__ not found", exec, code, ns) + ns = {'__builtins__': types.MappingProxyType({'__import__': lambda *args: args})} + exec(code, ns) + self.assertEqual(ns['foo'], ('foo.bar', ns, ns, None, 0)) + + def test_eval_builtins_mapping_reduce(self): + # list_iterator.__reduce__() calls _PyEval_GetBuiltin("iter") + code = compile("x.__reduce__()", "test", "eval") + ns = {'__builtins__': types.MappingProxyType({}), 'x': iter([1, 2])} + self.assertRaisesRegex(AttributeError, "iter", eval, code, ns) + ns = {'__builtins__': types.MappingProxyType({'iter': iter}), 'x': iter([1, 2])} + self.assertEqual(eval(code, ns), (iter, ([1, 2],), 0)) + def test_exec_redirected(self): savestdout = sys.stdout sys.stdout = None # Whatever that cannot flush() diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-12-05-20-41-58.gh-issue-112716.hOcx0Y.rst b/Misc/NEWS.d/next/Core and Builtins/2023-12-05-20-41-58.gh-issue-112716.hOcx0Y.rst new file mode 100644 index 00000000000000..44d63269c5424a --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-12-05-20-41-58.gh-issue-112716.hOcx0Y.rst @@ -0,0 +1,2 @@ +Fix SystemError in the ``import`` statement and in ``__reduce__()`` methods +of builtin types when ``__builtins__`` is not a dict. diff --git a/Python/ceval.c b/Python/ceval.c index bb6bb35030d212..1ce6bbc10fb8a5 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -7153,11 +7153,8 @@ PyObject * _PyEval_GetBuiltin(PyObject *name) { PyThreadState *tstate = _PyThreadState_GET(); - PyObject *attr = PyDict_GetItemWithError(PyEval_GetBuiltins(), name); - if (attr) { - Py_INCREF(attr); - } - else if (!_PyErr_Occurred(tstate)) { + PyObject *attr = PyObject_GetItem(PyEval_GetBuiltins(), name); + if (attr == NULL && _PyErr_ExceptionMatches(tstate, PyExc_KeyError)) { _PyErr_SetObject(tstate, PyExc_AttributeError, name); } return attr; @@ -7407,9 +7404,9 @@ import_name(PyThreadState *tstate, _PyInterpreterFrame *frame, PyObject *import_func, *res; PyObject* stack[5]; - import_func = _PyDict_GetItemWithError(frame->f_builtins, &_Py_ID(__import__)); + import_func = PyObject_GetItem(frame->f_builtins, &_Py_ID(__import__)); if (import_func == NULL) { - if (!_PyErr_Occurred(tstate)) { + if (_PyErr_ExceptionMatches(tstate, PyExc_KeyError)) { _PyErr_SetString(tstate, PyExc_ImportError, "__import__ not found"); } return NULL; @@ -7417,6 +7414,7 @@ import_name(PyThreadState *tstate, _PyInterpreterFrame *frame, PyObject *locals = frame->f_locals; /* Fast path for not overloaded __import__. */ if (import_func == tstate->interp->import_func) { + Py_DECREF(import_func); int ilevel = _PyLong_AsInt(level); if (ilevel == -1 && _PyErr_Occurred(tstate)) { return NULL; @@ -7430,8 +7428,6 @@ import_name(PyThreadState *tstate, _PyInterpreterFrame *frame, return res; } - Py_INCREF(import_func); - stack[0] = name; stack[1] = frame->f_globals; stack[2] = locals == NULL ? Py_None : locals;