Skip to content

Commit c2d3d6b

Browse files
authored
gh-144316: Fix missing exception in _remote_debugging with debug=False (#144442)
1 parent 4401f23 commit c2d3d6b

File tree

7 files changed

+21
-5
lines changed

7 files changed

+21
-5
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix crash in ``_remote_debugging`` that caused ``test_external_inspection`` to intermittently fail. Patch by Taegyun Kim.

Modules/_remote_debugging/_remote_debugging.h

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ extern "C" {
2929
#include "internal/pycore_interpframe.h" // FRAME_OWNED_BY_INTERPRETER
3030
#include "internal/pycore_llist.h" // struct llist_node
3131
#include "internal/pycore_long.h" // _PyLong_GetZero
32+
#include "internal/pycore_pyerrors.h" // _PyErr_FormatFromCause
3233
#include "internal/pycore_stackref.h" // Py_TAG_BITS
3334
#include "../../Python/remote_debug.h"
3435

@@ -173,10 +174,13 @@ typedef enum _WIN32_THREADSTATE {
173174
#define THREAD_STATUS_HAS_EXCEPTION (1 << 4)
174175

175176
/* Exception cause macro */
176-
#define set_exception_cause(unwinder, exc_type, message) \
177-
if (unwinder->debug) { \
178-
_set_debug_exception_cause(exc_type, message); \
179-
}
177+
#define set_exception_cause(unwinder, exc_type, message) \
178+
do { \
179+
assert(PyErr_Occurred() && "function returned -1 without setting exception"); \
180+
if (unwinder->debug) { \
181+
_set_debug_exception_cause(exc_type, message); \
182+
} \
183+
} while (0)
180184

181185
/* ============================================================================
182186
* TYPE DEFINITIONS

Modules/_remote_debugging/asyncio.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@ iterate_set_entries(
121121

122122
// Validate mask and num_els to prevent huge loop iterations from garbage data
123123
if (mask < 0 || mask >= MAX_SET_TABLE_SIZE || num_els < 0 || num_els > mask + 1) {
124+
PyErr_SetString(PyExc_RuntimeError, "Invalid set object (corrupted remote memory)");
124125
set_exception_cause(unwinder, PyExc_RuntimeError,
125126
"Invalid set object (corrupted remote memory)");
126127
return -1;

Modules/_remote_debugging/code_objects.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -446,6 +446,9 @@ parse_code_object(RemoteUnwinderObject *unwinder,
446446
if (tlbc_entry) {
447447
// Validate index bounds (also catches negative values since tlbc_index is signed)
448448
if (ctx->tlbc_index < 0 || ctx->tlbc_index >= tlbc_entry->tlbc_array_size) {
449+
PyErr_Format(PyExc_RuntimeError,
450+
"Invalid tlbc_index %d (array size %zd, corrupted remote memory)",
451+
ctx->tlbc_index, tlbc_entry->tlbc_array_size);
449452
set_exception_cause(unwinder, PyExc_RuntimeError,
450453
"Invalid tlbc_index (corrupted remote memory)");
451454
goto error;

Modules/_remote_debugging/frames.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ process_single_stack_chunk(
4949
// Size must be at least enough for the header and reasonably bounded
5050
if (actual_size <= offsetof(_PyStackChunk, data) || actual_size > MAX_STACK_CHUNK_SIZE) {
5151
PyMem_RawFree(this_chunk);
52+
PyErr_Format(PyExc_RuntimeError,
53+
"Invalid stack chunk size %zu (corrupted remote memory)", actual_size);
5254
set_exception_cause(unwinder, PyExc_RuntimeError,
5355
"Invalid stack chunk size (corrupted remote memory)");
5456
return -1;
@@ -244,6 +246,7 @@ parse_frame_from_chunks(
244246
) {
245247
void *frame_ptr = find_frame_in_chunks(chunks, address);
246248
if (!frame_ptr) {
249+
PyErr_Format(PyExc_RuntimeError, "Frame at address 0x%lx not found in stack chunks", address);
247250
set_exception_cause(unwinder, PyExc_RuntimeError, "Frame not found in stack chunks");
248251
return -1;
249252
}

Modules/_remote_debugging/module.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -595,6 +595,9 @@ _remote_debugging_RemoteUnwinder_get_stack_trace_impl(RemoteUnwinderObject *self
595595
// Detect cycle: if current_tstate didn't advance, we have corrupted data
596596
if (current_tstate == prev_tstate) {
597597
Py_DECREF(interpreter_threads);
598+
PyErr_Format(PyExc_RuntimeError,
599+
"Thread list cycle detected at address 0x%lx (corrupted remote memory)",
600+
current_tstate);
598601
set_exception_cause(self, PyExc_RuntimeError,
599602
"Thread list cycle detected (corrupted remote memory)");
600603
Py_CLEAR(result);

Python/remote_debug.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1302,6 +1302,7 @@ _Py_RemoteDebug_PagedReadRemoteMemory(proc_handle_t *handle,
13021302
if (entry->data == NULL) {
13031303
entry->data = PyMem_RawMalloc(page_size);
13041304
if (entry->data == NULL) {
1305+
PyErr_NoMemory();
13051306
_set_debug_exception_cause(PyExc_MemoryError,
13061307
"Cannot allocate %zu bytes for page cache entry "
13071308
"during read from PID %d at address 0x%lx",
@@ -1311,7 +1312,7 @@ _Py_RemoteDebug_PagedReadRemoteMemory(proc_handle_t *handle,
13111312
}
13121313

13131314
if (_Py_RemoteDebug_ReadRemoteMemory(handle, page_base, page_size, entry->data) < 0) {
1314-
// Try to just copy the exact ammount as a fallback
1315+
// Try to just copy the exact amount as a fallback
13151316
PyErr_Clear();
13161317
goto fallback;
13171318
}

0 commit comments

Comments
 (0)