Larson crash investigation: Add freelist header write + abort() on duplicate

## Changes

1. **TLS SLL duplicate detection** (core/box/tls_sll_box.h:381)
   - Changed 'return true' to 'abort()' to get backtrace on double-free
   - Enables precise root cause identification

2. **Freelist header write fix** (core/tiny_superslab_alloc.inc.h:159-169)
   - Added tiny_region_id_write_header() call in freelist allocation path
   - Previously only linear carve wrote headers → stale headers on reuse
   - Now both paths write headers consistently

## Root Cause Analysis

Backtrace revealed true double-free pattern:
- last_push_from=hak_tiny_free_fast_v2 (freed once)
- last_pop_from=(null) (never allocated)
- where=hak_tiny_free_fast_v2 (freed again!)

Same pointer freed twice WITHOUT reallocation in between.

## Status

- Freelist header fix:  Implemented (necessary but not sufficient)
- Double-free still occurs:  Deeper investigation needed
- Possible causes: User code bug, TLS drain race, remote free issue

Next: Investigate allocation/free flow with enhanced tracing
This commit is contained in:
Moe Charm (CI)
2025-11-27 05:57:22 +09:00
parent 12c36afe46
commit e4868bf236
2 changed files with 14 additions and 3 deletions

View File

@ -377,8 +377,8 @@ static inline bool tls_sll_push_impl(int class_idx, void* ptr, uint32_t capacity
g_tls_sll_last_writer[class_idx] ? g_tls_sll_last_writer[class_idx] : "(null)", g_tls_sll_last_writer[class_idx] ? g_tls_sll_last_writer[class_idx] : "(null)",
where ? where : "(null)"); where ? where : "(null)");
ptr_trace_dump_now("tls_sll_dup"); ptr_trace_dump_now("tls_sll_dup");
// Treat as already free; do not push again. // ABORT to get backtrace showing exact double-free location
return true; abort();
} }
void* next; void* next;
PTR_NEXT_READ("tls_sll_scan", class_idx, scan, 0, next); PTR_NEXT_READ("tls_sll_scan", class_idx, scan, 0, next);

View File

@ -155,7 +155,18 @@ static inline void* superslab_alloc_from_slab(SuperSlab* ss, int slab_idx) {
tiny_remote_track_on_alloc(ss, slab_idx, block, "freelist_alloc", 0); tiny_remote_track_on_alloc(ss, slab_idx, block, "freelist_alloc", 0);
tiny_remote_assert_not_remote(ss, slab_idx, block, "freelist_alloc_ret", 0); tiny_remote_assert_not_remote(ss, slab_idx, block, "freelist_alloc_ret", 0);
} }
return block;
// CRITICAL FIX (Larson double-free): Write header for freelist allocations
// Problem: Freelist path was returning BASE without writing header
// Result: Stale headers from previous allocations → double-free on next free
// Solution: Always write header before returning (same as linear carve path)
void* user =
#if HAKMEM_TINY_HEADER_CLASSIDX
tiny_region_id_write_header(block, meta->class_idx);
#else
block;
#endif
return user;
} }
return NULL; return NULL;