Skip to content

Commit 7d2ffad

Browse files
author
NGRsoftlab
authored
gh-116180: Check the globals argument in PyRun_* C API (GH-116637)
It used to crash when passing NULL or non-dict as globals. Now it sets a SystemError.
1 parent 7c97dc8 commit 7d2ffad

File tree

2 files changed

+36
-21
lines changed

2 files changed

+36
-21
lines changed

Lib/test/test_capi/test_run.py

+24-12
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@
1111
Py_eval_input = _testcapi.Py_eval_input
1212

1313

14+
class DictSubclass(dict):
15+
pass
16+
17+
1418
class CAPITest(unittest.TestCase):
1519
# TODO: Test the following functions:
1620
#
@@ -50,15 +54,19 @@ def run(s, *args):
5054
self.assertRaises(TypeError, run, b'a\n', dict(a=1), [])
5155
self.assertRaises(TypeError, run, b'a\n', dict(a=1), 1)
5256

57+
self.assertIsNone(run(b'a\n', DictSubclass(a=1)))
58+
self.assertIsNone(run(b'a\n', DictSubclass(), dict(a=1)))
59+
self.assertRaises(NameError, run, b'a\n', DictSubclass())
60+
5361
self.assertIsNone(run(b'\xc3\xa4\n', {'\xe4': 1}))
5462
self.assertRaises(SyntaxError, run, b'\xe4\n', {})
5563

56-
# CRASHES run(b'a\n', NULL)
57-
# CRASHES run(b'a\n', NULL, {})
58-
# CRASHES run(b'a\n', NULL, dict(a=1))
59-
# CRASHES run(b'a\n', UserDict())
60-
# CRASHES run(b'a\n', UserDict(), {})
61-
# CRASHES run(b'a\n', UserDict(), dict(a=1))
64+
self.assertRaises(SystemError, run, b'a\n', NULL)
65+
self.assertRaises(SystemError, run, b'a\n', NULL, {})
66+
self.assertRaises(SystemError, run, b'a\n', NULL, dict(a=1))
67+
self.assertRaises(SystemError, run, b'a\n', UserDict())
68+
self.assertRaises(SystemError, run, b'a\n', UserDict(), {})
69+
self.assertRaises(SystemError, run, b'a\n', UserDict(), dict(a=1))
6270

6371
# CRASHES run(NULL, {})
6472

@@ -82,12 +90,16 @@ def run(*args):
8290
self.assertRaises(TypeError, run, dict(a=1), [])
8391
self.assertRaises(TypeError, run, dict(a=1), 1)
8492

85-
# CRASHES run(NULL)
86-
# CRASHES run(NULL, {})
87-
# CRASHES run(NULL, dict(a=1))
88-
# CRASHES run(UserDict())
89-
# CRASHES run(UserDict(), {})
90-
# CRASHES run(UserDict(), dict(a=1))
93+
self.assertIsNone(run(DictSubclass(a=1)))
94+
self.assertIsNone(run(DictSubclass(), dict(a=1)))
95+
self.assertRaises(NameError, run, DictSubclass())
96+
97+
self.assertRaises(SystemError, run, NULL)
98+
self.assertRaises(SystemError, run, NULL, {})
99+
self.assertRaises(SystemError, run, NULL, dict(a=1))
100+
self.assertRaises(SystemError, run, UserDict())
101+
self.assertRaises(SystemError, run, UserDict(), {})
102+
self.assertRaises(SystemError, run, UserDict(), dict(a=1))
91103

92104
@unittest.skipUnless(TESTFN_UNDECODABLE, 'only works if there are undecodable paths')
93105
@unittest.skipIf(os.name == 'nt', 'does not work on Windows')

Python/pythonrun.c

+12-9
Original file line numberDiff line numberDiff line change
@@ -1275,17 +1275,20 @@ run_eval_code_obj(PyThreadState *tstate, PyCodeObject *co, PyObject *globals, Py
12751275
_PyRuntime.signals.unhandled_keyboard_interrupt = 0;
12761276

12771277
/* Set globals['__builtins__'] if it doesn't exist */
1278-
if (globals != NULL) {
1279-
int has_builtins = PyDict_ContainsString(globals, "__builtins__");
1280-
if (has_builtins < 0) {
1278+
if (!globals || !PyDict_Check(globals)) {
1279+
PyErr_SetString(PyExc_SystemError, "globals must be a real dict");
1280+
return NULL;
1281+
}
1282+
int has_builtins = PyDict_ContainsString(globals, "__builtins__");
1283+
if (has_builtins < 0) {
1284+
return NULL;
1285+
}
1286+
if (!has_builtins) {
1287+
if (PyDict_SetItemString(globals, "__builtins__",
1288+
tstate->interp->builtins) < 0)
1289+
{
12811290
return NULL;
12821291
}
1283-
if (!has_builtins) {
1284-
if (PyDict_SetItemString(globals, "__builtins__",
1285-
tstate->interp->builtins) < 0) {
1286-
return NULL;
1287-
}
1288-
}
12891292
}
12901293

12911294
v = PyEval_EvalCode((PyObject*)co, globals, locals);

0 commit comments

Comments
 (0)