Skip to content

Commit 8468e10

Browse files
committed
Merge branch 'main' into tstrings
2 parents ce78496 + a94c752 commit 8468e10

File tree

5 files changed

+129
-53
lines changed

5 files changed

+129
-53
lines changed

Include/internal/pycore_ceval.h

+4
Original file line numberDiff line numberDiff line change
@@ -373,6 +373,10 @@ PyAPI_FUNC(_PyStackRef) _PyFloat_FromDouble_ConsumeInputs(_PyStackRef left, _PyS
373373
#endif
374374
#endif
375375

376+
#if defined(Py_REMOTE_DEBUG) && defined(Py_SUPPORTS_REMOTE_DEBUG)
377+
extern int _PyRunRemoteDebugger(PyThreadState *tstate);
378+
#endif
379+
376380
#ifdef __cplusplus
377381
}
378382
#endif

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()

Modules/signalmodule.c

+4
Original file line numberDiff line numberDiff line change
@@ -1777,6 +1777,10 @@ PyErr_CheckSignals(void)
17771777
_Py_RunGC(tstate);
17781778
}
17791779

1780+
#if defined(Py_REMOTE_DEBUG) && defined(Py_SUPPORTS_REMOTE_DEBUG)
1781+
_PyRunRemoteDebugger(tstate);
1782+
#endif
1783+
17801784
if (!_Py_ThreadCanHandleSignals(tstate->interp)) {
17811785
return 0;
17821786
}

Python/ceval_gil.c

+56-37
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,23 +1248,44 @@ 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
}
1256+
}
12381257

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);
1258+
int _PyRunRemoteDebugger(PyThreadState *tstate)
1259+
{
1260+
const PyConfig *config = _PyInterpreterState_GetConfig(tstate->interp);
1261+
if (config->remote_debug == 1
1262+
&& tstate->remote_debugger_support.debugger_pending_call == 1)
1263+
{
1264+
tstate->remote_debugger_support.debugger_pending_call = 0;
1265+
1266+
// Immediately make a copy in case of a race with another debugger
1267+
// process that's trying to write to the buffer. At least this way
1268+
// we'll be internally consistent: what we audit is what we run.
1269+
const size_t pathsz
1270+
= sizeof(tstate->remote_debugger_support.debugger_script_path);
1271+
1272+
char *path = PyMem_Malloc(pathsz);
1273+
if (path) {
1274+
// And don't assume the debugger correctly null terminated it.
1275+
memcpy(
1276+
path,
1277+
tstate->remote_debugger_support.debugger_script_path,
1278+
pathsz);
1279+
path[pathsz - 1] = '\0';
1280+
if (*path) {
1281+
run_remote_debugger_script(path);
1282+
}
1283+
PyMem_Free(path);
1284+
}
12431285
}
1286+
return 0;
12441287
}
1288+
12451289
#endif
12461290

12471291
/* Do periodic things, like check for signals and async I/0.
@@ -1372,32 +1416,7 @@ _Py_HandlePending(PyThreadState *tstate)
13721416
}
13731417

13741418
#if defined(Py_REMOTE_DEBUG) && defined(Py_SUPPORTS_REMOTE_DEBUG)
1375-
const PyConfig *config = _PyInterpreterState_GetConfig(tstate->interp);
1376-
if (config->remote_debug == 1
1377-
&& tstate->remote_debugger_support.debugger_pending_call == 1)
1378-
{
1379-
tstate->remote_debugger_support.debugger_pending_call = 0;
1380-
1381-
// Immediately make a copy in case of a race with another debugger
1382-
// process that's trying to write to the buffer. At least this way
1383-
// we'll be internally consistent: what we audit is what we run.
1384-
const size_t pathsz
1385-
= sizeof(tstate->remote_debugger_support.debugger_script_path);
1386-
1387-
char *path = PyMem_Malloc(pathsz);
1388-
if (path) {
1389-
// And don't assume the debugger correctly null terminated it.
1390-
memcpy(
1391-
path,
1392-
tstate->remote_debugger_support.debugger_script_path,
1393-
pathsz);
1394-
path[pathsz - 1] = '\0';
1395-
if (*path) {
1396-
run_remote_debugger_script(path);
1397-
}
1398-
PyMem_Free(path);
1399-
}
1400-
}
1419+
_PyRunRemoteDebugger(tstate);
14011420
#endif
14021421

14031422
return 0;

Python/traceback.c

+52-16
Original file line numberDiff line numberDiff line change
@@ -842,11 +842,11 @@ _Py_DumpDecimal(int fd, size_t value)
842842

843843
/* Format an integer as hexadecimal with width digits into fd file descriptor.
844844
The function is signal safe. */
845-
void
846-
_Py_DumpHexadecimal(int fd, uintptr_t value, Py_ssize_t width)
845+
static void
846+
dump_hexadecimal(int fd, uintptr_t value, Py_ssize_t width, int strip_zeros)
847847
{
848848
char buffer[sizeof(uintptr_t) * 2 + 1], *ptr, *end;
849-
const Py_ssize_t size = Py_ARRAY_LENGTH(buffer) - 1;
849+
Py_ssize_t size = Py_ARRAY_LENGTH(buffer) - 1;
850850

851851
if (width > size)
852852
width = size;
@@ -862,7 +862,35 @@ _Py_DumpHexadecimal(int fd, uintptr_t value, Py_ssize_t width)
862862
value >>= 4;
863863
} while ((end - ptr) < width || value);
864864

