Skip to content

Commit a94c752

Browse files
authored
gh-132859: Run debugger scripts in their own namespaces (#132860)
Run debugger scripts in their own namespaces Previously scripts injected by `sys.remote_exec` were run with the globals of the `__main__` module. Instead, run each injected script with an empty set of globals. If someone really wants to use the `__main__` module's namespace, they can always `import __main__`.
1 parent 402dba2 commit a94c752

File tree

2 files changed

+38
-13
lines changed

2 files changed

+38
-13
lines changed

Lib/test/test_sys.py

+13
Original file line numberDiff line numberDiff line change
@@ -2125,6 +2125,19 @@ def test_remote_exec_with_exception(self):
21252125
self.assertIn(b"Remote script exception", stderr)
21262126
self.assertEqual(stdout.strip(), b"Target process running...")
21272127

2128+
def test_new_namespace_for_each_remote_exec(self):
2129+
"""Test that each remote_exec call gets its own namespace."""
2130+
script = textwrap.dedent(
2131+
"""
2132+
assert globals() is not __import__("__main__").__dict__
2133+
print("Remote script executed successfully!")
2134+
"""
2135+
)
2136+
returncode, stdout, stderr = self._run_remote_exec_test(script)
2137+
self.assertEqual(returncode, 0)
2138+
self.assertEqual(stderr, b"")
2139+
self.assertIn(b"Remote script executed successfully", stdout)
2140+
21282141
def test_remote_exec_disabled_by_env(self):
21292142
"""Test remote exec is disabled when PYTHON_DISABLE_REMOTE_DEBUG is set"""
21302143
env = os.environ.copy()

Python/ceval_gil.c

+25-13
Original file line numberDiff line numberDiff line change
@@ -1193,6 +1193,29 @@ _PyEval_DisableGIL(PyThreadState *tstate)
11931193
#endif
11941194

11951195
#if defined(Py_REMOTE_DEBUG) && defined(Py_SUPPORTS_REMOTE_DEBUG)
1196+
// Note that this function is inline to avoid creating a PLT entry
1197+
// that would be an easy target for a ROP gadget.
1198+
static inline int run_remote_debugger_source(PyObject *source)
1199+
{
1200+
const char *str = PyBytes_AsString(source);
1201+
if (!str) {
1202+
return -1;
1203+
}
1204+
1205+
PyObject *ns = PyDict_New();
1206+
if (!ns) {
1207+
return -1;
1208+
}
1209+
1210+
PyObject *res = PyRun_String(str, Py_file_input, ns, ns);
1211+
Py_DECREF(ns);
1212+
if (!res) {
1213+
return -1;
1214+
}
1215+
Py_DECREF(res);
1216+
return 0;
1217+
}
1218+
11961219
// Note that this function is inline to avoid creating a PLT entry
11971220
// that would be an easy target for a ROP gadget.
11981221
static inline void run_remote_debugger_script(const char *path)
@@ -1225,22 +1248,11 @@ static inline void run_remote_debugger_script(const char *path)
12251248
Py_DECREF(fileobj);
12261249

12271250
if (source) {
1228-
const char *str = PyBytes_AsString(source);
1229-
if (str) {
1230-
// PyRun_SimpleString() automatically raises an unraisable
1231-
// exception if it fails so we don't need to check the return value.
1232-
PyRun_SimpleString(str);
1233-
} else {
1234-
PyErr_FormatUnraisable("Error reading debugger script %s", path);
1251+
if (0 != run_remote_debugger_source(source)) {
1252+
PyErr_FormatUnraisable("Error executing debugger script %s", path);
12351253
}
12361254
Py_DECREF(source);
12371255
}
1238-
1239-
// Just in case something went wrong, don't leave this function
1240-
// with an unhandled exception.
1241-
if (PyErr_Occurred()) {
1242-
PyErr_FormatUnraisable("Error executing debugger script %s", path);
1243-
}
12441256
}
12451257

12461258
int _PyRunRemoteDebugger(PyThreadState *tstate)

0 commit comments

Comments
 (0)