Issue #19255: The builtins module is restored to initial value before

cleaning other modules.  The sys and builtins modules are cleaned last.
This commit is contained in:
Serhiy Storchaka 2014-02-10 18:21:34 +02:00
parent 7614122467
commit 013bb91aa3
7 changed files with 96 additions and 39 deletions

View File

@ -25,6 +25,7 @@ PyAPI_FUNC(const char *) PyModule_GetFilename(PyObject *);
PyAPI_FUNC(PyObject *) PyModule_GetFilenameObject(PyObject *); PyAPI_FUNC(PyObject *) PyModule_GetFilenameObject(PyObject *);
#ifndef Py_LIMITED_API #ifndef Py_LIMITED_API
PyAPI_FUNC(void) _PyModule_Clear(PyObject *); PyAPI_FUNC(void) _PyModule_Clear(PyObject *);
PyAPI_FUNC(void) _PyModule_ClearDict(PyObject *);
#endif #endif
PyAPI_FUNC(struct PyModuleDef*) PyModule_GetDef(PyObject*); PyAPI_FUNC(struct PyModuleDef*) PyModule_GetDef(PyObject*);
PyAPI_FUNC(void*) PyModule_GetState(PyObject*); PyAPI_FUNC(void*) PyModule_GetState(PyObject*);

View File

@ -33,7 +33,6 @@ typedef struct _is {
int codecs_initialized; int codecs_initialized;
int fscodec_initialized; int fscodec_initialized;
#ifdef HAVE_DLOPEN #ifdef HAVE_DLOPEN
int dlopenflags; int dlopenflags;
#endif #endif
@ -41,6 +40,7 @@ typedef struct _is {
int tscdump; int tscdump;
#endif #endif
PyObject *builtins_copy;
} PyInterpreterState; } PyInterpreterState;
#endif #endif

View File

@ -16,6 +16,7 @@ import unittest
import warnings import warnings
from operator import neg from operator import neg
from test.support import TESTFN, unlink, run_unittest, check_warnings from test.support import TESTFN, unlink, run_unittest, check_warnings
from test.script_helper import assert_python_ok
try: try:
import pty, signal import pty, signal
except ImportError: except ImportError:
@ -1592,6 +1593,34 @@ class TestSorted(unittest.TestCase):
data = 'The quick Brown fox Jumped over The lazy Dog'.split() data = 'The quick Brown fox Jumped over The lazy Dog'.split()
self.assertRaises(TypeError, sorted, data, None, lambda x,y: 0) self.assertRaises(TypeError, sorted, data, None, lambda x,y: 0)
class ShutdownTest(unittest.TestCase):
def test_cleanup(self):
# Issue #19255: builtins are still available at shutdown
code = """if 1:
import builtins
import sys
class C:
def __del__(self):
print("before")
# Check that builtins still exist
len(())
print("after")
c = C()
# Make this module survive until builtins and sys are cleaned
builtins.here = sys.modules[__name__]
sys.here = sys.modules[__name__]
# Create a reference loop so that this module needs to go
# through a GC phase.
here = sys.modules[__name__]
"""
rc, out, err = assert_python_ok("-c", code)
self.assertEqual(["before", "after"], out.decode().splitlines())
def load_tests(loader, tests, pattern): def load_tests(loader, tests, pattern):
from doctest import DocTestSuite from doctest import DocTestSuite
tests.addTest(DocTestSuite(builtins)) tests.addTest(DocTestSuite(builtins))

View File

@ -89,6 +89,9 @@ Release date: 2014-02-10
Core and Builtins Core and Builtins
----------------- -----------------
- Issue #19255: The builtins module is restored to initial value before
cleaning other modules. The sys and builtins modules are cleaned last.
- Issue #20437: Fixed 22 potential bugs when deleting objects references. - Issue #20437: Fixed 22 potential bugs when deleting objects references.
- Issue #20500: Displaying an exception at interpreter shutdown no longer - Issue #20500: Displaying an exception at interpreter shutdown no longer

View File

