]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-144316: Fix missing exception in _remote_debugging with debug=False (#144442)
authorTaegyun Kim <taegyun.kim@datadoghq.com>
Thu, 26 Feb 2026 21:14:34 +0000 (22:14 +0100)
committerGitHub <noreply@github.com>
Thu, 26 Feb 2026 21:14:34 +0000 (21:14 +0000)
Misc/NEWS.d/next/Library/2026-02-03-19-57-41.gh-issue-144316.wop870.rst [new file with mode: 0644]
Modules/_remote_debugging/_remote_debugging.h
Modules/_remote_debugging/asyncio.c
Modules/_remote_debugging/code_objects.c
Modules/_remote_debugging/frames.c
Modules/_remote_debugging/module.c
Python/remote_debug.h

diff --git a/Misc/NEWS.d/next/Library/2026-02-03-19-57-41.gh-issue-144316.wop870.rst b/Misc/NEWS.d/next/Library/2026-02-03-19-57-41.gh-issue-144316.wop870.rst
new file mode 100644 (file)
index 0000000..b9d0749
--- /dev/null
@@ -0,0 +1 @@
+Fix crash in ``_remote_debugging`` that caused ``test_external_inspection`` to intermittently fail. Patch by Taegyun Kim.
index 78add74423b608dd6e776c697b7ab76af5af1bd2..7bcb2f483234ecb6d6cd1b9c8f4533c7268f7ab4 100644 (file)
@@ -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"
 
@@ -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
index fc059659511fd8d0c0668adcfe843b1841b7186a..69478634de6926201815a70b6514d802c54093b6 100644 (file)
@@ -121,6 +121,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;
index 9b7b4dc22b873b5aa4b500a58e6ad0e6ecb409f6..91f7a02005391a82715f63045e1b7f11572e478a 100644 (file)
@@ -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;
index 02c48205b85a37e1fcab343c3b7c519ce38b2032..2ace0c0f7676ae94fabc993d083d40220db43118 100644 (file)
@@ -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;
@@ -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;
     }
index 26ebed13098f0ede94c59c4e8a183d5d59487561..040bd3db3773154a4a84468cd1f8eafaaf675e3f 100644 (file)
@@ -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);
index 4ae1166e885485fec867d605beb06be632e67811..7628fb04ba5bae9dda0d4fd8d937ebe81ed3f461 100644 (file)
@@ -1302,6 +1302,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",
@@ -1311,7 +1312,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;
             }