diff --git a/core/box/sp_rebind_slot_box.h b/core/box/sp_rebind_slot_box.h new file mode 100644 index 00000000..ad360f9f --- /dev/null +++ b/core/box/sp_rebind_slot_box.h @@ -0,0 +1,94 @@ +// sp_rebind_slot_box.h - SuperSlab Slot Rebinding (atomic class change) +// Purpose: Atomically rebind a slab slot to a new class with consistent state +// License: MIT +// Date: 2025-11-30 + +#ifndef HAKMEM_SP_REBIND_SLOT_BOX_H +#define HAKMEM_SP_REBIND_SLOT_BOX_H + +#include "hakmem_tiny_superslab_internal.h" +#include "box/tls_sll_box.h" // TLS_SLL_RESET + +// ========== SuperSlab Slot Rebinding ========== +// +// Atomically rebind a slab slot when: +// - Stage 1: EMPTY → ACTIVE (same or different class) +// - Stage 2: UNUSED → ACTIVE (new class) +// - Stage 3: New SuperSlab (initial class assignment) +// +// Operations (in order): +// 1. Remember old class (for TLS cleanup) +// 2. Fix geometry if needed (update capacity/class_idx) +// 3. Reset TLS SLL for BOTH old and new class (防御的) +// 4. Update class_map (out-of-band lookup) +// +// Box Theory: +// - Single Responsibility: Ensure slab→class binding consistency +// - Clear Contract: (ss, slab_idx, new_class) → consistent state +// - Observable: Debug log shows old_cls→new_cls transitions +// - Composable: Called from all acquire paths + +#if !HAKMEM_BUILD_RELEASE +#define SP_REBIND_SLOT(ss, slab_idx, new_class_idx) \ + do { \ + static __thread int s_trace = -1; \ + if (__builtin_expect(s_trace == -1, 0)) { \ + const char* e = getenv("HAKMEM_SP_REBIND_TRACE"); \ + s_trace = (e && *e && *e != '0') ? 1 : 0; \ + } \ + \ + /* Step 1: Remember old class for TLS cleanup */ \ + uint8_t old_class_idx = (ss)->slabs[slab_idx].class_idx; \ + \ + /* Step 2: Fix geometry (updates capacity, class_idx, etc) */ \ + sp_fix_geometry_if_needed((ss), (slab_idx), (new_class_idx)); \ + \ + /* Step 3: Reset TLS SLL for old class (if different) */ \ + if (old_class_idx != (uint8_t)(new_class_idx) && \ + old_class_idx < TINY_NUM_CLASSES) { \ + TLS_SLL_RESET(old_class_idx); \ + if (s_trace) { \ + fprintf(stderr, \ + "[SP_REBIND_SLOT] OLD class TLS reset: cls=%d ss=%p slab=%d (old_cls=%d -> new_cls=%d)\n", \ + old_class_idx, (void*)(ss), (slab_idx), old_class_idx, (new_class_idx)); \ + } \ + } \ + \ + /* Step 4: Reset TLS SLL for new class (防御的 - 常にクリア) */ \ + if ((new_class_idx) < TINY_NUM_CLASSES) { \ + TLS_SLL_RESET(new_class_idx); \ + if (s_trace) { \ + fprintf(stderr, \ + "[SP_REBIND_SLOT] NEW class TLS reset: cls=%d ss=%p slab=%d\n", \ + (new_class_idx), (void*)(ss), (slab_idx)); \ + } \ + } \ + \ + /* Step 5: Update class_map (out-of-band lookup) */ \ + ss_slab_meta_class_idx_set((ss), (slab_idx), (uint8_t)(new_class_idx)); \ + \ + if (s_trace) { \ + fprintf(stderr, \ + "[SP_REBIND_SLOT] COMPLETE: ss=%p slab=%d old_cls=%d -> new_cls=%d cap=%u\n", \ + (void*)(ss), (slab_idx), old_class_idx, (new_class_idx), \ + (unsigned)(ss)->slabs[slab_idx].capacity); \ + } \ + } while (0) +#else +// Release build: no trace, just execute operations +#define SP_REBIND_SLOT(ss, slab_idx, new_class_idx) \ + do { \ + uint8_t old_class_idx = (ss)->slabs[slab_idx].class_idx; \ + sp_fix_geometry_if_needed((ss), (slab_idx), (new_class_idx)); \ + if (old_class_idx != (uint8_t)(new_class_idx) && \ + old_class_idx < TINY_NUM_CLASSES) { \ + TLS_SLL_RESET(old_class_idx); \ + } \ + if ((new_class_idx) < TINY_NUM_CLASSES) { \ + TLS_SLL_RESET(new_class_idx); \ + } \ + ss_slab_meta_class_idx_set((ss), (slab_idx), (uint8_t)(new_class_idx)); \ + } while (0) +#endif + +#endif // HAKMEM_SP_REBIND_SLOT_BOX_H diff --git a/core/box/tiny_geometry_box.h b/core/box/tiny_geometry_box.h new file mode 100644 index 00000000..0eadc995 --- /dev/null +++ b/core/box/tiny_geometry_box.h @@ -0,0 +1,174 @@ +// tiny_geometry_box.h - Pointer Geometry Calculation Box +// Purpose: Unified pointer arithmetic for BASE/USER/SuperSlab/slab_idx conversions +// License: MIT +// Date: 2025-11-30 +// +// Box Theory Principles: +// - Single Responsibility: Pointer/offset calculation only +// - Clear Contract: Input(ptr/ss/slab_idx) → Output(conversion result) +// - Observable: Debug builds validate all range checks +// - Composable: Used by all allocation/free paths +// +// Background: +// Phase 9-3 analysis revealed pointer arithmetic duplication across codebase: +// - BASE ↔ USER conversion scattered in 40+ locations +// - ptr → SuperSlab lookup duplicated in 20+ files +// - slab_idx calculation implemented inconsistently +// +// Solution: +// Unified API for all pointer geometry operations with single source of truth + +#ifndef HAKMEM_TINY_GEOMETRY_BOX_H +#define HAKMEM_TINY_GEOMETRY_BOX_H + +#include "../hakmem_tiny_superslab_internal.h" +#include "../hakmem_super_registry.h" +#include "../superslab/superslab_inline.h" +#include +#include + +// ========== BASE ↔ USER Pointer Conversion ========== +// +// BASE pointer: Points to block start (where header is stored for C1-C6) +// USER pointer: Points to usable memory (BASE + header_size) +// +// Class 0 (16B): header_size = 0 (no header) +// Class 1-6: header_size = 1 byte +// Class 7 (C7): header_size = 0 (no header, uses offset 0 for next) + +// Convert USER pointer → BASE pointer +static inline void* BASE_FROM_USER(int class_idx, void* user_ptr) +{ + if (!user_ptr) return NULL; + + // Class 0 and C7: no header, USER == BASE + if (class_idx == 0 || class_idx == 7) { + return user_ptr; + } + + // Class 1-6: header is 1 byte before user pointer + return (void*)((uint8_t*)user_ptr - 1); +} + +// Convert BASE pointer → USER pointer +static inline void* USER_FROM_BASE(int class_idx, void* base_ptr) +{ + if (!base_ptr) return NULL; + + // Class 0 and C7: no header, BASE == USER + if (class_idx == 0 || class_idx == 7) { + return base_ptr; + } + + // Class 1-6: user pointer is 1 byte after base + return (void*)((uint8_t*)base_ptr + 1); +} + +// ========== SuperSlab Lookup ========== +// +// Find which SuperSlab a pointer belongs to (uses existing hash table) + +static inline SuperSlab* SS_FROM_PTR(void* ptr) +{ + if (!ptr) return NULL; + + // Use existing registry-based lookup (safe, crash-free) + return hak_super_lookup(ptr); +} + +// ========== Slab Index Lookup ========== +// +// Find which slab index within a SuperSlab a pointer belongs to + +static inline int SLAB_IDX_FROM_PTR(SuperSlab* ss, void* ptr) +{ + if (!ss || !ptr) return -1; + + // Delegate to existing slab_index_for() function + // (defined in superslab_inline.h) + return slab_index_for(ss, ptr); +} + +// ========== Slab Data Offset Calculation ========== +// +// Calculate data offset for a slab index within SuperSlab + +static inline size_t SLAB_DATA_OFFSET(SuperSlab* ss, int slab_idx) +{ + if (!ss || slab_idx < 0) return 0; + + // Use existing geometry helper + extern size_t ss_slab_data_offset(SuperSlab* ss, int slab_idx); + return ss_slab_data_offset(ss, slab_idx); +} + +// ========== Block Stride Calculation ========== +// +// Get block stride (total size including header) for a class + +static inline size_t BLOCK_STRIDE(int class_idx) +{ + extern const size_t g_tiny_class_sizes[TINY_NUM_CLASSES]; + + if (class_idx < 0 || class_idx >= TINY_NUM_CLASSES) { + return 0; + } + + return g_tiny_class_sizes[class_idx]; +} + +// ========== Debug Range Validation ========== + +#if !HAKMEM_BUILD_RELEASE +static inline bool VALIDATE_PTR_RANGE(void* ptr, const char* context) +{ + if (!ptr) return false; + + uintptr_t addr = (uintptr_t)ptr; + + // Check for NULL-ish and canonical address range + if (addr < 4096 || addr > 0x00007fffffffffffULL) { + fprintf(stderr, "[GEOMETRY_INVALID_PTR] context=%s ptr=%p\n", + context ? context : "(null)", ptr); + return false; + } + + return true; +} +#else +#define VALIDATE_PTR_RANGE(ptr, context) (1) +#endif + +// ========== Combined Lookup Helpers ========== +// +// Common pattern: ptr → (SuperSlab, slab_idx, meta) + +typedef struct { + SuperSlab* ss; + int slab_idx; + TinySlabMeta* meta; +} PtrGeometry; + +static inline PtrGeometry PTR_CLASSIFY(void* ptr) +{ + PtrGeometry result = {NULL, -1, NULL}; + + if (!VALIDATE_PTR_RANGE(ptr, "PTR_CLASSIFY")) { + return result; + } + + result.ss = SS_FROM_PTR(ptr); + if (!result.ss) { + return result; + } + + result.slab_idx = SLAB_IDX_FROM_PTR(result.ss, ptr); + if (result.slab_idx < 0) { + return result; + } + + result.meta = &result.ss->slabs[result.slab_idx]; + return result; +} + +#endif // HAKMEM_TINY_GEOMETRY_BOX_H diff --git a/core/box/tiny_guard_box.h b/core/box/tiny_guard_box.h new file mode 100644 index 00000000..24575675 --- /dev/null +++ b/core/box/tiny_guard_box.h @@ -0,0 +1,277 @@ +// tiny_guard_box.h - Unified Safety Guards for Tiny Allocator +// Purpose: Centralized validation for TLS push/drain/recycle operations +// License: MIT +// Date: 2025-11-30 (Phase 9-3 unification) +// +// Box Theory Principles: +// - Single Responsibility: All tiny allocator safety validations +// - Clear Contract: true = safe to proceed, false = reject operation +// - Observable: HAKMEM_TINY_GUARD=1 enables all guards + logging +// - Composable: Called from tls_sll_box.h, tls_sll_drain_box.h, slab_recycling_box.h +// +// Phase 9-3 Unification: +// Previously scattered across: +// - tls_sll_guard_box.h (TLS push validation) +// - tls_sll_drain_box.h (drain assertions) +// - slab_recycling_box.h (recycle checks) +// Now unified under single HAKMEM_TINY_GUARD environment variable. +// +// Guards: +// 1. TLS Push Guard: Prevent cross-slab pointer contamination +// 2. TLS Drain Guard: Validate meta->used == 0 before drain +// 3. Slab Recycle Guard: Validate EMPTY state before recycle + +#ifndef HAKMEM_TINY_GUARD_BOX_H +#define HAKMEM_TINY_GUARD_BOX_H + +#include "../hakmem_tiny_superslab_internal.h" +#include "../hakmem_shared_pool.h" +#include "../superslab/superslab_inline.h" +#include "tiny_geometry_box.h" // Phase 9-3: Unified pointer arithmetic +#include +#include + +// ========== TLS SLL Push Guard ========== +// +// Validates that pointer belongs to the expected SuperSlab/slab/class +// before allowing TLS push. Prevents cross-slab contamination. +// +// Parameters: +// class_idx: Expected class index for this TLS freelist +// ptr: Pointer to validate (BASE pointer) +// ss_out: (Optional) Output parameter for validated SuperSlab +// slab_idx_out: (Optional) Output parameter for validated slab index +// +// Returns: +// true = safe to push (all checks passed) +// false = reject push (caller should use slow path / remote push) +// +// Guard Logic: +// 1. SuperSlab lookup: Verify pointer belongs to a valid SuperSlab +// 2. Slab index lookup: Find which slab within SuperSlab contains pointer +// 3. Class validation: Verify slab's class_idx matches expected class +// 4. State validation: Verify slab is ACTIVE (not EMPTY/UNUSED) +// +// Performance: +// - Guard disabled by default in release builds (zero overhead) +// - Debug builds: always enabled +// - Release builds: enable via HAKMEM_TLS_SLL_GUARD=1 +// +// Counters (debug builds only): +// - g_guard_no_ss: Pointer not in any SuperSlab +// - g_guard_no_slab: Pointer not in any slab (invalid address range) +// - g_guard_class_mismatch: Slab class doesn't match expected class +// - g_guard_not_active: Slab state is not ACTIVE + +static inline bool tls_sll_push_guard(int class_idx, void* ptr, SuperSlab** ss_out, int* slab_idx_out) +{ + // Step 0: Guard enable/disable check (cached per thread) + static __thread int s_guard_enabled = -1; + if (__builtin_expect(s_guard_enabled == -1, 0)) { +#if !HAKMEM_BUILD_RELEASE + s_guard_enabled = 1; // Always on in debug builds +#else + const char* e = getenv("HAKMEM_TINY_GUARD"); + s_guard_enabled = (e && *e && *e != '0') ? 1 : 0; +#endif + } + + if (!s_guard_enabled) { + return true; // Guard disabled, always pass + } + + // Step 1: SuperSlab lookup (use unified geometry API) + SuperSlab* ss = SS_FROM_PTR(ptr); + if (!ss) { + // Not in any SuperSlab - reject push +#if !HAKMEM_BUILD_RELEASE + static _Atomic uint32_t g_guard_no_ss = 0; + if (atomic_fetch_add_explicit(&g_guard_no_ss, 1, memory_order_relaxed) < 10) { + fprintf(stderr, "[TLS_SLL_GUARD_NO_SS] cls=%d ptr=%p (rejecting push)\n", + class_idx, ptr); + } +#endif + return false; + } + + // Step 2: Find slab index (use unified geometry API) + int slab_idx = SLAB_IDX_FROM_PTR(ss, ptr); + if (slab_idx < 0) { +#if !HAKMEM_BUILD_RELEASE + static _Atomic uint32_t g_guard_no_slab = 0; + if (atomic_fetch_add_explicit(&g_guard_no_slab, 1, memory_order_relaxed) < 10) { + fprintf(stderr, "[TLS_SLL_GUARD_NO_SLAB] cls=%d ptr=%p ss=%p (rejecting push)\n", + class_idx, ptr, (void*)ss); + } +#endif + return false; + } + + // Step 3: Validate class match + TinySlabMeta* meta = &ss->slabs[slab_idx]; + if (meta->class_idx != (uint8_t)class_idx) { + // Class mismatch - slab was reused for different class +#if !HAKMEM_BUILD_RELEASE + static _Atomic uint32_t g_guard_class_mismatch = 0; + if (atomic_fetch_add_explicit(&g_guard_class_mismatch, 1, memory_order_relaxed) < 10) { + fprintf(stderr, "[TLS_SLL_GUARD_CLASS_MISMATCH] cls=%d ptr=%p slab_cls=%d ss=%p slab=%d (rejecting push)\n", + class_idx, ptr, meta->class_idx, (void*)ss, slab_idx); + } +#endif + return false; + } + + // Step 4: Validate slab is ACTIVE (not EMPTY/UNUSED) + // Use external function to find SharedSSMeta for this SuperSlab + extern SharedSSMeta* sp_find_meta_for_ss(SuperSlab* ss); + SharedSSMeta* sp_meta = sp_find_meta_for_ss(ss); + if (sp_meta) { + SlotState state = atomic_load_explicit(&sp_meta->slots[slab_idx].state, memory_order_acquire); + if (state != SLOT_ACTIVE) { +#if !HAKMEM_BUILD_RELEASE + static _Atomic uint32_t g_guard_not_active = 0; + if (atomic_fetch_add_explicit(&g_guard_not_active, 1, memory_order_relaxed) < 10) { + fprintf(stderr, "[TLS_SLL_GUARD_NOT_ACTIVE] cls=%d ptr=%p state=%d ss=%p slab=%d (rejecting push)\n", + class_idx, ptr, state, (void*)ss, slab_idx); + } +#endif + return false; + } + } + + // All checks passed - safe to push + if (ss_out) *ss_out = ss; + if (slab_idx_out) *slab_idx_out = slab_idx; + return true; +} + +// ========== TLS SLL Drain Guard ========== +// +// Validates that slab is truly empty (meta->used == 0) before draining +// TLS freelist back to the slab. +// +// Parameters: +// class_idx: Class index for logging +// ss: SuperSlab pointer +// slab_idx: Slab index within SuperSlab +// meta: Slab metadata pointer +// +// Returns: +// true = safe to drain (used == 0) +// false = reject drain (used != 0, potential corruption) +// +// Counter (debug builds only): +// - g_guard_drain_used: Drain attempts with non-zero used count + +static inline bool tiny_guard_drain_check(int class_idx, SuperSlab* ss, int slab_idx, TinySlabMeta* meta) +{ + static __thread int s_guard_enabled = -1; + if (__builtin_expect(s_guard_enabled == -1, 0)) { +#if !HAKMEM_BUILD_RELEASE + s_guard_enabled = 1; // Always on in debug builds +#else + const char* e = getenv("HAKMEM_TINY_GUARD"); + s_guard_enabled = (e && *e && *e != '0') ? 1 : 0; +#endif + } + + if (!s_guard_enabled) return true; + + // Check meta->used == 0 + uint16_t used = atomic_load_explicit(&meta->used, memory_order_relaxed); + if (used != 0) { +#if !HAKMEM_BUILD_RELEASE + static _Atomic uint32_t g_guard_drain_used = 0; + if (atomic_fetch_add_explicit(&g_guard_drain_used, 1, memory_order_relaxed) < 10) { + fprintf(stderr, "[TINY_GUARD_DRAIN_USED] cls=%d ss=%p slab=%d used=%u (expected 0)\n", + class_idx, (void*)ss, slab_idx, used); + } +#endif + return false; + } + + return true; +} + +// ========== Slab Recycle Guard ========== +// +// Validates that slab is truly EMPTY before recycling to another class. +// +// Parameters: +// ss: SuperSlab pointer +// slab_idx: Slab index within SuperSlab +// meta: Slab metadata pointer +// +// Returns: +// true = safe to recycle (used == 0 && capacity > 0) +// false = reject recycle (invalid state) +// +// Counters (debug builds only): +// - g_guard_recycle_used: Recycle attempts with non-zero used count +// - g_guard_recycle_no_cap: Recycle attempts with zero capacity + +static inline bool tiny_guard_recycle_check(SuperSlab* ss, int slab_idx, TinySlabMeta* meta) +{ + static __thread int s_guard_enabled = -1; + if (__builtin_expect(s_guard_enabled == -1, 0)) { +#if !HAKMEM_BUILD_RELEASE + s_guard_enabled = 1; // Always on in debug builds +#else + const char* e = getenv("HAKMEM_TINY_GUARD"); + s_guard_enabled = (e && *e && *e != '0') ? 1 : 0; +#endif + } + + if (!s_guard_enabled) return true; + + // Check used == 0 + uint16_t used = atomic_load_explicit(&meta->used, memory_order_relaxed); + if (used != 0) { +#if !HAKMEM_BUILD_RELEASE + static _Atomic uint32_t g_guard_recycle_used = 0; + if (atomic_fetch_add_explicit(&g_guard_recycle_used, 1, memory_order_relaxed) < 10) { + fprintf(stderr, "[TINY_GUARD_RECYCLE_USED] ss=%p slab=%d used=%u (expected 0)\n", + (void*)ss, slab_idx, used); + } +#endif + return false; + } + + // Check capacity > 0 + if (meta->capacity == 0) { +#if !HAKMEM_BUILD_RELEASE + static _Atomic uint32_t g_guard_recycle_no_cap = 0; + if (atomic_fetch_add_explicit(&g_guard_recycle_no_cap, 1, memory_order_relaxed) < 10) { + fprintf(stderr, "[TINY_GUARD_RECYCLE_NO_CAP] ss=%p slab=%d cap=0\n", + (void*)ss, slab_idx); + } +#endif + return false; + } + + return true; +} + +// ========== Unified Guard Enable Check ========== +// +// Single source of truth for guard enable/disable state. +// +// Returns: +// 1 = guards enabled +// 0 = guards disabled + +static inline int tiny_guard_enabled(void) +{ + static __thread int s_guard_enabled = -1; + if (__builtin_expect(s_guard_enabled == -1, 0)) { +#if !HAKMEM_BUILD_RELEASE + s_guard_enabled = 1; // Always on in debug builds +#else + const char* e = getenv("HAKMEM_TINY_GUARD"); + s_guard_enabled = (e && *e && *e != '0') ? 1 : 0; +#endif + } + return s_guard_enabled; +} + +#endif // HAKMEM_TINY_GUARD_BOX_H