Skip to content
Open
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
38 changes: 38 additions & 0 deletions Lib/test/test_interpreters/test_queues.py
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,44 @@ def test_put_get_full_fallback(self):
self.assertEqual(obj, obj2)
self.assertIsNot(obj, obj2)

def _check_unpickle_attributeerror_arg(self, arg):
# Cross an object through a queue where get() must re-import its
# class via a module __getattr__ that raises AttributeError(arg).
source = dedent("""
_attrerr_arg = None

class Thing:
pass

def break_module(mod, arg):
del mod.Thing
mod._attrerr_arg = arg
def __getattr__(name):
raise AttributeError(mod._attrerr_arg)
mod.__getattr__ = __getattr__
""")
with import_helper.ready_to_import('_xi_attrerr', source) as (name, _):
mod = importlib.import_module(name)
queue = queues.create()
queue.put(mod.Thing())
mod.break_module(mod, arg)
with self.assertRaises(interpreters.NotShareableError):
queue.get()

def test_get_unpickle_fails_with_bad_attributeerror_arg(self):
# gh-151862: an AttributeError arg that can't be UTF-8 encoded used
# to crash (NULL deref); covers both non-str and surrogate-str args.
for arg in [42, b'x', None, '\ud800']:
with self.subTest(arg=arg):
self._check_unpickle_attributeerror_arg(arg)

def test_get_unpickle_fails_with_str_attributeerror_arg(self):
# Positive control: a normal str arg (incl. the real missing-__main__
# message) must not crash, locking in the non-NULL strncmp() path.
for arg in ['boom', "module '__main__' has no attribute 'Thing'"]:
with self.subTest(arg=arg):
self._check_unpickle_attributeerror_arg(arg)

def test_put_get_same_interpreter(self):
interp = interpreters.create()
interp.exec(dedent("""
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Fixed a crash (``NULL`` dereference) when an object passed between
interpreters via :mod:`concurrent.interpreters` fails to unpickle with an
:exc:`AttributeError` whose first argument is not a string.
5 changes: 5 additions & 0 deletions Python/crossinterp.c
Original file line number Diff line number Diff line change
Expand Up @@ -664,6 +664,11 @@ check_missing___main___attr(PyObject *exc)
}
}
const char *err = PyUnicode_AsUTF8(msgobj);
if (err == NULL) {
PyErr_Clear();
Py_DECREF(msgobj);
return 0;
}

// Check if it's a missing __main__ attr.
int cmp = strncmp(err, "module '__main__' has no attribute '", 36);
Expand Down
Loading