Skip to content
Open
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
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix crash in ``_remote_debugging`` that caused ``test_external_inspection`` to intermittently fail. Patch by Taegyun Kim.
12 changes: 8 additions & 4 deletions Modules/_remote_debugging/_remote_debugging.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ extern "C" {
#include "internal/pycore_interpframe.h" // FRAME_OWNED_BY_INTERPRETER
#include "internal/pycore_llist.h" // struct llist_node
#include "internal/pycore_long.h" // _PyLong_GetZero
#include "internal/pycore_pyerrors.h" // _PyErr_FormatFromCause
#include "internal/pycore_stackref.h" // Py_TAG_BITS
#include "../../Python/remote_debug.h"

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

/* Exception cause macro */
#define set_exception_cause(unwinder, exc_type, message) \
if (unwinder->debug) { \
_set_debug_exception_cause(exc_type, message); \
}
#define set_exception_cause(unwinder, exc_type, message) \
do { \
assert(PyErr_Occurred() && "function returned -1 without setting exception"); \
if (unwinder->debug) { \
_set_debug_exception_cause(exc_type, message); \
} \
} while (0)

/* ============================================================================
* TYPE DEFINITIONS
Expand Down
1 change: 1 addition & 0 deletions Modules/_remote_debugging/asyncio.c
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ iterate_set_entries(

// Validate mask and num_els to prevent huge loop iterations from garbage data
if (mask < 0 || mask >= MAX_SET_TABLE_SIZE || num_els < 0 || num_els > mask + 1) {
PyErr_SetString(PyExc_RuntimeError, "Invalid set object (corrupted remote memory)");
set_exception_cause(unwinder, PyExc_RuntimeError,
"Invalid set object (corrupted remote memory)");
return -1;
Expand Down
3 changes: 3 additions & 0 deletions Modules/_remote_debugging/code_objects.c
Original file line number Diff line number Diff line change
Expand Up @@ -446,6 +446,9 @@ parse_code_object(RemoteUnwinderObject *unwinder,
if (tlbc_entry) {
// Validate index bounds (also catches negative values since tlbc_index is signed)
if (ctx->tlbc_index < 0 || ctx->tlbc_index >= tlbc_entry->tlbc_array_size) {
PyErr_Format(PyExc_RuntimeError,
"Invalid tlbc_index %d (array size %zd, corrupted remote memory)",
ctx->tlbc_index, tlbc_entry->tlbc_array_size);
set_exception_cause(unwinder, PyExc_RuntimeError,
"Invalid tlbc_index (corrupted remote memory)");
goto error;
Expand Down
3 changes: 3 additions & 0 deletions Modules/_remote_debugging/frames.c
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ process_single_stack_chunk(
// Size must be at least enough for the header and reasonably bounded
if (actual_size <= offsetof(_PyStackChunk, data) || actual_size > MAX_STACK_CHUNK_SIZE) {
PyMem_RawFree(this_chunk);
PyErr_Format(PyExc_RuntimeError,
"Invalid stack chunk size %zu (corrupted remote memory)", actual_size);
set_exception_cause(unwinder, PyExc_RuntimeError,
"Invalid stack chunk size (corrupted remote memory)");
return -1;
Expand Down Expand Up @@ -244,6 +246,7 @@ parse_frame_from_chunks(
) {
void *frame_ptr = find_frame_in_chunks(chunks, address);
if (!frame_ptr) {
PyErr_Format(PyExc_RuntimeError, "Frame at address 0x%lx not found in stack chunks", address);
set_exception_cause(unwinder, PyExc_RuntimeError, "Frame not found in stack chunks");
return -1;
}
Expand Down
3 changes: 3 additions & 0 deletions Modules/_remote_debugging/module.c
Original file line number Diff line number Diff line change
Expand Up @@ -595,6 +595,9 @@ _remote_debugging_RemoteUnwinder_get_stack_trace_impl(RemoteUnwinderObject *self
// Detect cycle: if current_tstate didn't advance, we have corrupted data
if (current_tstate == prev_tstate) {
Py_DECREF(interpreter_threads);
PyErr_Format(PyExc_RuntimeError,
"Thread list cycle detected at address 0x%lx (corrupted remote memory)",
current_tstate);
set_exception_cause(self, PyExc_RuntimeError,
"Thread list cycle detected (corrupted remote memory)");
Py_CLEAR(result);
Expand Down
3 changes: 2 additions & 1 deletion Python/remote_debug.h
Original file line number Diff line number Diff line change
Expand Up @@ -1263,6 +1263,7 @@ _Py_RemoteDebug_PagedReadRemoteMemory(proc_handle_t *handle,
if (entry->data == NULL) {
entry->data = PyMem_RawMalloc(page_size);
if (entry->data == NULL) {
PyErr_NoMemory();
_set_debug_exception_cause(PyExc_MemoryError,
"Cannot allocate %zu bytes for page cache entry "
"during read from PID %d at address 0x%lx",
Expand All @@ -1272,7 +1273,7 @@ _Py_RemoteDebug_PagedReadRemoteMemory(proc_handle_t *handle,
}

if (_Py_RemoteDebug_ReadRemoteMemory(handle, page_base, page_size, entry->data) < 0) {
// Try to just copy the exact ammount as a fallback
// Try to just copy the exact amount as a fallback
PyErr_Clear();
goto fallback;
}
Expand Down
Loading