From c496d01932c9815d65824ae891d62e534a5a8e09 Mon Sep 17 00:00:00 2001 From: An Long Date: Fri, 21 Aug 2026 01:02:26 +0900 Subject: [PATCH] gh-156121: Fix crash on bad AttributeError args in crossinterp --- Lib/test/test_interpreters/test_api.py | 22 +++++++++++++++++++ ...-08-21-00-52-18.gh-issue-156121.xrGUcF.rst | 3 +++ Python/crossinterp.c | 11 +++++++++- 3 files changed, 35 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2026-08-21-00-52-18.gh-issue-156121.xrGUcF.rst diff --git a/Lib/test/test_interpreters/test_api.py b/Lib/test/test_interpreters/test_api.py index aac3cdd717668ca..9966161942a0ed1 100644 --- a/Lib/test/test_interpreters/test_api.py +++ b/Lib/test/test_interpreters/test_api.py @@ -1741,6 +1741,28 @@ def test_call_invalid(self): with self.assertRaises(interpreters.NotShareableError): interp.call(func, op, 'eggs!') + def test_call_unpickle_bad_attribute_error(self): + # gh-156121: unpickling in the other interpreter may fail + # with an AttributeError whose message is not a str + # or cannot be encoded as UTF-8. + interp = interpreters.create() + script = dedent(""" + x = 1 + def f(): + return x # forces the pickle fallback + """) + with defined_in___main__('f', script) as func: + for arg in (42, '\ud800'): + with self.subTest(arg): + interp.exec(dedent(f""" + import pickle + def loads(data): + raise AttributeError({arg!r}) + pickle.loads = loads + """)) + with self.assertRaises(interpreters.NotShareableError): + interp.call(func) + def test_callable_requires_frame(self): # There are various functions that require a current frame. interp = interpreters.create() diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-08-21-00-52-18.gh-issue-156121.xrGUcF.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-08-21-00-52-18.gh-issue-156121.xrGUcF.rst new file mode 100644 index 000000000000000..43a2edf6aea56ba --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-08-21-00-52-18.gh-issue-156121.xrGUcF.rst @@ -0,0 +1,3 @@ +Fix a crash in cross-interpreter unpickling +when the :exc:`AttributeError` raised by :func:`pickle.loads` +has a non-string argument or a message that cannot be encoded as UTF-8. diff --git a/Python/crossinterp.c b/Python/crossinterp.c index ed77c1be646e275..937aabfa414b898 100644 --- a/Python/crossinterp.c +++ b/Python/crossinterp.c @@ -651,7 +651,7 @@ check_missing___main___attr(PyObject *exc) PyObject *args = PyException_GetArgs(exc); if (args == NULL || args == Py_None || PyObject_Size(args) < 1) { Py_XDECREF(args); - assert(!PyErr_Occurred()); + PyErr_Clear(); return 0; } PyObject *msgobj = args; @@ -662,8 +662,17 @@ check_missing___main___attr(PyObject *exc) PyErr_Clear(); return 0; } + if (!PyUnicode_Check(msgobj)) { + Py_DECREF(msgobj); + return 0; + } } const char *err = PyUnicode_AsUTF8(msgobj); + if (err == NULL) { + Py_DECREF(msgobj); + PyErr_Clear(); + return 0; + } // Check if it's a missing __main__ attr. int cmp = strncmp(err, "module '__main__' has no attribute '", 36);