From e19d7a3c0ac25680d2f72669f1441dadbb2f9a41 Mon Sep 17 00:00:00 2001 From: Collin Winter Date: Mon, 12 Mar 2007 16:49:23 +0000 Subject: [PATCH] Bug #742342: make Python stop segfaulting on infinitely-recursive reload()s. Fixed by patch #922167. Backported from r54291. --- Include/pystate.h | 1 + Lib/test/infinite_reload.py | 7 +++++++ Lib/test/test_import.py | 12 ++++++++++++ Misc/NEWS | 3 +++ Python/import.c | 37 +++++++++++++++++++++++++++++++++++-- Python/pystate.c | 2 ++ Python/pythonrun.c | 4 ++++ 7 files changed, 64 insertions(+), 2 deletions(-) create mode 100644 Lib/test/infinite_reload.py diff --git a/Include/pystate.h b/Include/pystate.h index cf296959631..4919d99b6c7 100644 --- a/Include/pystate.h +++ b/Include/pystate.h @@ -21,6 +21,7 @@ typedef struct _is { PyObject *modules; PyObject *sysdict; PyObject *builtins; + PyObject *modules_reloading; PyObject *codec_search_path; PyObject *codec_search_cache; diff --git a/Lib/test/infinite_reload.py b/Lib/test/infinite_reload.py new file mode 100644 index 00000000000..bfbec91b0a1 --- /dev/null +++ b/Lib/test/infinite_reload.py @@ -0,0 +1,7 @@ +# For testing http://python.org/sf/742342, which reports that Python +# segfaults (infinite recursion in C) in the presence of infinite +# reload()ing. This module is imported by test_import.py:test_infinite_reload +# to make sure this doesn't happen any more. + +import infinite_reload +reload(infinite_reload) diff --git a/Lib/test/test_import.py b/Lib/test/test_import.py index effba3cc117..ed97f84fefb 100644 --- a/Lib/test/test_import.py +++ b/Lib/test/test_import.py @@ -222,3 +222,15 @@ def test_import_initless_directory_warning(): warnings.filters = oldfilters test_import_initless_directory_warning() + +def test_infinite_reload(): + # Bug #742342 reports that Python segfaults (infinite recursion in C) + # when faced with self-recursive reload()ing. + + sys.path.insert(0, os.path.dirname(__file__)) + try: + import infinite_reload + finally: + sys.path.pop(0) + +test_infinite_reload() diff --git a/Misc/NEWS b/Misc/NEWS index dc981ffe131..c21972d37a7 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -12,6 +12,9 @@ What's New in Python 2.5.1c1? Core and builtins ----------------- +- Patch #922167: Python no longer segfaults when faced with infinitely + self-recursive reload() calls (as reported by bug #742342). + - Patch #1675981: remove unreachable code from ``type.__new__()`` method. - Patch #1638879: don't accept strings with embedded NUL bytes in long(). diff --git a/Python/import.c b/Python/import.c index 390f9e3292f..b62eeef7681 100644 --- a/Python/import.c +++ b/Python/import.c @@ -339,6 +339,25 @@ imp_release_lock(PyObject *self, PyObject *noargs) return Py_None; } +PyObject * +PyImport_GetModulesReloading(void) +{ + PyInterpreterState *interp = PyThreadState_Get()->interp; + if (interp->modules_reloading == NULL) + Py_FatalError("PyImport_GetModuleDict: no modules_reloading dictionary!"); + return interp->modules_reloading; +} + +static void +imp_modules_reloading_clear (void) +{ + PyInterpreterState *interp = PyThreadState_Get()->interp; + if (interp->modules_reloading == NULL) + return; + PyDict_Clear(interp->modules_reloading); + return; +} + /* Helper for sys */ PyObject * @@ -498,6 +517,7 @@ PyImport_Cleanup(void) PyDict_Clear(modules); interp->modules = NULL; Py_DECREF(modules); + Py_CLEAR(interp->modules_reloading); } @@ -2400,8 +2420,9 @@ import_submodule(PyObject *mod, char *subname, char *fullname) PyObject * PyImport_ReloadModule(PyObject *m) { + PyObject *modules_reloading = PyImport_GetModulesReloading(); PyObject *modules = PyImport_GetModuleDict(); - PyObject *path = NULL, *loader = NULL; + PyObject *path = NULL, *loader = NULL, *existing_m = NULL; char *name, *subname; char buf[MAXPATHLEN+1]; struct filedescr *fdp; @@ -2422,20 +2443,30 @@ PyImport_ReloadModule(PyObject *m) name); return NULL; } + if ((existing_m = PyDict_GetItemString(modules_reloading, name)) != NULL) { + /* Due to a recursive reload, this module is already being reloaded. */ + Py_INCREF(existing_m); + return existing_m; + } + PyDict_SetItemString(modules_reloading, name, m); + subname = strrchr(name, '.'); if (subname == NULL) subname = name; else { PyObject *parentname, *parent; parentname = PyString_FromStringAndSize(name, (subname-name)); - if (parentname == NULL) + if (parentname == NULL) { + imp_modules_reloading_clear(); return NULL; + } parent = PyDict_GetItem(modules, parentname); if (parent == NULL) { PyErr_Format(PyExc_ImportError, "reload(): parent %.200s not in sys.modules", PyString_AS_STRING(parentname)); Py_DECREF(parentname); + imp_modules_reloading_clear(); return NULL; } Py_DECREF(parentname); @@ -2450,6 +2481,7 @@ PyImport_ReloadModule(PyObject *m) if (fdp == NULL) { Py_XDECREF(loader); + imp_modules_reloading_clear(); return NULL; } @@ -2466,6 +2498,7 @@ PyImport_ReloadModule(PyObject *m) */ PyDict_SetItemString(modules, name, m); } + imp_modules_reloading_clear(); return newm; } diff --git a/Python/pystate.c b/Python/pystate.c index cc25e3ed38e..086789d3553 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -68,6 +68,7 @@ PyInterpreterState_New(void) Py_FatalError("Can't initialize threads for interpreter"); #endif interp->modules = NULL; + interp->modules_reloading = NULL; interp->sysdict = NULL; interp->builtins = NULL; interp->tstate_head = NULL; @@ -107,6 +108,7 @@ PyInterpreterState_Clear(PyInterpreterState *interp) Py_CLEAR(interp->codec_search_cache); Py_CLEAR(interp->codec_error_registry); Py_CLEAR(interp->modules); + Py_CLEAR(interp->modules_reloading); Py_CLEAR(interp->sysdict); Py_CLEAR(interp->builtins); } diff --git a/Python/pythonrun.c b/Python/pythonrun.c index 3b5307c7fb0..75241fe9159 100644 --- a/Python/pythonrun.c +++ b/Python/pythonrun.c @@ -193,6 +193,9 @@ Py_InitializeEx(int install_sigs) interp->modules = PyDict_New(); if (interp->modules == NULL) Py_FatalError("Py_Initialize: can't make modules dictionary"); + interp->modules_reloading = PyDict_New(); + if (interp->modules_reloading == NULL) + Py_FatalError("Py_Initialize: can't make modules_reloading dictionary"); #ifdef Py_USING_UNICODE /* Init Unicode implementation; relies on the codec registry */ @@ -530,6 +533,7 @@ Py_NewInterpreter(void) /* XXX The following is lax in error checking */ interp->modules = PyDict_New(); + interp->modules_reloading = PyDict_New(); bimod = _PyImport_FindExtension("__builtin__", "__builtin__"); if (bimod != NULL) {