Skip to content

Commit 19cb2a6

Browse files
author
collin.winter
committed
Bug #742342: make Python stop segfaulting on infinitely-recursive reload()s. Fixed by patch #922167.
Will backport. git-svn-id: http://svn.python.org/projects/python/trunk@54291 6015fed2-1504-0410-9fe1-9d1591cc4771
1 parent a930ad5 commit 19cb2a6

7 files changed

Lines changed: 62 additions & 2 deletions

File tree

Include/pystate.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ typedef struct _is {
2121
PyObject *modules;
2222
PyObject *sysdict;
2323
PyObject *builtins;
24+
PyObject *modules_reloading;
2425

2526
PyObject *codec_search_path;
2627
PyObject *codec_search_cache;

Lib/test/infinite_reload.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# For testing http://python.org/sf/742342, which reports that Python
2+
# segfaults (infinite recursion in C) in the presence of infinite
3+
# reload()ing. This module is imported by test_import.py:test_infinite_reload
4+
# to make sure this doesn't happen any more.
5+
6+
import infinite_reload
7+
reload(infinite_reload)

Lib/test/test_import.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,16 @@ def test_failing_reload(self):
192192
remove_files(TESTFN)
193193
if TESTFN in sys.modules:
194194
del sys.modules[TESTFN]
195+
196+
def test_infinite_reload(self):
197+
# Bug #742342 reports that Python segfaults (infinite recursion in C)
198+
# when faced with self-recursive reload()ing.
199+
200+
sys.path.insert(0, os.path.dirname(__file__))
201+
try:
202+
import infinite_reload
203+
finally:
204+
sys.path.pop(0)
195205

196206
def test_import_name_binding(self):
197207
# import x.y.z binds x in the current namespace

Misc/NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ Core and builtins
1919
its argument, if it exists. If not, it will work like before. This allows
2020
customizing the output of dir() in the presence of a __getattr__().
2121

22+
- Patch #922167: Python no longer segfaults when faced with infinitely
23+
self-recursive reload() calls (as reported by bug #742342).
24+
2225
- Patch #1675981: remove unreachable code from ``type.__new__()`` method.
2326

2427
- Patch #1491866: change the complex() constructor to allow parthensized

Python/import.c

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -340,6 +340,25 @@ imp_release_lock(PyObject *self, PyObject *noargs)
340340
return Py_None;
341341
}
342342

343+
PyObject *
344+
PyImport_GetModulesReloading(void)
345+
{
346+
PyInterpreterState *interp = PyThreadState_Get()->interp;
347+
if (interp->modules_reloading == NULL)
348+
Py_FatalError("PyImport_GetModuleDict: no modules_reloading dictionary!");
349+
return interp->modules_reloading;
350+
}
351+
352+
static void
353+
imp_modules_reloading_clear (void)
354+
{
355+
PyInterpreterState *interp = PyThreadState_Get()->interp;
356+
if (interp->modules_reloading == NULL)
357+
return;
358+
PyDict_Clear(interp->modules_reloading);
359+
return;
360+
}
361+
343362
/* Helper for sys */
344363

345364
PyObject *
@@ -499,6 +518,7 @@ PyImport_Cleanup(void)
499518
PyDict_Clear(modules);
500519
interp->modules = NULL;
501520
Py_DECREF(modules);
521+
Py_CLEAR(interp->modules_reloading);
502522
}
503523

504524

@@ -2401,8 +2421,9 @@ import_submodule(PyObject *mod, char *subname, char *fullname)
24012421
PyObject *
24022422
PyImport_ReloadModule(PyObject *m)
24032423
{
2424+
PyObject *modules_reloading = PyImport_GetModulesReloading();
24042425
PyObject *modules = PyImport_GetModuleDict();
2405-
PyObject *path = NULL, *loader = NULL;
2426+
PyObject *path = NULL, *loader = NULL, *existing_m = NULL;
24062427
char *name, *subname;
24072428
char buf[MAXPATHLEN+1];
24082429
struct filedescr *fdp;
@@ -2423,20 +2444,30 @@ PyImport_ReloadModule(PyObject *m)
24232444
name);
24242445
return NULL;
24252446
}
2447+
if ((existing_m = PyDict_GetItemString(modules_reloading, name)) != NULL) {
2448+
/* Due to a recursive reload, this module is already being reloaded. */
2449+
Py_INCREF(existing_m);
2450+
return existing_m;
2451+
}
2452+
PyDict_SetItemString(modules_reloading, name, m);
2453+
24262454
subname = strrchr(name, '.');
24272455
if (subname == NULL)
24282456
subname = name;
24292457
else {
24302458
PyObject *parentname, *parent;
24312459
parentname = PyString_FromStringAndSize(name, (subname-name));
2432-
if (parentname == NULL)
2460+
if (parentname == NULL) {
2461+
imp_modules_reloading_clear();
24332462
return NULL;
2463+
}
24342464
parent = PyDict_GetItem(modules, parentname);
24352465
if (parent == NULL) {
24362466
PyErr_Format(PyExc_ImportError,
24372467
"reload(): parent %.200s not in sys.modules",
24382468
PyString_AS_STRING(parentname));
24392469
Py_DECREF(parentname);
2470+
imp_modules_reloading_clear();
24402471
return NULL;
24412472
}
24422473
Py_DECREF(parentname);
@@ -2451,6 +2482,7 @@ PyImport_ReloadModule(PyObject *m)
24512482

24522483
if (fdp == NULL) {
24532484
Py_XDECREF(loader);
2485+
imp_modules_reloading_clear();
24542486
return NULL;
24552487
}
24562488

@@ -2467,6 +2499,7 @@ PyImport_ReloadModule(PyObject *m)
24672499
*/
24682500
PyDict_SetItemString(modules, name, m);
24692501
}
2502+
imp_modules_reloading_clear ();
24702503
return newm;
24712504
}
24722505

Python/pystate.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ PyInterpreterState_New(void)
6868
Py_FatalError("Can't initialize threads for interpreter");
6969
#endif
7070
interp->modules = NULL;
71+
interp->modules_reloading = NULL;
7172
interp->sysdict = NULL;
7273
interp->builtins = NULL;
7374
interp->tstate_head = NULL;
@@ -107,6 +108,7 @@ PyInterpreterState_Clear(PyInterpreterState *interp)
107108
Py_CLEAR(interp->codec_search_cache);
108109
Py_CLEAR(interp->codec_error_registry);
109110
Py_CLEAR(interp->modules);
111+
Py_CLEAR(interp->modules_reloading);
110112
Py_CLEAR(interp->sysdict);
111113
Py_CLEAR(interp->builtins);
112114
}

Python/pythonrun.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,9 @@ Py_InitializeEx(int install_sigs)
194194
interp->modules = PyDict_New();
195195
if (interp->modules == NULL)
196196
Py_FatalError("Py_Initialize: can't make modules dictionary");
197+
interp->modules_reloading = PyDict_New();
198+
if (interp->modules_reloading == NULL)
199+
Py_FatalError("Py_Initialize: can't make modules_reloading dictionary");
197200

198201
#ifdef Py_USING_UNICODE
199202
/* Init Unicode implementation; relies on the codec registry */
@@ -531,6 +534,7 @@ Py_NewInterpreter(void)
531534
/* XXX The following is lax in error checking */
532535

533536
interp->modules = PyDict_New();
537+
interp->modules_reloading = PyDict_New();
534538

535539
bimod = _PyImport_FindExtension("__builtin__", "__builtin__");
536540
if (bimod != NULL) {

0 commit comments

Comments
 (0)