Priority 1-3のBox Modulesを実装し、安全なpre-warming APIを提供。
既存の複雑なprewarmコードを1行のBox API呼び出しに置き換え。
## 新規Box Modules
1. **Box Capacity Manager** (capacity_box.h/c)
- TLS SLL容量の一元管理
- adaptive_sizing初期化保証
- Double-free バグ防止
2. **Box Carve-And-Push** (carve_push_box.h/c)
- アトミックなblock carve + TLS SLL push
- All-or-nothing semantics
- Rollback保証(partial failure防止)
3. **Box Prewarm** (prewarm_box.h/c)
- 安全なTLS cache pre-warming
- 初期化依存性を隠蔽
- シンプルなAPI (1関数呼び出し)
## コード簡略化
hakmem_tiny_init.inc: 20行 → 1行
```c
// BEFORE: 複雑なP0分岐とエラー処理
adaptive_sizing_init();
if (prewarm > 0) {
#if HAKMEM_TINY_P0_BATCH_REFILL
int taken = sll_refill_batch_from_ss(5, prewarm);
#else
int taken = sll_refill_small_from_ss(5, prewarm);
#endif
}
// AFTER: Box API 1行
int taken = box_prewarm_tls(5, prewarm);
```
## シンボルExport修正
hakmem_tiny.c: 5つのシンボルをstatic → non-static
- g_tls_slabs[] (TLS slab配列)
- g_sll_multiplier (SLL容量乗数)
- g_sll_cap_override[] (容量オーバーライド)
- superslab_refill() (SuperSlab再充填)
- ss_active_add() (アクティブカウンタ)
## ビルドシステム
Makefile: TINY_BENCH_OBJS_BASEに3つのBox modules追加
- core/box/capacity_box.o
- core/box/carve_push_box.o
- core/box/prewarm_box.o
## 動作確認
✅ Debug build成功
✅ Box Prewarm API動作確認
[PREWARM] class=5 requested=128 taken=32
## 次のステップ
- Box Refill Manager (Priority 4)
- Box SuperSlab Allocator (Priority 5)
- Release build修正(tiny_debug_ring_record)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
579 lines
25 KiB
C
579 lines
25 KiB
C
// tls_sll_box.h - Box TLS-SLL: Single-Linked List API (C7-safe)
|
|
//
|
|
// Purpose: Centralized TLS SLL management with C7 protection
|
|
// Design: Zero-overhead static inline API, C7 always rejected
|
|
//
|
|
// Key Rules:
|
|
// 1. C7 (1KB headerless) is ALWAYS rejected (returns false/0)
|
|
// 2. All SLL direct writes MUST go through this API
|
|
// 3. Pop returns with first 8 bytes cleared for C7 (safety)
|
|
// 4. Capacity checks prevent overflow
|
|
//
|
|
// Architecture:
|
|
// - Box TLS-SLL (this): Push/Pop/Splice authority
|
|
// - Caller: Provides capacity limits, handles fallback on failure
|
|
//
|
|
// Performance:
|
|
// - Static inline → zero function call overhead
|
|
// - C7 check: 1 comparison + predict-not-taken (< 1 cycle)
|
|
// - Same performance as direct SLL access for C0-C6
|
|
|
|
#ifndef TLS_SLL_BOX_H
|
|
#define TLS_SLL_BOX_H
|
|
|
|
#include <stdint.h>
|
|
#include <stdbool.h>
|
|
#include <stdio.h> // For fprintf in debug
|
|
#include <stdlib.h> // For abort in debug
|
|
#include "../ptr_trace.h" // Debug-only: pointer next read/write tracing
|
|
#include "../hakmem_tiny_config.h" // For TINY_NUM_CLASSES
|
|
#include "../hakmem_build_flags.h"
|
|
#include "../tiny_region_id.h" // HEADER_MAGIC / HEADER_CLASS_MASK
|
|
#include "../hakmem_tiny_integrity.h" // PRIORITY 2: Freelist integrity checks
|
|
#include "../ptr_track.h" // Pointer tracking for debugging header corruption
|
|
#include "tiny_next_ptr_box.h" // Box API: Next pointer read/write
|
|
|
|
// Debug guard: validate base pointer before SLL ops (Debug only)
|
|
#if !HAKMEM_BUILD_RELEASE
|
|
extern const size_t g_tiny_class_sizes[];
|
|
static inline void tls_sll_debug_guard(int class_idx, void* base, const char* where) {
|
|
(void)g_tiny_class_sizes;
|
|
// Only a minimal guard: tiny integers are always invalid
|
|
if ((uintptr_t)base < 4096) {
|
|
fprintf(stderr, "[TLS_SLL_GUARD] %s: small ptr=%p cls=%d (likely corruption)\n", where, base, class_idx);
|
|
abort();
|
|
}
|
|
// NOTE: Do NOT check alignment vs class size here.
|
|
// Blocks are stride-aligned (size+header) from slab base; modulo class size is not 0.
|
|
}
|
|
#else
|
|
static inline void tls_sll_debug_guard(int class_idx, void* base, const char* where) { (void)class_idx; (void)base; (void)where; }
|
|
#endif
|
|
|
|
// Normalize a possibly user-pointer (base+1) to base (header classes)
|
|
static inline void* tls_sll_normalize_base(int class_idx, void* node) {
|
|
(void)class_idx;
|
|
// Caller must pass base pointers; do not heuristically adjust.
|
|
return node;
|
|
}
|
|
|
|
// External TLS SLL state (defined elsewhere)
|
|
extern __thread void* g_tls_sll_head[TINY_NUM_CLASSES];
|
|
extern __thread uint32_t g_tls_sll_count[TINY_NUM_CLASSES];
|
|
|
|
// ========== Push ==========
|
|
|
|
// Push pointer to TLS SLL
|
|
// Returns: true on success, false if C7 or capacity exceeded
|
|
//
|
|
// CRITICAL Phase 7 Header Design:
|
|
// - C0-C6 (header classes): [1B header][user data]
|
|
// ^base ^ptr (caller passes this)
|
|
// - SLL stores "base" (ptr-1) to avoid overwriting header
|
|
// - C7 (headerless): ptr == base (no offset)
|
|
//
|
|
// Safety:
|
|
// - C7 always rejected (headerless, first 8 bytes = user data)
|
|
// - Capacity check prevents overflow
|
|
// - Header protection: stores base (ptr-1) for C0-C6
|
|
//
|
|
// Performance: 3-4 cycles (C0-C6), < 1 cycle (C7 fast rejection)
|
|
static inline bool tls_sll_push(int class_idx, void* ptr, uint32_t capacity) {
|
|
// PRIORITY 1: Bounds check BEFORE any array access
|
|
HAK_CHECK_CLASS_IDX(class_idx, "tls_sll_push");
|
|
|
|
// Phase E1-CORRECT: All classes including C7 can now use TLS SLL
|
|
|
|
// Capacity check
|
|
if (g_tls_sll_count[class_idx] >= capacity) {
|
|
return false; // SLL full
|
|
}
|
|
|
|
// ✅ FIX #15: CATCH USER pointer contamination at injection point
|
|
// For Class 2 (32B blocks), BASE addresses should be multiples of 33 (stride)
|
|
// USER pointers are BASE+1, so for Class 2 starting at even address, USER is ODD
|
|
// This catches USER pointers being passed to TLS SLL (should be BASE!)
|
|
#if !HAKMEM_BUILD_RELEASE && HAKMEM_TINY_HEADER_CLASSIDX
|
|
if (class_idx == 2) { // Class 2 specific check (can extend to all header classes)
|
|
uintptr_t addr = (uintptr_t)ptr;
|
|
// For class 2 with 32B blocks, check if pointer looks like USER (BASE+1)
|
|
// If slab base is at offset 0x...X0, then:
|
|
// - First block BASE: 0x...X0 (even)
|
|
// - First block USER: 0x...X1 (odd)
|
|
// - Second block BASE: 0x...X0 + 33 = 0x...Y1 (odd)
|
|
// - Second block USER: 0x...Y2 (even)
|
|
// So ODD/EVEN alternates, but we can detect obvious USER pointers
|
|
// by checking if ptr-1 has a header
|
|
if ((addr & 0xF) <= 15) { // Check last nibble for patterns
|
|
uint8_t* possible_base = (addr & 1) ? ((uint8_t*)ptr - 1) : (uint8_t*)ptr;
|
|
uint8_t byte_at_possible_base = *possible_base;
|
|
uint8_t expected_header = HEADER_MAGIC | (class_idx & HEADER_CLASS_MASK);
|
|
|
|
// If ptr is ODD and ptr-1 has valid header, ptr is USER!
|
|
if ((addr & 1) && byte_at_possible_base == expected_header) {
|
|
extern _Atomic uint64_t malloc_count;
|
|
uint64_t call = atomic_load(&malloc_count);
|
|
fprintf(stderr, "\n========================================\n");
|
|
fprintf(stderr, "=== USER POINTER BUG DETECTED ===\n");
|
|
fprintf(stderr, "========================================\n");
|
|
fprintf(stderr, "Call: %lu\n", call);
|
|
fprintf(stderr, "Class: %d\n", class_idx);
|
|
fprintf(stderr, "Passed ptr: %p (ODD address - USER pointer!)\n", ptr);
|
|
fprintf(stderr, "Expected: %p (EVEN address - BASE pointer)\n", (void*)possible_base);
|
|
fprintf(stderr, "Header at ptr-1: 0x%02x (valid header!)\n", byte_at_possible_base);
|
|
fprintf(stderr, "========================================\n");
|
|
fprintf(stderr, "BUG: Caller passed USER pointer to tls_sll_push!\n");
|
|
fprintf(stderr, "FIX: Convert USER → BASE before push\n");
|
|
fprintf(stderr, "========================================\n");
|
|
fflush(stderr);
|
|
abort();
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// CRITICAL: Caller must pass "base" pointer (NOT user ptr)
|
|
// Phase 7 carve operations return base (stride includes header)
|
|
// SLL stores base to avoid overwriting header with next pointer
|
|
|
|
// ✅ FIX #11C: ALWAYS restore header before pushing to SLL (defense in depth)
|
|
// ROOT CAUSE (multiple sources):
|
|
// 1. User may overwrite byte 0 (header) during normal use
|
|
// 2. Freelist stores next at base (offset 0), overwriting header
|
|
// 3. Simple refill carves blocks without writing headers
|
|
//
|
|
// SOLUTION: Restore header HERE (single point of truth) instead of at each call site.
|
|
// This prevents all header corruption bugs at the TLS SLL boundary.
|
|
// COST: 1 byte write (~1-2 cycles, negligible vs SEGV debugging cost).
|
|
#if HAKMEM_TINY_HEADER_CLASSIDX
|
|
// DEBUG: Log if header was corrupted (0x00) before restoration for class 2
|
|
uint8_t before = *(uint8_t*)ptr;
|
|
PTR_TRACK_TLS_PUSH(ptr, class_idx); // Track BEFORE header write
|
|
*(uint8_t*)ptr = HEADER_MAGIC | (class_idx & HEADER_CLASS_MASK);
|
|
PTR_TRACK_HEADER_WRITE(ptr, HEADER_MAGIC | (class_idx & HEADER_CLASS_MASK));
|
|
|
|
// ✅ Option C: Class 2 inline logs - PUSH operation (DISABLED for performance)
|
|
if (0 && class_idx == 2) {
|
|
extern _Atomic uint64_t malloc_count;
|
|
uint64_t call = atomic_load(&malloc_count);
|
|
fprintf(stderr, "[C2_PUSH] ptr=%p before=0x%02x after=0xa2 call=%lu\n",
|
|
ptr, before, call);
|
|
fflush(stderr);
|
|
}
|
|
#endif
|
|
|
|
// Phase 7: Store next pointer at header-safe offset (base+1 for C0-C6)
|
|
#if HAKMEM_TINY_HEADER_CLASSIDX
|
|
const size_t next_offset = 1; // C7 is rejected above; always skip header
|
|
#else
|
|
const size_t next_offset = 0;
|
|
#endif
|
|
tls_sll_debug_guard(class_idx, ptr, "push");
|
|
|
|
#if !HAKMEM_BUILD_RELEASE
|
|
// PRIORITY 2+: Double-free detection - scan existing SLL for duplicates
|
|
// This is expensive but critical for debugging the P0 corruption bug
|
|
{
|
|
void* scan = g_tls_sll_head[class_idx];
|
|
uint32_t scan_count = 0;
|
|
const uint32_t scan_limit = (g_tls_sll_count[class_idx] < 100) ? g_tls_sll_count[class_idx] : 100;
|
|
|
|
while (scan && scan_count < scan_limit) {
|
|
if (scan == ptr) {
|
|
fprintf(stderr, "[TLS_SLL_PUSH] FATAL: Double-free detected!\n");
|
|
fprintf(stderr, " class_idx=%d ptr=%p appears multiple times in SLL\n", class_idx, ptr);
|
|
fprintf(stderr, " g_tls_sll_count[%d]=%u scan_pos=%u\n",
|
|
class_idx, g_tls_sll_count[class_idx], scan_count);
|
|
fprintf(stderr, " This indicates the same pointer was freed twice\n");
|
|
ptr_trace_dump_now("double_free");
|
|
fflush(stderr);
|
|
abort();
|
|
}
|
|
|
|
void* next_scan;
|
|
PTR_NEXT_READ("sll_scan", class_idx, scan, next_offset, next_scan);
|
|
scan = next_scan;
|
|
scan_count++;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
PTR_NEXT_WRITE("tls_push", class_idx, ptr, next_offset, g_tls_sll_head[class_idx]);
|
|
g_tls_sll_head[class_idx] = ptr;
|
|
g_tls_sll_count[class_idx]++;
|
|
|
|
return true;
|
|
}
|
|
|
|
// ========== Pop ==========
|
|
|
|
// Pop pointer from TLS SLL
|
|
// Returns: true on success (writes user ptr to *out), false if empty
|
|
//
|
|
// CRITICAL Phase 7 Header Design:
|
|
// - SLL stores "base" (ptr-1) for C0-C6
|
|
// - Must return "ptr" (base+1) to user
|
|
// - C7: base == ptr (no offset)
|
|
//
|
|
// Safety:
|
|
// - C7 protection: clears first 8 bytes on pop (prevents next pointer leak)
|
|
// - Header protection: returns ptr (base+1) for C0-C6
|
|
// - NULL check before deref
|
|
//
|
|
// Performance: 4-5 cycles
|
|
static inline bool tls_sll_pop(int class_idx, void** out) {
|
|
// PRIORITY 1: Bounds check
|
|
HAK_CHECK_CLASS_IDX(class_idx, "tls_sll_pop");
|
|
atomic_fetch_add(&g_integrity_check_class_bounds, 1);
|
|
|
|
void* base = g_tls_sll_head[class_idx];
|
|
if (!base) {
|
|
return false; // SLL empty
|
|
}
|
|
|
|
// PRIORITY 2: Validate base pointer BEFORE dereferencing
|
|
#if !HAKMEM_BUILD_RELEASE
|
|
if (!validate_ptr_range(base, "tls_sll_pop_base")) {
|
|
fprintf(stderr, "[TLS_SLL_POP] FATAL: Invalid BASE pointer!\n");
|
|
fprintf(stderr, " class_idx=%d base=%p\n", class_idx, base);
|
|
fprintf(stderr, " g_tls_sll_count[%d]=%u\n", class_idx, g_tls_sll_count[class_idx]);
|
|
fflush(stderr);
|
|
abort();
|
|
}
|
|
#endif
|
|
|
|
// Pop from SLL (reads next from base)
|
|
// Phase E1-CORRECT FIX: Class 0 must use offset 0 (8B block can't fit 8B pointer at offset 1)
|
|
#if HAKMEM_TINY_HEADER_CLASSIDX
|
|
// CRITICAL: Use class_idx argument (NOT header byte) because Class 0/7 overwrite header with next pointer!
|
|
const size_t next_offset = (class_idx == 0 || class_idx == 7) ? 0 : 1;
|
|
#else
|
|
const size_t next_offset = 0;
|
|
#endif
|
|
|
|
// PRIORITY 2: Validate that (base + next_offset) is safe to dereference BEFORE reading
|
|
#if !HAKMEM_BUILD_RELEASE
|
|
void* read_addr = (uint8_t*)base + next_offset;
|
|
if (!validate_ptr_range(read_addr, "tls_sll_pop_read_addr")) {
|
|
fprintf(stderr, "[TLS_SLL_POP] FATAL: Cannot safely read next pointer!\n");
|
|
fprintf(stderr, " class_idx=%d base=%p read_addr=%p (base+%zu)\n",
|
|
class_idx, base, read_addr, next_offset);
|
|
fprintf(stderr, " g_tls_sll_count[%d]=%u\n", class_idx, g_tls_sll_count[class_idx]);
|
|
fflush(stderr);
|
|
abort();
|
|
}
|
|
atomic_fetch_add(&g_integrity_check_freelist, 1);
|
|
#endif
|
|
|
|
tls_sll_debug_guard(class_idx, base, "pop");
|
|
|
|
// ✅ FIX #12: VALIDATION - Detect header corruption at the moment it's injected
|
|
// This is the CRITICAL validation point: we validate the header BEFORE reading next pointer.
|
|
// If the header is corrupted here, we know corruption happened BEFORE this pop (during push/splice/carve).
|
|
// Phase E1-CORRECT: Class 1-6 have headers, Class 0/7 overwrite header with next pointer
|
|
#if HAKMEM_TINY_HEADER_CLASSIDX
|
|
if (class_idx != 0 && class_idx != 7) {
|
|
// Read byte 0 (should be header = HEADER_MAGIC | class_idx)
|
|
uint8_t byte0 = *(uint8_t*)base;
|
|
PTR_TRACK_TLS_POP(base, class_idx); // Track POP operation
|
|
PTR_TRACK_HEADER_READ(base, byte0); // Track header read
|
|
uint8_t expected = HEADER_MAGIC | (class_idx & HEADER_CLASS_MASK);
|
|
|
|
// ✅ Option C: Class 2 inline logs - POP operation (DISABLED for performance)
|
|
if (0 && class_idx == 2) {
|
|
extern _Atomic uint64_t malloc_count;
|
|
uint64_t call = atomic_load(&malloc_count);
|
|
fprintf(stderr, "[C2_POP] ptr=%p header=0x%02x expected=0xa2 call=%lu\n",
|
|
base, byte0, call);
|
|
fflush(stderr);
|
|
}
|
|
|
|
if (byte0 != expected) {
|
|
// 🚨 CORRUPTION DETECTED AT INJECTION POINT!
|
|
// Get call number from malloc wrapper
|
|
extern _Atomic uint64_t malloc_count; // Defined in hak_wrappers.inc.h
|
|
uint64_t call_num = atomic_load(&malloc_count);
|
|
|
|
fprintf(stderr, "\n========================================\n");
|
|
fprintf(stderr, "=== CORRUPTION DETECTED (Fix #12) ===\n");
|
|
fprintf(stderr, "========================================\n");
|
|
fprintf(stderr, "Malloc call: %lu\n", call_num);
|
|
fprintf(stderr, "Class: %d\n", class_idx);
|
|
fprintf(stderr, "Base ptr: %p\n", base);
|
|
fprintf(stderr, "Expected: 0x%02x (HEADER_MAGIC | class_idx)\n", expected);
|
|
fprintf(stderr, "Actual: 0x%02x\n", byte0);
|
|
fprintf(stderr, "========================================\n");
|
|
fprintf(stderr, "\nThis means corruption was injected BEFORE this pop.\n");
|
|
fprintf(stderr, "Likely culprits:\n");
|
|
fprintf(stderr, " 1. tls_sll_push() - failed to restore header\n");
|
|
fprintf(stderr, " 2. tls_sll_splice() - chain had corrupted headers\n");
|
|
fprintf(stderr, " 3. trc_linear_carve() - didn't write header\n");
|
|
fprintf(stderr, " 4. trc_pop_from_freelist() - didn't restore header\n");
|
|
fprintf(stderr, " 5. Remote free path - overwrote header\n");
|
|
fprintf(stderr, "========================================\n");
|
|
fflush(stderr);
|
|
abort(); // Immediate crash with backtrace
|
|
}
|
|
} // end if (class_idx != 0 && class_idx != 7)
|
|
#endif
|
|
|
|
// DEBUG: Log read operation for crash investigation
|
|
static _Atomic uint64_t g_pop_count = 0;
|
|
uint64_t pop_num = atomic_fetch_add(&g_pop_count, 1);
|
|
|
|
// Log ALL class 0 pops (DISABLED for performance)
|
|
if (0 && class_idx == 0) {
|
|
// Check byte 0 to see if header exists
|
|
uint8_t byte0 = *(uint8_t*)base;
|
|
fprintf(stderr, "[TLS_POP_C0] pop=%lu base=%p byte0=0x%02x next_off=%zu\n",
|
|
pop_num, base, byte0, next_offset);
|
|
fflush(stderr);
|
|
}
|
|
|
|
void* next; PTR_NEXT_READ("tls_pop", class_idx, base, next_offset, next);
|
|
|
|
if (0 && class_idx == 0) {
|
|
fprintf(stderr, "[TLS_POP_C0] pop=%lu base=%p next=%p\n",
|
|
pop_num, base, next);
|
|
fflush(stderr);
|
|
}
|
|
|
|
// PRIORITY 2: Validate next pointer after reading it
|
|
#if !HAKMEM_BUILD_RELEASE
|
|
if (!validate_ptr_range(next, "tls_sll_pop_next")) {
|
|
fprintf(stderr, "[TLS_SLL_POP] FATAL: Invalid next pointer after read!\n");
|
|
fprintf(stderr, " class_idx=%d base=%p next=%p next_offset=%zu\n",
|
|
class_idx, base, next, next_offset);
|
|
fprintf(stderr, " g_tls_sll_count[%d]=%u\n", class_idx, g_tls_sll_count[class_idx]);
|
|
fflush(stderr);
|
|
abort();
|
|
}
|
|
|
|
// PRIORITY 2+: Additional check for obviously corrupted pointers (non-canonical addresses)
|
|
// Detects patterns like 0x7fff00008000 that pass validate_ptr_range but are still invalid
|
|
if (next != NULL) {
|
|
uintptr_t addr = (uintptr_t)next;
|
|
// x86-64 canonical addresses: bits 48-63 must be copies of bit 47
|
|
// Valid ranges: 0x0000_0000_0000_0000 to 0x0000_7FFF_FFFF_FFFF (user space)
|
|
// or 0xFFFF_8000_0000_0000 to 0xFFFF_FFFF_FFFF_FFFF (kernel space)
|
|
// Invalid: 0x0001_xxxx_xxxx_xxxx to 0xFFFE_xxxx_xxxx_xxxx
|
|
uint64_t top_bits = addr >> 47;
|
|
if (top_bits != 0 && top_bits != 0x1FFFF) {
|
|
fprintf(stderr, "[TLS_SLL_POP] FATAL: Corrupted SLL chain - non-canonical address!\n");
|
|
fprintf(stderr, " class_idx=%d base=%p next=%p (top_bits=0x%lx)\n",
|
|
class_idx, base, next, (unsigned long)top_bits);
|
|
fprintf(stderr, " g_tls_sll_count[%d]=%u\n", class_idx, g_tls_sll_count[class_idx]);
|
|
fprintf(stderr, " Likely causes: double-free, use-after-free, buffer overflow\n");
|
|
ptr_trace_dump_now("sll_chain_corruption");
|
|
fflush(stderr);
|
|
abort();
|
|
}
|
|
}
|
|
#endif
|
|
|
|
g_tls_sll_head[class_idx] = next;
|
|
if (g_tls_sll_count[class_idx] > 0) {
|
|
g_tls_sll_count[class_idx]--;
|
|
}
|
|
|
|
// CRITICAL FIX: Clear next pointer to prevent stale pointer corruption
|
|
//
|
|
// ROOT CAUSE OF P0 BUG (iteration 28,440 crash):
|
|
// When a block is popped from SLL and given to user, the `next` pointer at base+1
|
|
// (for C0-C6) or base (for C7) was NOT cleared. If the user doesn't overwrite it,
|
|
// the stale `next` pointer remains. When the block is freed and pushed back to SLL,
|
|
// the stale pointer creates loops or invalid pointers → SEGV at 0x7fff00008000!
|
|
//
|
|
// FIX: Clear next pointer for BOTH C7 AND C0-C6:
|
|
// - C7 (headerless): next at base (offset 0) - was already cleared
|
|
// - C0-C6 (header): next at base+1 (offset 1) - **WAS NOT CLEARED** ← BUG!
|
|
//
|
|
// Previous WRONG assumption: "C0-C6 header hides next" - FALSE!
|
|
// Phase E1-CORRECT: All classes have 1-byte header at base, next is at base+1
|
|
//
|
|
// Cost: 1 store instruction (~1 cycle) for all classes
|
|
#if HAKMEM_TINY_HEADER_CLASSIDX
|
|
// DEBUG: Verify header is intact BEFORE clearing next pointer
|
|
if (class_idx == 2) {
|
|
uint8_t header_before_clear = *(uint8_t*)base;
|
|
if (header_before_clear != (HEADER_MAGIC | (class_idx & HEADER_CLASS_MASK))) {
|
|
extern _Atomic uint64_t malloc_count;
|
|
uint64_t call_num = atomic_load(&malloc_count);
|
|
fprintf(stderr, "[POP_HEADER_CHECK] call=%lu cls=%d base=%p header=0x%02x BEFORE clear_next!\n",
|
|
call_num, class_idx, base, header_before_clear);
|
|
fflush(stderr);
|
|
}
|
|
}
|
|
|
|
tiny_next_write(class_idx, base, NULL); // All classes: clear next pointer
|
|
|
|
// DEBUG: Verify header is STILL intact AFTER clearing next pointer
|
|
if (class_idx == 2) {
|
|
uint8_t header_after_clear = *(uint8_t*)base;
|
|
if (header_after_clear != (HEADER_MAGIC | (class_idx & HEADER_CLASS_MASK))) {
|
|
extern _Atomic uint64_t malloc_count;
|
|
uint64_t call_num = atomic_load(&malloc_count);
|
|
fprintf(stderr, "[POP_HEADER_CORRUPTED] call=%lu cls=%d base=%p header=0x%02x AFTER clear_next!\n",
|
|
call_num, class_idx, base, header_after_clear);
|
|
fprintf(stderr, "[POP_HEADER_CORRUPTED] This means clear_next OVERWROTE the header!\n");
|
|
fprintf(stderr, "[POP_HEADER_CORRUPTED] Bug: next_offset calculation is WRONG!\n");
|
|
fflush(stderr);
|
|
abort();
|
|
}
|
|
}
|
|
#else
|
|
*(void**)base = NULL; // No header: clear at base
|
|
#endif
|
|
|
|
*out = base; // Return base (caller converts to ptr if needed)
|
|
return true;
|
|
}
|
|
|
|
// ========== Splice ==========
|
|
|
|
// Splice chain of pointers to TLS SLL (batch push)
|
|
// Returns: actual count moved (0 for C7 or if capacity exceeded)
|
|
//
|
|
// CRITICAL Phase 7 Header Design:
|
|
// - Caller MUST pass chain of "base" pointers (ptr-1 for C0-C6)
|
|
// - Chain links are stored at base (*(void**)base = next_base)
|
|
// - SLL head stores base pointers
|
|
//
|
|
// Safety:
|
|
// - C7 always returns 0 (no splice)
|
|
// - Capacity check limits splice size
|
|
// - Chain traversal with safety (breaks on NULL)
|
|
// - Assumes chain is already linked using base pointers
|
|
//
|
|
// Performance: ~5 cycles + O(count) for chain traversal
|
|
static inline uint32_t tls_sll_splice(int class_idx, void* chain_head, uint32_t count, uint32_t capacity) {
|
|
// Phase E1-CORRECT: All classes including C7 can now use splice
|
|
|
|
// 🐛 DEBUG: UNCONDITIONAL log to verify function is called
|
|
#if !HAKMEM_BUILD_RELEASE
|
|
{
|
|
static _Atomic int g_once = 0;
|
|
if (atomic_fetch_add(&g_once, 1) == 0) {
|
|
fprintf(stderr, "[SPLICE_ENTRY] First call to tls_sll_splice()! cls=%d count=%u capacity=%u\n",
|
|
class_idx, count, capacity);
|
|
fflush(stderr);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// Calculate available capacity
|
|
uint32_t available = (capacity > g_tls_sll_count[class_idx])
|
|
? (capacity - g_tls_sll_count[class_idx]) : 0;
|
|
|
|
// 🐛 DEBUG: Log ALL splice inputs to diagnose truncation
|
|
#if !HAKMEM_BUILD_RELEASE
|
|
{
|
|
static _Atomic uint64_t g_splice_log_count = 0;
|
|
uint64_t splice_num = atomic_fetch_add(&g_splice_log_count, 1);
|
|
if (splice_num < 10) { // Log first 10 splices
|
|
fprintf(stderr, "[SPLICE_DEBUG #%lu] cls=%d count=%u capacity=%u sll_count=%u available=%u\n",
|
|
splice_num, class_idx, count, capacity, g_tls_sll_count[class_idx], available);
|
|
fflush(stderr);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (available == 0 || count == 0 || !chain_head) {
|
|
return 0; // No space or empty chain
|
|
}
|
|
|
|
// Limit splice size to available capacity
|
|
uint32_t to_move = (count < available) ? count : available;
|
|
|
|
// ✅ FIX #14: DEFENSE IN DEPTH - Restore headers for ALL nodes in chain
|
|
// ROOT CAUSE: Even though callers (trc_linear_carve, trc_pop_from_freelist) are
|
|
// supposed to restore headers, there might be edge cases or future code paths
|
|
// that forget. Adding header restoration HERE provides a safety net.
|
|
//
|
|
// COST: 1 byte write per node (~1-2 cycles each, negligible vs SEGV debugging)
|
|
// BENEFIT: Guaranteed header integrity at TLS SLL boundary (defense in depth!)
|
|
#if HAKMEM_TINY_HEADER_CLASSIDX
|
|
const size_t next_offset = 1; // C0-C6: next at base+1
|
|
|
|
// Restore headers for ALL nodes in chain (traverse once)
|
|
{
|
|
void* node = chain_head;
|
|
uint32_t restored_count = 0;
|
|
|
|
while (node != NULL && restored_count < to_move) {
|
|
uint8_t before = *(uint8_t*)node;
|
|
uint8_t expected = HEADER_MAGIC | (class_idx & HEADER_CLASS_MASK);
|
|
|
|
// Restore header unconditionally
|
|
*(uint8_t*)node = expected;
|
|
|
|
// ✅ Option C: Class 2 inline logs - SPLICE operation (DISABLED for performance)
|
|
if (0 && class_idx == 2) {
|
|
extern _Atomic uint64_t malloc_count;
|
|
uint64_t call = atomic_load(&malloc_count);
|
|
fprintf(stderr, "[C2_SPLICE] ptr=%p before=0x%02x after=0xa2 restored=%u/%u call=%lu\n",
|
|
node, before, restored_count+1, to_move, call);
|
|
fflush(stderr);
|
|
}
|
|
|
|
// Move to next node
|
|
void* next = tiny_next_read(class_idx, node);
|
|
node = next;
|
|
restored_count++;
|
|
}
|
|
}
|
|
#else
|
|
const size_t next_offset = 0; // No header: next at base
|
|
#endif
|
|
|
|
// Traverse chain to find tail (needed for splicing)
|
|
void* tail = chain_head;
|
|
for (uint32_t i = 1; i < to_move; i++) {
|
|
tls_sll_debug_guard(class_idx, tail, "splice_trav");
|
|
void* next; PTR_NEXT_READ("tls_sp_trav", class_idx, tail, next_offset, next);
|
|
if (!next) {
|
|
// Chain shorter than expected, adjust to_move
|
|
to_move = i;
|
|
break;
|
|
}
|
|
tail = next;
|
|
}
|
|
|
|
// Splice chain to SLL head
|
|
// tail is a base pointer by construction
|
|
tls_sll_debug_guard(class_idx, tail, "splice_link");
|
|
#if !HAKMEM_BUILD_RELEASE
|
|
fprintf(stderr, "[SPLICE_LINK] cls=%d tail=%p off=%zu old_head=%p\n",
|
|
class_idx, tail, (size_t)next_offset, g_tls_sll_head[class_idx]);
|
|
#endif
|
|
PTR_NEXT_WRITE("tls_sp_link", class_idx, tail, next_offset, g_tls_sll_head[class_idx]);
|
|
|
|
// ✅ FIX #11: chain_head is already correct BASE pointer from caller
|
|
tls_sll_debug_guard(class_idx, chain_head, "splice_head");
|
|
#if !HAKMEM_BUILD_RELEASE
|
|
fprintf(stderr, "[SPLICE_SET_HEAD] cls=%d head=%p moved=%u\n",
|
|
class_idx, chain_head, (unsigned)to_move);
|
|
#endif
|
|
g_tls_sll_head[class_idx] = chain_head;
|
|
g_tls_sll_count[class_idx] += to_move;
|
|
|
|
return to_move;
|
|
}
|
|
|
|
// ========== Debug/Stats (optional) ==========
|
|
|
|
#if !HAKMEM_BUILD_RELEASE
|
|
// Verify C7 is not in SLL (debug only, call at safe points)
|
|
static inline void tls_sll_verify_no_c7(void) {
|
|
void* head = g_tls_sll_head[7];
|
|
if (head != NULL) {
|
|
fprintf(stderr, "[TLS_SLL_BUG] C7 found in TLS SLL! head=%p count=%u\n",
|
|
head, g_tls_sll_count[7]);
|
|
fprintf(stderr, "[TLS_SLL_BUG] This should NEVER happen - C7 is headerless!\n");
|
|
abort();
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#endif // TLS_SLL_BOX_H
|