Skip to content

gh-132859: Run debugger scripts in their own namespaces #132860

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 23, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions Lib/test/test_sys.py
Original file line number Diff line number Diff line change
Expand Up @@ -2125,6 +2125,19 @@ def test_remote_exec_with_exception(self):
self.assertIn(b"Remote script exception", stderr)
self.assertEqual(stdout.strip(), b"Target process running...")

def test_new_namespace_for_each_remote_exec(self):
"""Test that each remote_exec call gets its own namespace."""
script = textwrap.dedent(
"""
assert globals() is not __import__("__main__").__dict__
print("Remote script executed successfully!")
"""
)
returncode, stdout, stderr = self._run_remote_exec_test(script)
self.assertEqual(returncode, 0)
self.assertEqual(stderr, b"")
self.assertIn(b"Remote script executed successfully", stdout)

def test_remote_exec_disabled_by_env(self):
"""Test remote exec is disabled when PYTHON_DISABLE_REMOTE_DEBUG is set"""
env = os.environ.copy()
Expand Down
38 changes: 25 additions & 13 deletions Python/ceval_gil.c
Original file line number Diff line number Diff line change
Expand Up @@ -1193,6 +1193,29 @@ _PyEval_DisableGIL(PyThreadState *tstate)
#endif

#if defined(Py_REMOTE_DEBUG) && defined(Py_SUPPORTS_REMOTE_DEBUG)
// Note that this function is inline to avoid creating a PLT entry
// that would be an easy target for a ROP gadget.
static inline int run_remote_debugger_source(PyObject *source)
{
const char *str = PyBytes_AsString(source);
if (!str) {
return -1;
}

PyObject *ns = PyDict_New();
if (!ns) {
return -1;
}

PyObject *res = PyRun_String(str, Py_file_input, ns, ns);
Py_DECREF(ns);
if (!res) {
return -1;
}
Py_DECREF(res);
return 0;
}

// Note that this function is inline to avoid creating a PLT entry
// that would be an easy target for a ROP gadget.
static inline void run_remote_debugger_script(const char *path)
Expand Down Expand Up @@ -1225,22 +1248,11 @@ static inline void run_remote_debugger_script(const char *path)
Py_DECREF(fileobj);

if (source) {
const char *str = PyBytes_AsString(source);
if (str) {
// PyRun_SimpleString() automatically raises an unraisable
// exception if it fails so we don't need to check the return value.
PyRun_SimpleString(str);
} else {
PyErr_FormatUnraisable("Error reading debugger script %s", path);
if (0 != run_remote_debugger_source(source)) {
PyErr_FormatUnraisable("Error executing debugger script %s", path);
}
Py_DECREF(source);
}

// Just in case something went wrong, don't leave this function
// with an unhandled exception.
if (PyErr_Occurred()) {
PyErr_FormatUnraisable("Error executing debugger script %s", path);
}
}

int _PyRunRemoteDebugger(PyThreadState *tstate)
Expand Down
Loading