Phase E1-CORRECT: Fix USER/BASE pointer conversion bugs in slab_index_for calls
CRITICAL BUG FIX: Phase E1 introduced 1-byte headers for ALL size classes (C0-C7), changing the pointer contract. However, many locations still called slab_index_for() with USER pointers (storage+1) instead of BASE pointers (storage), causing off-by-one slab index calculations that corrupted memory. Root Cause: - USER pointer = BASE + 1 (returned by malloc, points past header) - BASE pointer = storage start (where 1-byte header is written) - slab_index_for() expects BASE pointer for correct slab boundary calculations - Passing USER pointer → wrong slab_idx → wrong metadata → freelist corruption Impact Before Fix: - bench_random_mixed crashes at ~14K iterations with SEGV - Massive C7 alignment check failures (wrong slab classification) - Memory corruption from writing to wrong slab freelists Fixes Applied (8 locations): 1. core/hakmem_tiny_free.inc:137 - Added USER→BASE conversion before slab_index_for() 2. core/hakmem_tiny_ultra_simple.inc:148 - Added USER→BASE conversion before slab_index_for() 3. core/tiny_free_fast.inc.h:220 - Added USER→BASE conversion before slab_index_for() 4-5. core/tiny_free_magazine.inc.h:126,315 - Added USER→BASE conversion before slab_index_for() (2 locations) 6. core/box/free_local_box.c:14,22,62 - Added USER→BASE conversion before slab_index_for() - Fixed delta calculation to use BASE instead of USER - Fixed debug logging to use BASE instead of USER 7. core/hakmem_tiny.c:448,460,473 (tiny_debug_track_alloc_ret) - Added USER→BASE conversion before slab_index_for() (2 calls) - Fixed delta calculation to use BASE instead of USER - This function is called on EVERY allocation in debug builds Results After Fix: ✅ bench_random_mixed stable up to 66K iterations (~4.7x improvement) ✅ C7 alignment check failures eliminated (was: 100% failure rate) ✅ Front Gate "Unknown" classification dropped to 0% (was: 1.67%) ✅ No segfaults for workloads up to ~33K allocations Remaining Issue: ❌ Segfault still occurs at iteration 66152 (allocs=33137, frees=33014) - Different bug from USER/BASE conversion issues - Likely capacity/boundary condition (further investigation needed) Testing: - bench_random_mixed_hakmem 1K-66K iterations: PASS - bench_random_mixed_hakmem 67K+ iterations: FAIL (different bug) - bench_fixed_size_hakmem 200K iterations: PASS 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@ -1,6 +1,7 @@
|
||||
#include "free_local_box.h"
|
||||
#include "free_publish_box.h"
|
||||
#include "hakmem_tiny.h"
|
||||
#include "tiny_next_ptr_box.h" // Phase E1-CORRECT: Box API
|
||||
|
||||
void tiny_free_local_box(SuperSlab* ss, int slab_idx, TinySlabMeta* meta, void* ptr, uint32_t my_tid) {
|
||||
extern _Atomic uint64_t g_free_local_box_calls;
|
||||
@ -9,14 +10,17 @@ void tiny_free_local_box(SuperSlab* ss, int slab_idx, TinySlabMeta* meta, void*
|
||||
if (slab_idx < 0 || slab_idx >= ss_slabs_capacity(ss)) return;
|
||||
(void)my_tid;
|
||||
|
||||
// ✅ Phase E1-CORRECT: ALL classes have headers, calculate BASE pointer once
|
||||
void* base = (void*)((uint8_t*)ptr - 1);
|
||||
|
||||
if (__builtin_expect(tiny_refill_failfast_level() >= 2, 0)) {
|
||||
int actual_idx = slab_index_for(ss, ptr);
|
||||
int actual_idx = slab_index_for(ss, base);
|
||||
if (actual_idx != slab_idx) {
|
||||
tiny_failfast_abort_ptr("free_local_box_idx", ss, slab_idx, ptr, "slab_idx_mismatch");
|
||||
} else {
|
||||
size_t blk = g_tiny_class_sizes[ss->size_class];
|
||||
uint8_t* slab_base = tiny_slab_base_for(ss, slab_idx);
|
||||
uintptr_t delta = (uintptr_t)ptr - (uintptr_t)slab_base;
|
||||
uintptr_t delta = (uintptr_t)base - (uintptr_t)slab_base;
|
||||
if (blk == 0 || (delta % blk) != 0) {
|
||||
tiny_failfast_abort_ptr("free_local_box_align", ss, slab_idx, ptr, "misaligned");
|
||||
} else if (meta && delta / blk >= meta->capacity) {
|
||||
@ -56,16 +60,16 @@ void tiny_free_local_box(SuperSlab* ss, int slab_idx, TinySlabMeta* meta, void*
|
||||
|
||||
fprintf(stderr, "[FREE_VERIFY] cls=%u slab=%d ptr=%p prev=%p (offset_ptr=%zu offset_prev=%zu)\n",
|
||||
ss->size_class, slab_idx, ptr, prev,
|
||||
(size_t)((uintptr_t)ptr - (uintptr_t)slab_base),
|
||||
(size_t)((uintptr_t)base - (uintptr_t)slab_base),
|
||||
prev ? (size_t)((uintptr_t)prev - (uintptr_t)slab_base) : 0);
|
||||
}
|
||||
|
||||
*(void**)ptr = prev;
|
||||
tiny_next_write(ss->size_class, ptr, prev); // Phase E1-CORRECT: Box API
|
||||
meta->freelist = ptr;
|
||||
|
||||
// FREELIST CORRUPTION DEBUG: Verify write succeeded
|
||||
if (__builtin_expect(tiny_refill_failfast_level() >= 2, 0)) {
|
||||
void* readback = *(void**)ptr;
|
||||
void* readback = tiny_next_read(ss->size_class, ptr); // Phase E1-CORRECT: Box API
|
||||
if (readback != prev) {
|
||||
fprintf(stderr, "[FREE_CORRUPT] Wrote prev=%p to ptr=%p but read back %p!\n",
|
||||
prev, ptr, readback);
|
||||
|
||||
Reference in New Issue
Block a user