bpo-41993: Fix possible issues in remove_module() (GH-22631)

* PyMapping_HasKey() is not safe because it silences all exceptions and can return incorrect result.
* Informative exceptions from PyMapping_DelItem() are overridden with RuntimeError and
  the original exception raised before calling remove_module() is lost.
* There is a race condition between PyMapping_HasKey() and PyMapping_DelItem().
This commit is contained in:
Serhiy Storchaka 2020-10-11 16:51:07 +03:00 committed by GitHub
parent fa1d83db62
commit 8287aadb75
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 15 additions and 10 deletions

View File

@ -0,0 +1,2 @@
Fixed potential issues with removing not completely initialized module from
``sys.modules`` when import fails.

View File

@ -902,7 +902,11 @@ PyImport_AddModule(const char *name)
}
/* Remove name from sys.modules, if it's there. */
/* Remove name from sys.modules, if it's there.
* Can be called with an exception raised.
* If fail to remove name a new exception will be chained with the old
* exception, otherwise the old exception is preserved.
*/
static void
remove_module(PyThreadState *tstate, PyObject *name)
{
@ -910,18 +914,17 @@ remove_module(PyThreadState *tstate, PyObject *name)
_PyErr_Fetch(tstate, &type, &value, &traceback);
PyObject *modules = tstate->interp->modules;
if (!PyMapping_HasKey(modules, name)) {
goto out;
if (PyDict_CheckExact(modules)) {
PyObject *mod = _PyDict_Pop(modules, name, Py_None);
Py_XDECREF(mod);
}
else if (PyMapping_DelItem(modules, name) < 0) {
if (_PyErr_ExceptionMatches(tstate, PyExc_KeyError)) {
_PyErr_Clear(tstate);
}
if (PyMapping_DelItem(modules, name) < 0) {
_PyErr_SetString(tstate, PyExc_RuntimeError,
"deleting key in sys.modules failed");
_PyErr_ChainExceptions(type, value, traceback);
return;
}
out:
_PyErr_Restore(tstate, type, value, traceback);
_PyErr_ChainExceptions(type, value, traceback);
}