Skip to content

_remote_debugging: RemoteUnwinder crashes with SIGSEGV when _Py_DebugOffsets contains out-of-range values #146266

@jackhax

Description

@jackhax

CPython version: 3.15.0a7
Component: Modules/_remote_debugging/_remote_debugging.h
CWE: CWE-125 (Out-of-bounds Read)


Description

The GET_MEMBER and GET_MEMBER_NO_TAG macros in _remote_debugging.h (lines 127-129) perform unchecked pointer arithmetic using offset values read from a target process's _Py_DebugOffsets structure:

#define GET_MEMBER(type, base_ptr, offset) \
    (*(type*)((char*)(base_ptr) + (offset)))

If the offset values in the target process's _Py_DebugOffsets are corrupted or invalid (e.g., due to memory corruption, a partially-initialized interpreter, or a misbehaving extension), RemoteUnwinder dereferences a pointer far outside the intended struct boundaries and crashes with SIGSEGV.

validate_debug_offsets() only checks the cookie (xdebugpy) and version number — it does not validate that individual offset fields are within reasonable bounds for their respective structs.

Steps to Reproduce

  1. Build CPython 3.15.0a7 from source
  2. Start a target Python process
  3. Corrupt the _Py_DebugOffsets structure in the target (e.g., set offset fields at struct+120, +136, +168, +184, +192 to 0x8000)
  4. Attach RemoteUnwinder and call get_stack_trace()
  5. The debugger process crashes with return code -11 (SIGSEGV)

A clean target with valid offsets works fine — the crash only occurs when offset fields contain out-of-range values.

Expected Behavior

RemoteUnwinder should detect that the offsets are outside valid bounds and raise a Python exception (e.g., RuntimeError) rather than crashing with a segfault. The debugger process should never crash due to invalid data in the target process.

Suggested Fix

Add bounds validation for all offset fields after reading _Py_DebugOffsets from the remote process. For example:

#define GET_MEMBER_SAFE(type, obj, offset, obj_size) \
    (assert((offset) + sizeof(type) <= (obj_size)), \
     (*(type*)((char*)(obj) + (offset))))

Or extend the existing validate_debug_offsets() function to check that each offset is within the known size of its target struct (e.g., PyThreadState, PyInterpreterState, PyCodeObject, etc.).

Impact

Any tool using RemoteUnwinder — debugpy, py-spy, IDE debuggers — will crash instead of gracefully reporting an error when it encounters a process with corrupted debug offsets.

Linked PRs

Metadata

Metadata

Assignees

No one assigned

    Labels

    extension-modulesC modules in the Modules dirpendingThe issue will be closed if no feedback is providedtype-crashA hard crash of the interpreter, possibly with a core dump

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions