From e09442089eb86d88d4b5a96e56f713cb31173ae9 Mon Sep 17 00:00:00 2001 From: Sam Gross Date: Wed, 12 Feb 2025 18:09:15 -0500 Subject: [PATCH] gh-130030: Fix crash on 32-bit Linux with free threading (gh-130043) The `gc_get_refs` assertion needs to be after we check the alive and unreachable bits. Otherwise, `ob_tid` may store the actual thread id instead of the computed `gc_refs`, which may trigger the assertion if the `ob_tid` looks like a negative value. Also fix a few type warnings on 32-bit systems. --- Objects/obmalloc.c | 12 ++++++------ Python/gc_free_threading.c | 12 +++++++----- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/Objects/obmalloc.c b/Objects/obmalloc.c index 5688049b024..6341251007a 100644 --- a/Objects/obmalloc.c +++ b/Objects/obmalloc.c @@ -3297,12 +3297,12 @@ static bool _collect_alloc_stats( static void py_mimalloc_print_stats(FILE *out) { - fprintf(out, "Small block threshold = %zd, in %u size classes.\n", - MI_SMALL_OBJ_SIZE_MAX, MI_BIN_HUGE); - fprintf(out, "Medium block threshold = %zd\n", - MI_MEDIUM_OBJ_SIZE_MAX); - fprintf(out, "Large object max size = %zd\n", - MI_LARGE_OBJ_SIZE_MAX); + fprintf(out, "Small block threshold = %zu, in %u size classes.\n", + (size_t)MI_SMALL_OBJ_SIZE_MAX, MI_BIN_HUGE); + fprintf(out, "Medium block threshold = %zu\n", + (size_t)MI_MEDIUM_OBJ_SIZE_MAX); + fprintf(out, "Large object max size = %zu\n", + (size_t)MI_LARGE_OBJ_SIZE_MAX); mi_heap_t *heap = mi_heap_get_default(); struct _alloc_stats stats; diff --git a/Python/gc_free_threading.c b/Python/gc_free_threading.c index 9e459da3a44..0d6fddb5705 100644 --- a/Python/gc_free_threading.c +++ b/Python/gc_free_threading.c @@ -581,11 +581,13 @@ gc_mark_buffer_len(gc_mark_args_t *args) } // Returns number of free entry slots in buffer +#ifndef NDEBUG static inline unsigned int gc_mark_buffer_avail(gc_mark_args_t *args) { return BUFFER_SIZE - gc_mark_buffer_len(args); } +#endif static inline bool gc_mark_buffer_is_empty(gc_mark_args_t *args) @@ -1074,14 +1076,14 @@ mark_heap_visitor(const mi_heap_t *heap, const mi_heap_area_t *area, return true; } - _PyObject_ASSERT_WITH_MSG(op, gc_get_refs(op) >= 0, - "refcount is too small"); - if (gc_is_alive(op) || !gc_is_unreachable(op)) { // Object was already marked as reachable. return true; } + _PyObject_ASSERT_WITH_MSG(op, gc_get_refs(op) >= 0, + "refcount is too small"); + // GH-129236: If we've seen an active frame without a valid stack pointer, // then we can't collect objects with deferred references because we may // have missed some reference to the object on the stack. In that case, @@ -1178,10 +1180,10 @@ move_legacy_finalizer_reachable(struct collection_state *state); static void gc_prime_from_spans(gc_mark_args_t *args) { - Py_ssize_t space = BUFFER_HI - gc_mark_buffer_len(args); + unsigned int space = BUFFER_HI - gc_mark_buffer_len(args); // there should always be at least this amount of space assert(space <= gc_mark_buffer_avail(args)); - assert(space > 0); + assert(space <= BUFFER_HI); gc_span_t entry = args->spans.stack[--args->spans.size]; // spans on the stack should always have one or more elements assert(entry.start < entry.end);