865-
(void)_Py_write_noraise(fd, ptr, end - ptr);
865+
size = end - ptr;
866+
if (strip_zeros) {
867+
while (*ptr == '0' && size >= 2) {
868+
ptr++;
869+
size--;
870+
}
871+
}
872+
873+
(void)_Py_write_noraise(fd, ptr, size);
874+
}
875+
876+
void
877+
_Py_DumpHexadecimal(int fd, uintptr_t value, Py_ssize_t width)
878+
{
879+
dump_hexadecimal(fd, value, width, 0);
880+
}
881+
882+
static void
883+
dump_pointer(int fd, void *ptr)
884+
{
885+
PUTS(fd, "0x");
886+
dump_hexadecimal(fd, (uintptr_t)ptr, sizeof(void*), 1);
887+
}
888+
889+
static void
890+
dump_char(int fd, char ch)
891+
{
892+
char buf[1] = {ch};
893+
(void)_Py_write_noraise(fd, buf, 1);
866894
}
867895

868896
void
@@ -924,8 +952,7 @@ _Py_DumpASCII(int fd, PyObject *text)
924952
ch = PyUnicode_READ(kind, data, i);
925953
if (' ' <= ch && ch <= 126) {
926954
/* printable ASCII character */
927-
char c = (char)ch;
928-
(void)_Py_write_noraise(fd, &c, 1);
955+
dump_char(fd, (char)ch);
929956
}
930957
else if (ch <= 0xff) {
931958
PUTS(fd, "\\x");
@@ -1227,7 +1254,9 @@ _Py_backtrace_symbols_fd(int fd, void *const *array, Py_ssize_t size)
12271254
|| info[i].dli_fname == NULL
12281255
|| info[i].dli_fname[0] == '\0'
12291256
) {
1230-
dprintf(fd, " Binary file '<unknown>' [%p]\n", array[i]);
1257+
PUTS(fd, " Binary file '<unknown>' [");
1258+
dump_pointer(fd, array[i]);
1259+
PUTS(fd, "]\n");
12311260
continue;
12321261
}
12331262

@@ -1237,11 +1266,12 @@ _Py_backtrace_symbols_fd(int fd, void *const *array, Py_ssize_t size)
12371266
info[i].dli_saddr = info[i].dli_fbase;
12381267
}
12391268

1240-
if (info[i].dli_sname == NULL
1241-
&& info[i].dli_saddr == 0) {
1242-
dprintf(fd, " Binary file \"%s\" [%p]\n",
1243-
info[i].dli_fname,
1244-
array[i]);
1269+
if (info[i].dli_sname == NULL && info[i].dli_saddr == 0) {
1270+
PUTS(fd, " Binary file \"");
1271+
PUTS(fd, info[i].dli_fname);
1272+
PUTS(fd, "\" [");
1273+
dump_pointer(fd, array[i]);
1274+
PUTS(fd, "]\n");
12451275
}
12461276
else {
12471277
char sign;
@@ -1255,10 +1285,16 @@ _Py_backtrace_symbols_fd(int fd, void *const *array, Py_ssize_t size)
12551285
offset = info[i].dli_saddr - array[i];
12561286
}
12571287
const char *symbol_name = info[i].dli_sname != NULL ? info[i].dli_sname : "";
1258-
dprintf(fd, " Binary file \"%s\", at %s%c%#tx [%p]\n",
1259-
info[i].dli_fname,
1260-
symbol_name,
1261-
sign, offset, array[i]);
1288+
PUTS(fd, " Binary file \"");
1289+
PUTS(fd, info[i].dli_fname);
1290+
PUTS(fd, "\", at ");
1291+
PUTS(fd, symbol_name);
1292+
dump_char(fd, sign);
1293+
PUTS(fd, "0x");
1294+
dump_hexadecimal(fd, offset, sizeof(offset), 1);
1295+
PUTS(fd, " [");
1296+
dump_pointer(fd, array[i]);
1297+
PUTS(fd, "]\n");
12621298
}
12631299
}
12641300
}

0 commit comments

Comments
 (0)