@@ -240,6 +240,128 @@ validate_debug_offsets(struct _Py_DebugOffsets *debug_offsets)
240240 return -1 ;
241241 }
242242
243+ // Validate that all offset fields are within their declared struct sizes.
244+ // Each sub-struct has a .size field representing the target struct's total
245+ // size; every other field is an offset that must be strictly less than size.
246+ // Without this check, corrupted offsets cause out-of-bounds reads (SIGSEGV).
247+
248+ #define _CHECK_OFFSET (section , field ) \
249+ do { \
250+ if (debug_offsets->section.field >= debug_offsets->section.size) { \
251+ PyErr_Format(PyExc_RuntimeError, \
252+ "debug_offsets." #section "." #field " (%" PRIu64 ") " \
253+ "exceeds " #section ".size (%" PRIu64 ")", \
254+ debug_offsets->section.field, \
255+ debug_offsets->section.size); \
256+ return -1; \
257+ } \
258+ } while (0)
259+
260+ // runtime_state
261+ _CHECK_OFFSET (runtime_state , finalizing );
262+ _CHECK_OFFSET (runtime_state , interpreters_head );
263+
264+ // interpreter_state
265+ _CHECK_OFFSET (interpreter_state , id );
266+ _CHECK_OFFSET (interpreter_state , next );
267+ _CHECK_OFFSET (interpreter_state , threads_head );
268+ _CHECK_OFFSET (interpreter_state , threads_main );
269+ _CHECK_OFFSET (interpreter_state , gc );
270+ _CHECK_OFFSET (interpreter_state , imports_modules );
271+ _CHECK_OFFSET (interpreter_state , sysdict );
272+ _CHECK_OFFSET (interpreter_state , builtins );
273+ _CHECK_OFFSET (interpreter_state , ceval_gil );
274+ _CHECK_OFFSET (interpreter_state , gil_runtime_state );
275+ _CHECK_OFFSET (interpreter_state , gil_runtime_state_locked );
276+ _CHECK_OFFSET (interpreter_state , gil_runtime_state_holder );
277+ _CHECK_OFFSET (interpreter_state , code_object_generation );
278+
279+ // thread_state
280+ _CHECK_OFFSET (thread_state , prev );
281+ _CHECK_OFFSET (thread_state , next );
282+ _CHECK_OFFSET (thread_state , interp );
283+ _CHECK_OFFSET (thread_state , current_frame );
284+ _CHECK_OFFSET (thread_state , base_frame );
285+ _CHECK_OFFSET (thread_state , last_profiled_frame );
286+ _CHECK_OFFSET (thread_state , thread_id );
287+ _CHECK_OFFSET (thread_state , native_thread_id );
288+ _CHECK_OFFSET (thread_state , datastack_chunk );
289+ _CHECK_OFFSET (thread_state , status );
290+ _CHECK_OFFSET (thread_state , holds_gil );
291+ _CHECK_OFFSET (thread_state , gil_requested );
292+ _CHECK_OFFSET (thread_state , current_exception );
293+ _CHECK_OFFSET (thread_state , exc_state );
294+
295+ // interpreter_frame
296+ _CHECK_OFFSET (interpreter_frame , previous );
297+ _CHECK_OFFSET (interpreter_frame , executable );
298+ _CHECK_OFFSET (interpreter_frame , instr_ptr );
299+ _CHECK_OFFSET (interpreter_frame , localsplus );
300+ _CHECK_OFFSET (interpreter_frame , owner );
301+ _CHECK_OFFSET (interpreter_frame , stackpointer );
302+
303+ // code_object
304+ _CHECK_OFFSET (code_object , filename );
305+ _CHECK_OFFSET (code_object , name );
306+ _CHECK_OFFSET (code_object , qualname );
307+ _CHECK_OFFSET (code_object , linetable );
308+ _CHECK_OFFSET (code_object , firstlineno );
309+ _CHECK_OFFSET (code_object , argcount );
310+ _CHECK_OFFSET (code_object , localsplusnames );
311+ _CHECK_OFFSET (code_object , localspluskinds );
312+ _CHECK_OFFSET (code_object , co_code_adaptive );
313+
314+ // pyobject
315+ _CHECK_OFFSET (pyobject , ob_type );
316+
317+ // type_object
318+ _CHECK_OFFSET (type_object , tp_name );
319+ _CHECK_OFFSET (type_object , tp_repr );
320+ _CHECK_OFFSET (type_object , tp_flags );
321+
322+ // tuple_object
323+ _CHECK_OFFSET (tuple_object , ob_item );
324+ _CHECK_OFFSET (tuple_object , ob_size );
325+
326+ // list_object
327+ _CHECK_OFFSET (list_object , ob_item );
328+ _CHECK_OFFSET (list_object , ob_size );
329+
330+ // set_object
331+ _CHECK_OFFSET (set_object , used );
332+ _CHECK_OFFSET (set_object , table );
333+ _CHECK_OFFSET (set_object , mask );
334+
335+ // dict_object
336+ _CHECK_OFFSET (dict_object , ma_keys );
337+ _CHECK_OFFSET (dict_object , ma_values );
338+
339+ // float_object
340+ _CHECK_OFFSET (float_object , ob_fval );
341+
342+ // long_object
343+ _CHECK_OFFSET (long_object , lv_tag );
344+ _CHECK_OFFSET (long_object , ob_digit );
345+
346+ // bytes_object
347+ _CHECK_OFFSET (bytes_object , ob_size );
348+ _CHECK_OFFSET (bytes_object , ob_sval );
349+
350+ // unicode_object
351+ _CHECK_OFFSET (unicode_object , state );
352+ _CHECK_OFFSET (unicode_object , length );
353+
354+ // gc
355+ _CHECK_OFFSET (gc , collecting );
356+ _CHECK_OFFSET (gc , frame );
357+
358+ // gen_object
359+ _CHECK_OFFSET (gen_object , gi_name );
360+ _CHECK_OFFSET (gen_object , gi_iframe );
361+ _CHECK_OFFSET (gen_object , gi_frame_state );
362+
363+ #undef _CHECK_OFFSET
364+
243365 return 0 ;
244366}
245367
0 commit comments