7777 offsetof(PyInterpreterState, _gil.last_holder) + sizeof(PyThreadState*))
7878#endif
7979#define INTERP_STATE_BUFFER_SIZE MAX(INTERP_STATE_MIN_SIZE, 256)
80+ #define MAX_STACK_CHUNK_SIZE (16 * 1024 * 1024) /* 16 MB max for stack chunks */
81+ #define MAX_SET_TABLE_SIZE (1 << 20) /* 1 million entries max for set iteration */
8082
8183
8284
@@ -318,10 +320,13 @@ static int append_awaited_by(RemoteUnwinderObject *unwinder, unsigned long tid,
318320 * UTILITY FUNCTIONS AND HELPERS
319321 * ============================================================================ */
320322
321- #define set_exception_cause (unwinder , exc_type , message ) \
322- if (unwinder->debug) { \
323- _set_debug_exception_cause(exc_type, message); \
324- }
323+ #define set_exception_cause (unwinder , exc_type , message ) \
324+ do { \
325+ assert(PyErr_Occurred() && "function returned -1 without setting exception"); \
326+ if (unwinder->debug) { \
327+ _set_debug_exception_cause(exc_type, message); \
328+ } \
329+ } while (0)
325330
326331static void
327332cached_code_metadata_destroy (void * ptr )
@@ -498,8 +503,15 @@ iterate_set_entries(
498503 }
499504
500505 Py_ssize_t num_els = GET_MEMBER (Py_ssize_t , set_object , unwinder -> debug_offsets .set_object .used );
501- Py_ssize_t set_len = GET_MEMBER (Py_ssize_t , set_object , unwinder -> debug_offsets .set_object .mask ) + 1 ;
506+ Py_ssize_t mask = GET_MEMBER (Py_ssize_t , set_object , unwinder -> debug_offsets .set_object .mask );
502507 uintptr_t table_ptr = GET_MEMBER (uintptr_t , set_object , unwinder -> debug_offsets .set_object .table );
508+ if (mask < 0 || mask >= MAX_SET_TABLE_SIZE || num_els < 0 || num_els > mask + 1 ) {
509+ PyErr_SetString (PyExc_RuntimeError , "Invalid set object (corrupted remote memory)" );
510+ set_exception_cause (unwinder , PyExc_RuntimeError ,
511+ "Invalid set object (corrupted remote memory)" );
512+ return -1 ;
513+ }
514+ Py_ssize_t set_len = mask + 1 ;
503515
504516 Py_ssize_t i = 0 ;
505517 Py_ssize_t els = 0 ;
@@ -1825,7 +1837,15 @@ parse_code_object(RemoteUnwinderObject *unwinder,
18251837 tlbc_entry = get_tlbc_cache_entry (unwinder , real_address , unwinder -> tlbc_generation );
18261838 }
18271839
1828- if (tlbc_entry && tlbc_index < tlbc_entry -> tlbc_array_size ) {
1840+ if (tlbc_entry ) {
1841+ if (tlbc_index < 0 || tlbc_index >= tlbc_entry -> tlbc_array_size ) {
1842+ PyErr_Format (PyExc_RuntimeError ,
1843+ "Invalid tlbc_index %d (array size %zd, corrupted remote memory)" ,
1844+ tlbc_index , tlbc_entry -> tlbc_array_size );
1845+ set_exception_cause (unwinder , PyExc_RuntimeError ,
1846+ "Invalid tlbc_index (corrupted remote memory)" );
1847+ goto error ;
1848+ }
18291849 // Use cached TLBC data
18301850 uintptr_t * entries = (uintptr_t * )((char * )tlbc_entry -> tlbc_array + sizeof (Py_ssize_t ));
18311851 uintptr_t tlbc_bytecode_addr = entries [tlbc_index ];
@@ -1924,6 +1944,15 @@ process_single_stack_chunk(
19241944 // Check actual size and reread if necessary
19251945 size_t actual_size = GET_MEMBER (size_t , this_chunk , offsetof(_PyStackChunk , size ));
19261946 if (actual_size != current_size ) {
1947+ if (actual_size <= offsetof(_PyStackChunk , data ) || actual_size > MAX_STACK_CHUNK_SIZE ) {
1948+ PyMem_RawFree (this_chunk );
1949+ PyErr_Format (PyExc_RuntimeError ,
1950+ "Invalid stack chunk size %zu (corrupted remote memory)" , actual_size );
1951+ set_exception_cause (unwinder , PyExc_RuntimeError ,
1952+ "Invalid stack chunk size (corrupted remote memory)" );
1953+ return -1 ;
1954+ }
1955+
19271956 this_chunk = PyMem_RawRealloc (this_chunk , actual_size );
19281957 if (!this_chunk ) {
19291958 PyErr_NoMemory ();
@@ -2027,6 +2056,7 @@ parse_frame_from_chunks(
20272056) {
20282057 void * frame_ptr = find_frame_in_chunks (chunks , address );
20292058 if (!frame_ptr ) {
2059+ PyErr_Format (PyExc_RuntimeError , "Frame at address 0x%lx not found in stack chunks" , address );
20302060 set_exception_cause (unwinder , PyExc_RuntimeError , "Frame not found in stack chunks" );
20312061 return -1 ;
20322062 }
@@ -2736,6 +2766,7 @@ _remote_debugging_RemoteUnwinder_get_stack_trace_impl(RemoteUnwinderObject *self
27362766 }
27372767
27382768 while (current_tstate != 0 ) {
2769+ uintptr_t prev_tstate = current_tstate ;
27392770 PyObject * frame_info = unwind_stack_for_thread (self , & current_tstate );
27402771 if (!frame_info ) {
27412772 Py_CLEAR (result );
@@ -2751,6 +2782,16 @@ _remote_debugging_RemoteUnwinder_get_stack_trace_impl(RemoteUnwinderObject *self
27512782 }
27522783 Py_DECREF (frame_info );
27532784
2785+ if (current_tstate == prev_tstate ) {
2786+ PyErr_Format (PyExc_RuntimeError ,
2787+ "Thread list cycle detected at address 0x%lx (corrupted remote memory)" ,
2788+ current_tstate );
2789+ set_exception_cause (self , PyExc_RuntimeError ,
2790+ "Thread list cycle detected (corrupted remote memory)" );
2791+ Py_CLEAR (result );
2792+ goto exit ;
2793+ }
2794+
27542795 // We are targeting a single tstate, break here
27552796 if (self -> tstate_addr ) {
27562797 break ;
0 commit comments