Skip to content

Commit 87a5c51

Browse files
Issue python#19255: The builtins module is restored to initial value before
cleaning other modules. The sys and builtins modules are cleaned last.
1 parent 8f9f0f1 commit 87a5c51

File tree

7 files changed

+97
-40
lines changed

7 files changed

+97
-40
lines changed

Include/moduleobject.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ PyAPI_FUNC(const char *) PyModule_GetFilename(PyObject *);
2525
PyAPI_FUNC(PyObject *) PyModule_GetFilenameObject(PyObject *);
2626
#ifndef Py_LIMITED_API
2727
PyAPI_FUNC(void) _PyModule_Clear(PyObject *);
28+
PyAPI_FUNC(void) _PyModule_ClearDict(PyObject *);
2829
#endif
2930
PyAPI_FUNC(struct PyModuleDef*) PyModule_GetDef(PyObject*);
3031
PyAPI_FUNC(void*) PyModule_GetState(PyObject*);

Include/pystate.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,14 +33,14 @@ typedef struct _is {
3333
int codecs_initialized;
3434
int fscodec_initialized;
3535

36-
3736
#ifdef HAVE_DLOPEN
3837
int dlopenflags;
3938
#endif
4039
#ifdef WITH_TSC
4140
int tscdump;
4241
#endif
4342

43+
PyObject *builtins_copy;
4444
} PyInterpreterState;
4545
#endif
4646

Lib/test/test_builtin.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import warnings
1717
from operator import neg
1818
from test.support import TESTFN, unlink, run_unittest, check_warnings
19+
from test.script_helper import assert_python_ok
1920
try:
2021
import pty, signal
2122
except ImportError:
@@ -1592,6 +1593,34 @@ def test_baddecorator(self):
15921593
data = 'The quick Brown fox Jumped over The lazy Dog'.split()
15931594
self.assertRaises(TypeError, sorted, data, None, lambda x,y: 0)
15941595

1596+
1597+
class ShutdownTest(unittest.TestCase):
1598+
1599+
def test_cleanup(self):
1600+
# Issue #19255: builtins are still available at shutdown
1601+
code = """if 1:
1602+
import builtins
1603+
import sys
1604+
1605+
class C:
1606+
def __del__(self):
1607+
print("before")
1608+
# Check that builtins still exist
1609+
len(())
1610+
print("after")
1611+
1612+
c = C()
1613+
# Make this module survive until builtins and sys are cleaned
1614+
builtins.here = sys.modules[__name__]
1615+
sys.here = sys.modules[__name__]
1616+
# Create a reference loop so that this module needs to go
1617+
# through a GC phase.
1618+
here = sys.modules[__name__]
1619+
"""
1620+
rc, out, err = assert_python_ok("-c", code)
1621+
self.assertEqual(["before", "after"], out.decode().splitlines())
1622+
1623+
15951624
def load_tests(loader, tests, pattern):
15961625
from doctest import DocTestSuite
15971626
tests.addTest(DocTestSuite(builtins))

Misc/NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ Release date: 2014-02-09
1010
Core and Builtins
1111
-----------------
1212

13+
- Issue #19255: The builtins module is restored to initial value before
14+
cleaning other modules. The sys and builtins modules are cleaned last.
15+
1316
- Issue #20437: Fixed 22 potential bugs when deleting objects references.
1417

1518
- Issue #20500: Displaying an exception at interpreter shutdown no longer

Objects/moduleobject.c

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,14 @@ PyModule_GetState(PyObject* m)
298298

