Thanks to visit codestin.com
Credit goes to github.com

Skip to content

[3.14] gh-135371: Clean tags from pointers in all cases in remote debugging module (GH-135534) #135545

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 15, 2025
Merged
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
106 changes: 60 additions & 46 deletions Modules/_remote_debugging_module.c
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@
* ============================================================================ */

#define GET_MEMBER(type, obj, offset) (*(type*)((char*)(obj) + (offset)))
#define CLEAR_PTR_TAG(ptr) (((uintptr_t)(ptr) & ~Py_TAG_BITS))
#define GET_MEMBER_NO_TAG(type, obj, offset) (type)(CLEAR_PTR_TAG(*(type*)((char*)(obj) + (offset))))

/* Size macros for opaque buffers */
#define SIZEOF_BYTES_OBJ sizeof(PyBytesObject)
Expand Down Expand Up @@ -243,6 +245,13 @@ module _remote_debugging
* FORWARD DECLARATIONS
* ============================================================================ */

static inline int
is_frame_valid(
RemoteUnwinderObject *unwinder,
uintptr_t frame_addr,
uintptr_t code_object_addr
);

static int
parse_tasks_in_set(
RemoteUnwinderObject *unwinder,
Expand Down Expand Up @@ -734,8 +743,7 @@ parse_task_name(
return NULL;
}

uintptr_t task_name_addr = GET_MEMBER(uintptr_t, task_obj, unwinder->async_debug_offsets.asyncio_task_object.task_name);
task_name_addr &= ~Py_TAG_BITS;
uintptr_t task_name_addr = GET_MEMBER_NO_TAG(uintptr_t, task_obj, unwinder->async_debug_offsets.asyncio_task_object.task_name);

// The task name can be a long or a string so we need to check the type
char task_name_obj[SIZEOF_PYOBJECT];
Expand Down Expand Up @@ -798,8 +806,7 @@ static int parse_task_awaited_by(
return -1;
}

uintptr_t task_ab_addr = GET_MEMBER(uintptr_t, task_obj, unwinder->async_debug_offsets.asyncio_task_object.task_awaited_by);
task_ab_addr &= ~Py_TAG_BITS;
uintptr_t task_ab_addr = GET_MEMBER_NO_TAG(uintptr_t, task_obj, unwinder->async_debug_offsets.asyncio_task_object.task_awaited_by);

if ((void*)task_ab_addr == NULL) {
return 0;
Expand Down Expand Up @@ -849,8 +856,7 @@ handle_yield_from_frame(
return -1;
}

uintptr_t stackpointer_addr = GET_MEMBER(uintptr_t, iframe, unwinder->debug_offsets.interpreter_frame.stackpointer);
stackpointer_addr &= ~Py_TAG_BITS;
uintptr_t stackpointer_addr = GET_MEMBER_NO_TAG(uintptr_t, iframe, unwinder->debug_offsets.interpreter_frame.stackpointer);

if ((void*)stackpointer_addr != NULL) {
uintptr_t gi_await_addr;
Expand Down Expand Up @@ -917,6 +923,11 @@ parse_coro_chain(
return -1;
}

int8_t frame_state = GET_MEMBER(int8_t, gen_object, unwinder->debug_offsets.gen_object.gi_frame_state);
if (frame_state == FRAME_CLEARED) {
return 0;
}

uintptr_t gen_type_addr = GET_MEMBER(uintptr_t, gen_object, unwinder->debug_offsets.pyobject.ob_type);

PyObject* name = NULL;
Expand All @@ -936,7 +947,7 @@ parse_coro_chain(
}
Py_DECREF(name);

if (GET_MEMBER(int8_t, gen_object, unwinder->debug_offsets.gen_object.gi_frame_state) == FRAME_SUSPENDED_YIELD_FROM) {
if (frame_state == FRAME_SUSPENDED_YIELD_FROM) {
return handle_yield_from_frame(unwinder, gi_iframe_addr, gen_type_addr, render_to);
}

Expand Down Expand Up @@ -981,8 +992,7 @@ create_task_result(
goto error;
}

coro_addr = GET_MEMBER(uintptr_t, task_obj, unwinder->async_debug_offsets.asyncio_task_object.task_coro);
coro_addr &= ~Py_TAG_BITS;
coro_addr = GET_MEMBER_NO_TAG(uintptr_t, task_obj, unwinder->async_debug_offsets.asyncio_task_object.task_coro);

if ((void*)coro_addr != NULL) {
if (parse_coro_chain(unwinder, coro_addr, call_stack) < 0) {
Expand Down Expand Up @@ -1816,10 +1826,10 @@ parse_frame_from_chunks(

char *frame = (char *)frame_ptr;
*previous_frame = GET_MEMBER(uintptr_t, frame, unwinder->debug_offsets.interpreter_frame.previous);

if (GET_MEMBER(char, frame, unwinder->debug_offsets.interpreter_frame.owner) >= FRAME_OWNED_BY_INTERPRETER ||
!GET_MEMBER(uintptr_t, frame, unwinder->debug_offsets.interpreter_frame.executable)) {
return 0;
uintptr_t code_object = GET_MEMBER_NO_TAG(uintptr_t, frame_ptr, unwinder->debug_offsets.interpreter_frame.executable);
int frame_valid = is_frame_valid(unwinder, (uintptr_t)frame, code_object);
if (frame_valid != 1) {
return frame_valid;
}

uintptr_t instruction_pointer = GET_MEMBER(uintptr_t, frame, unwinder->debug_offsets.interpreter_frame.instr_ptr);
Expand All @@ -1832,9 +1842,7 @@ parse_frame_from_chunks(
}
#endif

return parse_code_object(
unwinder, result, GET_MEMBER(uintptr_t, frame, unwinder->debug_offsets.interpreter_frame.executable),
instruction_pointer, previous_frame, tlbc_index);
return parse_code_object(unwinder, result, code_object, instruction_pointer, previous_frame, tlbc_index);
}

/* ============================================================================
Expand Down Expand Up @@ -2077,6 +2085,33 @@ find_running_task_and_coro(
* FRAME PARSING FUNCTIONS
* ============================================================================ */

static inline int
is_frame_valid(
RemoteUnwinderObject *unwinder,
uintptr_t frame_addr,
uintptr_t code_object_addr
) {
if ((void*)code_object_addr == NULL) {
return 0;
}

void* frame = (void*)frame_addr;

if (GET_MEMBER(char, frame, unwinder->debug_offsets.interpreter_frame.owner) == FRAME_OWNED_BY_CSTACK ||
GET_MEMBER(char, frame, unwinder->debug_offsets.interpreter_frame.owner) == FRAME_OWNED_BY_INTERPRETER) {
return 0; // C frame
}

if (GET_MEMBER(char, frame, unwinder->debug_offsets.interpreter_frame.owner) != FRAME_OWNED_BY_GENERATOR
&& GET_MEMBER(char, frame, unwinder->debug_offsets.interpreter_frame.owner) != FRAME_OWNED_BY_THREAD) {
PyErr_Format(PyExc_RuntimeError, "Unhandled frame owner %d.\n",
GET_MEMBER(char, frame, unwinder->debug_offsets.interpreter_frame.owner));
set_exception_cause(unwinder, PyExc_RuntimeError, "Unhandled frame owner type in async frame");
return -1;
}
return 1;
}

static int
parse_frame_object(
RemoteUnwinderObject *unwinder,
Expand All @@ -2098,13 +2133,10 @@ parse_frame_object(
}

*previous_frame = GET_MEMBER(uintptr_t, frame, unwinder->debug_offsets.interpreter_frame.previous);

if (GET_MEMBER(char, frame, unwinder->debug_offsets.interpreter_frame.owner) >= FRAME_OWNED_BY_INTERPRETER) {
return 0;
}

if ((void*)GET_MEMBER(uintptr_t, frame, unwinder->debug_offsets.interpreter_frame.executable) == NULL) {
return 0;
uintptr_t code_object = GET_MEMBER_NO_TAG(uintptr_t, frame, unwinder->debug_offsets.interpreter_frame.executable);
int frame_valid = is_frame_valid(unwinder, (uintptr_t)frame, code_object);
if (frame_valid != 1) {
return frame_valid;
}

uintptr_t instruction_pointer = GET_MEMBER(uintptr_t, frame, unwinder->debug_offsets.interpreter_frame.instr_ptr);
Expand All @@ -2117,9 +2149,7 @@ parse_frame_object(
}
#endif

return parse_code_object(
unwinder, result, GET_MEMBER(uintptr_t, frame, unwinder->debug_offsets.interpreter_frame.executable),
instruction_pointer, previous_frame, tlbc_index);
return parse_code_object(unwinder, result, code_object,instruction_pointer, previous_frame, tlbc_index);
}

static int
Expand All @@ -2144,26 +2174,10 @@ parse_async_frame_object(
}

*previous_frame = GET_MEMBER(uintptr_t, frame, unwinder->debug_offsets.interpreter_frame.previous);

*code_object = GET_MEMBER(uintptr_t, frame, unwinder->debug_offsets.interpreter_frame.executable);
// Strip tag bits for consistent comparison
*code_object &= ~Py_TAG_BITS;
assert(code_object != NULL);
if ((void*)*code_object == NULL) {
return 0;
}

if (GET_MEMBER(char, frame, unwinder->debug_offsets.interpreter_frame.owner) == FRAME_OWNED_BY_CSTACK ||
GET_MEMBER(char, frame, unwinder->debug_offsets.interpreter_frame.owner) == FRAME_OWNED_BY_INTERPRETER) {
return 0; // C frame
}

if (GET_MEMBER(char, frame, unwinder->debug_offsets.interpreter_frame.owner) != FRAME_OWNED_BY_GENERATOR
&& GET_MEMBER(char, frame, unwinder->debug_offsets.interpreter_frame.owner) != FRAME_OWNED_BY_THREAD) {
PyErr_Format(PyExc_RuntimeError, "Unhandled frame owner %d.\n",
GET_MEMBER(char, frame, unwinder->debug_offsets.interpreter_frame.owner));
set_exception_cause(unwinder, PyExc_RuntimeError, "Unhandled frame owner type in async frame");
return -1;
*code_object = GET_MEMBER_NO_TAG(uintptr_t, frame, unwinder->debug_offsets.interpreter_frame.executable);
int frame_valid = is_frame_valid(unwinder, (uintptr_t)frame, *code_object);
if (frame_valid != 1) {
return frame_valid;
}

uintptr_t instruction_pointer = GET_MEMBER(uintptr_t, frame, unwinder->debug_offsets.interpreter_frame.instr_ptr);
Expand Down
Loading