@@ -516,6 +516,17 @@ parse_task(
516516// Forward declaration for mutual recursion
517517static int process_waiter_task (RemoteUnwinderObject * unwinder , uintptr_t key_addr , void * context );
518518
519+ // Carries the recursion depth so a cyclic or corrupted remote awaited_by graph
520+ // cannot drive unbounded C recursion and overflow the debugger's stack.
521+ typedef struct {
522+ PyObject * result ;
523+ int depth ;
524+ } waiter_context_t ;
525+
526+ static int process_task_and_waiters_impl (
527+ RemoteUnwinderObject * unwinder , uintptr_t task_addr , PyObject * result ,
528+ int depth );
529+
519530// Processor function for parsing tasks in sets
520531static int
521532process_task_parser (
@@ -658,19 +669,30 @@ process_single_task_node(
658669 return -1 ;
659670}
660671
661- int
662- process_task_and_waiters (
672+ static int
673+ process_task_and_waiters_impl (
663674 RemoteUnwinderObject * unwinder ,
664675 uintptr_t task_addr ,
665- PyObject * result
676+ PyObject * result ,
677+ int depth
666678) {
667679 // First, add this task to the result
668680 if (process_single_task_node (unwinder , task_addr , NULL , result ) < 0 ) {
669681 return -1 ;
670682 }
671683
672684 // Now find all tasks that are waiting for this task and process them
673- return process_task_awaited_by (unwinder , task_addr , process_waiter_task , result );
685+ waiter_context_t ctx = {result , depth };
686+ return process_task_awaited_by (unwinder , task_addr , process_waiter_task , & ctx );
687+ }
688+
689+ int
690+ process_task_and_waiters (
691+ RemoteUnwinderObject * unwinder ,
692+ uintptr_t task_addr ,
693+ PyObject * result
694+ ) {
695+ return process_task_and_waiters_impl (unwinder , task_addr , result , 0 );
674696}
675697
676698// Processor function for task waiters
@@ -680,8 +702,17 @@ process_waiter_task(
680702 uintptr_t key_addr ,
681703 void * context
682704) {
683- PyObject * result = (PyObject * )context ;
684- return process_task_and_waiters (unwinder , key_addr , result );
705+ waiter_context_t * ctx = (waiter_context_t * )context ;
706+ if (ctx -> depth >= MAX_TASK_AWAITED_BY_DEPTH ) {
707+ PyErr_SetString (PyExc_RuntimeError ,
708+ "Task awaited_by chain is too deep or cyclic "
709+ "(corrupted remote memory)" );
710+ set_exception_cause (unwinder , PyExc_RuntimeError ,
711+ "Task awaited_by recursion limit exceeded" );
712+ return -1 ;
713+ }
714+ return process_task_and_waiters_impl (unwinder , key_addr , ctx -> result ,
715+ ctx -> depth + 1 );
685716}
686717
687718/* ============================================================================
@@ -978,7 +1009,8 @@ process_running_task_chain(
9781009 }
9791010
9801011 // Now find all tasks that are waiting for this task and process them
981- if (process_task_awaited_by (unwinder , running_task_addr , process_waiter_task , result ) < 0 ) {
1012+ waiter_context_t ctx = {result , 0 };
1013+ if (process_task_awaited_by (unwinder , running_task_addr , process_waiter_task , & ctx ) < 0 ) {
9821014 return -1 ;
9831015 }
9841016
0 commit comments