299299
void
300300
_PyModule_Clear(PyObject *m)
301+
{
302+
PyObject *d = ((PyModuleObject *)m)->md_dict;
303+
if (d != NULL)
304+
_PyModule_ClearDict(d);
305+
}
306+
307+
void
308+
_PyModule_ClearDict(PyObject *d)
301309
{
302310
/* To make the execution order of destructors for global
303311
objects a bit more predictable, we first zap all objects
@@ -308,11 +316,6 @@ _PyModule_Clear(PyObject *m)
308316

309317
Py_ssize_t pos;
310318
PyObject *key, *value;
311-
PyObject *d;
312-
313-
d = ((PyModuleObject *)m)->md_dict;
314-
if (d == NULL)
315-
return;
316319

317320
/* First, clear only names starting with a single underscore */
318321
pos = 0;

Python/import.c

Lines changed: 53 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,13 @@ class fs_unicode_converter(CConverter):
4949
void
5050
_PyImport_Init(void)
5151
{
52+
PyInterpreterState *interp = PyThreadState_Get()->interp;
5253
initstr = PyUnicode_InternFromString("__init__");
5354
if (initstr == NULL)
5455
Py_FatalError("Can't initialize import variables");
56+
interp->builtins_copy = PyDict_Copy(interp->builtins);
57+
if (interp->builtins_copy == NULL)
58+
Py_FatalError("Can't backup builtins dict");
5559
}
5660

5761
void
@@ -397,8 +401,10 @@ PyImport_Cleanup(void)
397401
PyObject *key, *value, *dict;
398402
PyInterpreterState *interp = PyThreadState_GET()->interp;
399403
PyObject *modules = interp->modules;
400-
PyObject *builtins = interp->builtins;
404+
PyObject *builtins_mod = NULL;
405+
PyObject *sys_mod = NULL;
401406
PyObject *weaklist = NULL;
407+
char **p;
402408

403409
if (modules == NULL)
404410
return; /* Already done */
@@ -411,31 +417,22 @@ PyImport_Cleanup(void)
411417

412418
/* XXX Perhaps these precautions are obsolete. Who knows? */
413419

414-
value = PyDict_GetItemString(modules, "builtins");
415-
if (value != NULL && PyModule_Check(value)) {
416-
dict = PyModule_GetDict(value);
420+
if (Py_VerboseFlag)
421+
PySys_WriteStderr("# clear builtins._\n");
422+
PyDict_SetItemString(interp->builtins, "_", Py_None);
423+
424+
for (p = sys_deletes; *p != NULL; p++) {
417425
if (Py_VerboseFlag)
418-
PySys_WriteStderr("# clear builtins._\n");
419-
PyDict_SetItemString(dict, "_", Py_None);
420-
}
421-
value = PyDict_GetItemString(modules, "sys");
422-
if (value != NULL && PyModule_Check(value)) {
423-
char **p;
424-
PyObject *v;
425-
dict = PyModule_GetDict(value);
426-
for (p = sys_deletes; *p != NULL; p++) {
427-
if (Py_VerboseFlag)
428-
PySys_WriteStderr("# clear sys.%s\n", *p);
429-
PyDict_SetItemString(dict, *p, Py_None);
430-
}
431-
for (p = sys_files; *p != NULL; p+=2) {
432-
if (Py_VerboseFlag)
433-
PySys_WriteStderr("# restore sys.%s\n", *p);
434-
v = PyDict_GetItemString(dict, *(p+1));
435-
if (v == NULL)
436-
v = Py_None;
437-
PyDict_SetItemString(dict, *p, v);
438-
}
426+
PySys_WriteStderr("# clear sys.%s\n", *p);
427+
PyDict_SetItemString(interp->sysdict, *p, Py_None);
428+
}
429+
for (p = sys_files; *p != NULL; p+=2) {
430+
if (Py_VerboseFlag)
431+
PySys_WriteStderr("# restore sys.%s\n", *p);
432+
value = PyDict_GetItemString(interp->sysdict, *(p+1));
433+
if (value == NULL)
434+
value = Py_None;
435+
PyDict_SetItemString(interp->sysdict, *p, value);
439436
}
440437

441438
/* We prepare a list which will receive (name, weakref) tuples of
@@ -473,11 +470,15 @@ PyImport_Cleanup(void)
473470

474471
/* Clear the modules dict. */
475472
PyDict_Clear(modules);
476-
/* Replace the interpreter's reference to builtins with an empty dict
477-
(module globals still have a reference to the original builtins). */
478-
builtins = interp->builtins;
479-
interp->builtins = PyDict_New();
480-
Py_DECREF(builtins);
473+
/* Restore the original builtins dict, to ensure that any
474+
user data gets cleared. */
475+
dict = PyDict_Copy(interp->builtins);
476+
if (dict == NULL)
477+
PyErr_Clear();
478+
PyDict_Clear(interp->builtins);
479+
if (PyDict_Update(interp->builtins, interp->builtins_copy))
480+
PyErr_Clear();
481+
Py_XDECREF(dict);
481482
/* Clear module dict copies stored in the interpreter state */
482483
_PyState_ClearModules();
483484
/* Collect references */
@@ -488,7 +489,15 @@ PyImport_Cleanup(void)
488489

489490
/* Now, if there are any modules left alive, clear their globals to
490491
minimize potential leaks. All C extension modules actually end
491-
up here, since they are kept alive in the interpreter state. */
492+
up here, since they are kept alive in the interpreter state.
493+
494+
The special treatment of "builtins" here is because even
495+
when it's not referenced as a module, its dictionary is
496+
referenced by almost every module's __builtins__. Since
497+
deleting a module clears its dictionary (even if there are
498+
references left to it), we need to delete the "builtins"
499+
module last. Likewise, we don't delete sys until the very
500+
end because it is implicitly referenced (e.g. by print). */
492501
if (weaklist != NULL) {
493502
Py_ssize_t i, n;
494503
n = PyList_GET_SIZE(weaklist);
@@ -498,17 +507,27 @@ PyImport_Cleanup(void)
498507
PyObject *mod = PyWeakref_GET_OBJECT(PyTuple_GET_ITEM(tup, 1));
499508
if (mod == Py_None)
500509
continue;
501-
Py_INCREF(mod);
502510
assert(PyModule_Check(mod));
511+
dict = PyModule_GetDict(mod);
512+
if (dict == interp->builtins || dict == interp->sysdict)
513+
continue;
514+
Py_INCREF(mod);
503515
if (Py_VerboseFlag && PyUnicode_Check(name))
504-
PySys_FormatStderr("# cleanup[3] wiping %U\n",
505-
name, mod);
516+
PySys_FormatStderr("# cleanup[3] wiping %U\n", name);
506517
_PyModule_Clear(mod);
507518
Py_DECREF(mod);
508519
}
509520
Py_DECREF(weaklist);
510521
}
511522

523+
/* Next, delete sys and builtins (in that order) */
524+
if (Py_VerboseFlag)
525+
PySys_FormatStderr("# cleanup[3] wiping sys\n");
526+
_PyModule_ClearDict(interp->sysdict);
527+
if (Py_VerboseFlag)
528+
PySys_FormatStderr("# cleanup[3] wiping builtins\n");
529+
_PyModule_ClearDict(interp->builtins);
530+
512531
/* Clear and delete the modules directory. Actual modules will
513532
still be there only if imported during the execution of some
514533
destructor. */

Python/pystate.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ PyInterpreterState_New(void)
7272
interp->modules_by_index = NULL;
7373
interp->sysdict = NULL;
7474
interp->builtins = NULL;
75+
interp->builtins_copy = NULL;
7576
interp->tstate_head = NULL;
7677
interp->codec_search_path = NULL;
7778
interp->codec_search_cache = NULL;
@@ -115,6 +116,7 @@ PyInterpreterState_Clear(PyInterpreterState *interp)
115116
Py_CLEAR(interp->modules_by_index);
116117
Py_CLEAR(interp->sysdict);
117118
Py_CLEAR(interp->builtins);
119+
Py_CLEAR(interp->builtins_copy);
118120
Py_CLEAR(interp->importlib);
119121
}
120122

0 commit comments

Comments
 (0)