@ -298,6 +298,14 @@ PyModule_GetState(PyObject* m)
void void
_PyModule_Clear(PyObject *m) _PyModule_Clear(PyObject *m)
{
PyObject *d = ((PyModuleObject *)m)->md_dict;
if (d != NULL)
_PyModule_ClearDict(d);
}
void
_PyModule_ClearDict(PyObject *d)
{ {
/* To make the execution order of destructors for global /* To make the execution order of destructors for global
objects a bit more predictable, we first zap all objects objects a bit more predictable, we first zap all objects
@ -308,11 +316,6 @@ _PyModule_Clear(PyObject *m)
Py_ssize_t pos; Py_ssize_t pos;
PyObject *key, *value; PyObject *key, *value;
PyObject *d;
d = ((PyModuleObject *)m)->md_dict;
if (d == NULL)
return;
/* First, clear only names starting with a single underscore */ /* First, clear only names starting with a single underscore */
pos = 0; pos = 0;

View File

@ -49,9 +49,13 @@ class fs_unicode_converter(CConverter):
void void
_PyImport_Init(void) _PyImport_Init(void)
{ {
PyInterpreterState *interp = PyThreadState_Get()->interp;
initstr = PyUnicode_InternFromString("__init__"); initstr = PyUnicode_InternFromString("__init__");
if (initstr == NULL) if (initstr == NULL)
Py_FatalError("Can't initialize import variables"); Py_FatalError("Can't initialize import variables");
interp->builtins_copy = PyDict_Copy(interp->builtins);
if (interp->builtins_copy == NULL)
Py_FatalError("Can't backup builtins dict");
} }
void void
@ -397,8 +401,10 @@ PyImport_Cleanup(void)
PyObject *key, *value, *dict; PyObject *key, *value, *dict;
PyInterpreterState *interp = PyThreadState_GET()->interp; PyInterpreterState *interp = PyThreadState_GET()->interp;
PyObject *modules = interp->modules; PyObject *modules = interp->modules;
PyObject *builtins = interp->builtins; PyObject *builtins_mod = NULL;
PyObject *sys_mod = NULL;
PyObject *weaklist = NULL; PyObject *weaklist = NULL;
char **p;
if (modules == NULL) if (modules == NULL)
return; /* Already done */ return; /* Already done */
@ -411,31 +417,22 @@ PyImport_Cleanup(void)
/* XXX Perhaps these precautions are obsolete. Who knows? */ /* XXX Perhaps these precautions are obsolete. Who knows? */
value = PyDict_GetItemString(modules, "builtins"); if (Py_VerboseFlag)
if (value != NULL && PyModule_Check(value)) { PySys_WriteStderr("# clear builtins._\n");
dict = PyModule_GetDict(value); PyDict_SetItemString(interp->builtins, "_", Py_None);
for (p = sys_deletes; *p != NULL; p++) {
if (Py_VerboseFlag) if (Py_VerboseFlag)
PySys_WriteStderr("# clear builtins._\n"); PySys_WriteStderr("# clear sys.%s\n", *p);
PyDict_SetItemString(dict, "_", Py_None); PyDict_SetItemString(interp->sysdict, *p, Py_None);
} }
value = PyDict_GetItemString(modules, "sys"); for (p = sys_files; *p != NULL; p+=2) {
if (value != NULL && PyModule_Check(value)) { if (Py_VerboseFlag)
char **p; PySys_WriteStderr("# restore sys.%s\n", *p);
PyObject *v; value = PyDict_GetItemString(interp->sysdict, *(p+1));
dict = PyModule_GetDict(value); if (value == NULL)
for (p = sys_deletes; *p != NULL; p++) { value = Py_None;
if (Py_VerboseFlag) PyDict_SetItemString(interp->sysdict, *p, value);
PySys_WriteStderr("# clear sys.%s\n", *p);
PyDict_SetItemString(dict, *p, Py_None);
}
for (p = sys_files; *p != NULL; p+=2) {
if (Py_VerboseFlag)
PySys_WriteStderr("# restore sys.%s\n", *p);
v = PyDict_GetItemString(dict, *(p+1));
if (v == NULL)
v = Py_None;
PyDict_SetItemString(dict, *p, v);
}
} }
/* We prepare a list which will receive (name, weakref) tuples of /* We prepare a list which will receive (name, weakref) tuples of
@ -473,11 +470,15 @@ PyImport_Cleanup(void)
/* Clear the modules dict. */ /* Clear the modules dict. */
PyDict_Clear(modules); PyDict_Clear(modules);
/* Replace the interpreter's reference to builtins with an empty dict /* Restore the original builtins dict, to ensure that any
(module globals still have a reference to the original builtins). */ user data gets cleared. */
builtins = interp->builtins; dict = PyDict_Copy(interp->builtins);
interp->builtins = PyDict_New(); if (dict == NULL)
Py_DECREF(builtins); PyErr_Clear();
PyDict_Clear(interp->builtins);
if (PyDict_Update(interp->builtins, interp->builtins_copy))
PyErr_Clear();
Py_XDECREF(dict);
/* Clear module dict copies stored in the interpreter state */ /* Clear module dict copies stored in the interpreter state */
_PyState_ClearModules(); _PyState_ClearModules();
/* Collect references */ /* Collect references */
@ -488,7 +489,15 @@ PyImport_Cleanup(void)
/* Now, if there are any modules left alive, clear their globals to /* Now, if there are any modules left alive, clear their globals to
minimize potential leaks. All C extension modules actually end minimize potential leaks. All C extension modules actually end
up here, since they are kept alive in the interpreter state. */ up here, since they are kept alive in the interpreter state.
The special treatment of "builtins" here is because even
when it's not referenced as a module, its dictionary is
referenced by almost every module's __builtins__. Since
deleting a module clears its dictionary (even if there are
references left to it), we need to delete the "builtins"
module last. Likewise, we don't delete sys until the very
end because it is implicitly referenced (e.g. by print). */
if (weaklist != NULL) { if (weaklist != NULL) {
Py_ssize_t i, n; Py_ssize_t i, n;
n = PyList_GET_SIZE(weaklist); n = PyList_GET_SIZE(weaklist);
@ -498,17 +507,27 @@ PyImport_Cleanup(void)
PyObject *mod = PyWeakref_GET_OBJECT(PyTuple_GET_ITEM(tup, 1)); PyObject *mod = PyWeakref_GET_OBJECT(PyTuple_GET_ITEM(tup, 1));
if (mod == Py_None) if (mod == Py_None)
continue; continue;
Py_INCREF(mod);
assert(PyModule_Check(mod)); assert(PyModule_Check(mod));
dict = PyModule_GetDict(mod);
if (dict == interp->builtins || dict == interp->sysdict)
continue;
Py_INCREF(mod);
if (Py_VerboseFlag && PyUnicode_Check(name)) if (Py_VerboseFlag && PyUnicode_Check(name))
PySys_FormatStderr("# cleanup[3] wiping %U\n", PySys_FormatStderr("# cleanup[3] wiping %U\n", name);
name, mod);
_PyModule_Clear(mod); _PyModule_Clear(mod);
Py_DECREF(mod); Py_DECREF(mod);
} }
Py_DECREF(weaklist); Py_DECREF(weaklist);
} }
/* Next, delete sys and builtins (in that order) */
if (Py_VerboseFlag)
PySys_FormatStderr("# cleanup[3] wiping sys\n");
_PyModule_ClearDict(interp->sysdict);
if (Py_VerboseFlag)
PySys_FormatStderr("# cleanup[3] wiping builtins\n");
_PyModule_ClearDict(interp->builtins);
/* Clear and delete the modules directory. Actual modules will /* Clear and delete the modules directory. Actual modules will
still be there only if imported during the execution of some still be there only if imported during the execution of some
destructor. */ destructor. */

View File

@ -72,6 +72,7 @@ PyInterpreterState_New(void)
interp->modules_by_index = NULL; interp->modules_by_index = NULL;
interp->sysdict = NULL; interp->sysdict = NULL;
interp->builtins = NULL; interp->builtins = NULL;
interp->builtins_copy = NULL;
interp->tstate_head = NULL; interp->tstate_head = NULL;
interp->codec_search_path = NULL; interp->codec_search_path = NULL;
interp->codec_search_cache = NULL; interp->codec_search_cache = NULL;
@ -115,6 +116,7 @@ PyInterpreterState_Clear(PyInterpreterState *interp)
Py_CLEAR(interp->modules_by_index); Py_CLEAR(interp->modules_by_index);
Py_CLEAR(interp->sysdict); Py_CLEAR(interp->sysdict);
Py_CLEAR(interp->builtins); Py_CLEAR(interp->builtins);
Py_CLEAR(interp->builtins_copy);
Py_CLEAR(interp->importlib); Py_CLEAR(interp->importlib);
} }