diff --git a/core/box/ss_allocation_box.c b/core/box/ss_allocation_box.c index 484a9410..269a3579 100644 --- a/core/box/ss_allocation_box.c +++ b/core/box/ss_allocation_box.c @@ -408,7 +408,14 @@ void superslab_init_slab(SuperSlab* ss, int slab_idx, size_t block_size, uint32_ meta->capacity = capacity; meta->carved = 0; meta->owner_tid_low = (uint8_t)(owner_tid & 0xFFu); - // meta->class_idx is set by the caller (shared_pool / refill path) + // Fail-safe: stamp class_idx from geometry (stride → class). + // This normalizes both legacy and shared pool paths. + for (int i = 0; i < TINY_NUM_CLASSES; i++) { + if (g_tiny_class_sizes[i] == stride) { + meta->class_idx = (uint8_t)i; + break; + } + } superslab_activate_slab(ss, slab_idx); } diff --git a/core/box/ss_legacy_backend_box.c b/core/box/ss_legacy_backend_box.c index 57033bcb..1667f126 100644 --- a/core/box/ss_legacy_backend_box.c +++ b/core/box/ss_legacy_backend_box.c @@ -125,6 +125,11 @@ void* hak_tiny_alloc_superslab_backend_legacy(int class_idx) for (int slab_idx = 0; slab_idx < cap; slab_idx++) { TinySlabMeta* meta = &chunk->slabs[slab_idx]; + // Skip slabs that belong to a different class (or are uninitialized). + if (meta->class_idx != (uint8_t)class_idx) { + continue; + } + if (meta->capacity == 0) { continue; } @@ -270,6 +275,10 @@ int expand_superslab_head(SuperSlabHead* head) { superslab_init_slab(new_chunk, 0, block_size, owner_tid); + // CRITICAL FIX: Explicitly set class_idx to avoid C0/C7 confusion. + // New SuperSlabs start with meta->class_idx=0 (mmap zero-init). + new_chunk->slabs[0].class_idx = (uint8_t)head->class_idx; + // Initialize the next_chunk link to NULL new_chunk->next_chunk = NULL; diff --git a/core/box/ss_unified_backend_box.c b/core/box/ss_unified_backend_box.c index cad27ff3..7151c874 100644 --- a/core/box/ss_unified_backend_box.c +++ b/core/box/ss_unified_backend_box.c @@ -64,11 +64,10 @@ void* hak_tiny_alloc_superslab_backend_shared(int class_idx) superslab_init_slab(ss, slab_idx, block_size, 0); meta = &ss->slabs[slab_idx]; - // Ensure class_idx is bound to this class after init. superslab_init_slab - // does not touch class_idx by design; shared_pool owns that field. - if (meta->class_idx == 255) { - meta->class_idx = (uint8_t)class_idx; - } + // CRITICAL FIX: Always set class_idx after init to avoid C0/C7 confusion. + // New SuperSlabs start with meta->class_idx=0 (mmap zero-init). + // Must explicitly set to requested class, not just when class_idx==255. + meta->class_idx = (uint8_t)class_idx; } // Final contract check before computing addresses. diff --git a/core/box/superslab_expansion_box.c b/core/box/superslab_expansion_box.c index 95f4aca4..5a400f92 100644 --- a/core/box/superslab_expansion_box.c +++ b/core/box/superslab_expansion_box.c @@ -67,6 +67,10 @@ ExpansionResult expansion_expand_with_tls_guarantee( size_t block_size = g_tiny_class_sizes[class_idx]; superslab_init_slab(new_ss, 0, block_size, my_tid); + // CRITICAL FIX: Explicitly set class_idx to avoid C0/C7 confusion. + // New SuperSlabs start with meta->class_idx=0 (mmap zero-init). + new_ss->slabs[0].class_idx = (uint8_t)class_idx; + // Now bind slab 0 to TLS state result.new_state.ss = new_ss; result.new_state.class_idx = class_idx; @@ -78,6 +82,14 @@ ExpansionResult expansion_expand_with_tls_guarantee( // Formula: base = ss + (slab_idx * SLAB_SIZE) + (slab_idx == 0 ? SLAB0_OFFSET : 0) result.new_state.slab_base = (uint8_t*)new_ss + SUPERSLAB_SLAB0_DATA_OFFSET; + // Debug: log backend used for expansion (first few only) + static _Atomic uint32_t g_ss_backend_log = 0; + uint32_t n = atomic_fetch_add_explicit(&g_ss_backend_log, 1, memory_order_relaxed); + if (n < 4) { + fprintf(stderr, "[SS_BACKEND] expand legacy cls=%d ss=%p slab_idx=0 base=%p\n", + class_idx, (void*)new_ss, result.new_state.slab_base); + } + result.success = true; result.error_code = 0; diff --git a/core/box/tiny_next_ptr_box.h b/core/box/tiny_next_ptr_box.h index 39b78b6f..a463e1b3 100644 --- a/core/box/tiny_next_ptr_box.h +++ b/core/box/tiny_next_ptr_box.h @@ -13,8 +13,7 @@ * * HAKMEM_TINY_HEADER_CLASSIDX != 0: * - Class 0: next_off = 0 (free中は header を潰す) - * - Class 1-6: next_off = 1 - * - Class 7: next_off = 0 + * - Class 1-7: next_off = 1 (headerを保持) * * HAKMEM_TINY_HEADER_CLASSIDX == 0: * - 全クラス: next_off = 0 diff --git a/core/hakmem_tiny.h b/core/hakmem_tiny.h index 68b838a6..00068ea6 100644 --- a/core/hakmem_tiny.h +++ b/core/hakmem_tiny.h @@ -25,7 +25,7 @@ int hak_is_initializing(void); #define TINY_SLAB_SIZE (64 * 1024) // 64KB per slab // Phase E1-CORRECT: All Tiny classes use a 1-byte header. // C7 stride=1024B → usable 1023B (1024-1). 1024B は Mid allocator に委譲する。 -#define TINY_MAX_SIZE 1023 // Tiny handles up to 1023B (C7 usable size) - default +#define TINY_MAX_SIZE 1024 // Tiny handles up to 1024B (C7 total size) - default // Phase 16: Dynamic Tiny max size control (ENV: HAKMEM_TINY_MAX_CLASS) // Strategy: Reduce Tiny coverage to ~256B, delegate 512/1024B to Mid @@ -68,8 +68,9 @@ typedef struct { // Box 3 (tiny_box_geometry.h) uses this via hakmem_tiny_config.h // (Definition removed from header - see hakmem_tiny.c) -// Full LUT (1..1024) for branchless size-to-class mapping (index by size). -// Memory cost ~1KB. Zero hot-path arithmetic for all Tiny sizes. +// Full LUT (1..2048) for branchless size-to-class mapping (index by size). +// Phase C7-UPGRADE: Expanded from 1025 -> 2049 to support 2048B stride (C7). +// Memory cost ~2KB. Zero hot-path arithmetic for all Tiny sizes. // Generate repeated values via helper macros to keep the source compact. #define HAK_R1(x) x #define HAK_R2(x) HAK_R1(x), HAK_R1(x) @@ -81,8 +82,9 @@ typedef struct { #define HAK_R128(x) HAK_R64(x), HAK_R64(x) #define HAK_R256(x) HAK_R128(x), HAK_R128(x) #define HAK_R512(x) HAK_R256(x), HAK_R256(x) +#define HAK_R1024(x) HAK_R512(x), HAK_R512(x) -static const int8_t g_size_to_class_lut_1k[1025] = { +static const int8_t g_size_to_class_lut_2k[2049] = { -1, // index 0: invalid HAK_R8(0), // 1..8 -> class 0 HAK_R8(1), // 9..16 -> class 1 @@ -91,9 +93,11 @@ static const int8_t g_size_to_class_lut_1k[1025] = { HAK_R64(4), // 65..128 -> class 4 HAK_R128(5), // 129..256 -> class 5 HAK_R256(6), // 257..512 -> class 6 - HAK_R512(7), // 513..1024 -> class 7 + HAK_R1024(7), // 513..1536 -> class 7 (1024 entries) + HAK_R512(7), // 1537..2048 -> class 7 (512 entries) }; +#undef HAK_R1024 #undef HAK_R512 #undef HAK_R256 #undef HAK_R128 @@ -114,7 +118,7 @@ static const uint16_t g_tiny_blocks_per_slab[TINY_NUM_CLASSES] = { 512, // Class 4: 64KB / 128B = 512 blocks 256, // Class 5: 64KB / 256B = 256 blocks 128, // Class 6: 64KB / 512B = 128 blocks - 64 // Class 7: 64KB / 1024B = 64 blocks + 32 // Class 7: 64KB / 2048B = 32 blocks }; // Bitmap size (uint64_t words) for each class @@ -267,26 +271,26 @@ void hkm_ace_set_drain_threshold(int class_idx, uint32_t threshold); // ============================================================================ // Convert size to class index (branchless lookup) -// Phase E1-CORRECT: ALL classes have 1-byte header -// C7 max usable: 1023B (1024B total with header) -// malloc(1024+) → routed to Mid allocator +// Phase C7-UPGRADE: ALL classes have 1-byte header +// C7 max usable: 2047B (2048B total with header) +// malloc(2048+) → routed to Mid allocator static inline int hak_tiny_size_to_class(size_t size) { if (size == 0) return -1; #if HAKMEM_TINY_HEADER_CLASSIDX - // Phase E1-CORRECT: ALL classes have 1-byte header + // Phase C7-UPGRADE: ALL classes have 1-byte header // Box: [Header 1B][Data NB] = (N+1) bytes total // g_tiny_class_sizes stores TOTAL size, so we need size+1 bytes // User requests N bytes → need (N+1) total → look up class with stride ≥ (N+1) - // Max usable: 1023B (C7 stride=1024B) - if (size > 1023) return -1; // 1024+ → Mid allocator + // Max usable: 2047B (C7 stride=2048B) + if (size > 2047) return -1; // 2048+ → Mid allocator // Find smallest class where stride ≥ (size + 1) // LUT maps total_size → class, so lookup (size + 1) to find class with that stride size_t needed = size + 1; // total bytes needed (data + header) - if (needed > 1024) return -1; - return g_size_to_class_lut_1k[needed]; + if (needed > 2048) return -1; + return g_size_to_class_lut_2k[needed]; #else if (size > 1024) return -1; - return g_size_to_class_lut_1k[size]; // 1..1024 + return g_size_to_class_lut_2k[size]; // 1..1024 #endif } diff --git a/core/hakmem_tiny_config_box.inc b/core/hakmem_tiny_config_box.inc index 5907a16c..65e8b130 100644 --- a/core/hakmem_tiny_config_box.inc +++ b/core/hakmem_tiny_config_box.inc @@ -16,7 +16,7 @@ const size_t g_tiny_class_sizes[TINY_NUM_CLASSES] = { 128, // Class 4: 128B total = [Header 1B][Data 127B] 256, // Class 5: 256B total = [Header 1B][Data 255B] 512, // Class 6: 512B total = [Header 1B][Data 511B] - 1024 // Class 7: 1024B total = [Header 1B][Data 1023B] + 2048 // Class 7: 2048B total = [Header 1B][Data 2047B] (upgraded for 1024B requests) }; // ============================================================================ @@ -54,9 +54,9 @@ size_t tiny_get_max_size(void) { } // Map class to max usable size (stride - 1) - // C0=8B, C1=16B, C2=32B, C3=64B, C4=128B, C5=256B, C6=512B, C7=1024B + // C0=8B, C1=16B, C2=32B, C3=64B, C4=128B, C5=256B, C6=512B, C7=2048B static const size_t class_to_max_size[TINY_NUM_CLASSES] = { - 7, 15, 31, 63, 127, 255, 511, 1023 + 7, 15, 31, 63, 127, 255, 511, 2047 }; return class_to_max_size[effective_class]; } diff --git a/core/hakmem_tiny_refill_p0.inc.h b/core/hakmem_tiny_refill_p0.inc.h index 948ba815..21b5e6f2 100644 --- a/core/hakmem_tiny_refill_p0.inc.h +++ b/core/hakmem_tiny_refill_p0.inc.h @@ -1,6 +1,9 @@ #ifndef HAKMEM_TINY_REFILL_P0_INC_H #define HAKMEM_TINY_REFILL_P0_INC_H +#include +#include + // hakmem_tiny_refill_p0.inc.h // P0: Batch refill implementation (sll_refill_batch_from_ss only). // Phase 12: DO NOT alias or redefine sll_refill_small_from_ss here. @@ -8,6 +11,7 @@ #if HAKMEM_TINY_P0_BATCH_REFILL +#include "hakmem_tiny_integrity.h" #include "tiny_box_geometry.h" // Box 3: Geometry & Capacity Calculator #include "tiny_refill_opt.h" #include "tiny_fc_api.h" @@ -50,6 +54,15 @@ static inline int sll_refill_batch_from_ss(int class_idx, int max_take) { } } while (0); + HAK_CHECK_CLASS_IDX(class_idx, "sll_refill_batch_from_ss"); + if (__builtin_expect(class_idx < 0 || class_idx >= TINY_NUM_CLASSES, 0)) { + static _Atomic int g_p0_class_oob_log = 0; + if (atomic_fetch_add_explicit(&g_p0_class_oob_log, 1, memory_order_relaxed) == 0) { + fprintf(stderr, "[P0_CLASS_OOB] class_idx=%d max_take=%d\n", class_idx, max_take); + } + return 0; + } + if (!g_use_superslab || max_take <= 0) { #if HAKMEM_DEBUG_COUNTERS if (!g_use_superslab) g_rf_early_no_ss[class_idx]++; @@ -179,7 +192,7 @@ static inline int sll_refill_batch_from_ss(int class_idx, int max_take) { } while (0); uint32_t sll_cap = sll_cap_for_class(class_idx, (uint32_t)TINY_TLS_MAG_CAP); - int room = (int)sll_cap - (int)g_tls_sll_count[class_idx]; + int room = (int)sll_cap - (int)g_tls_sll[class_idx].count; if (room <= 0) { #if HAKMEM_DEBUG_COUNTERS g_rf_early_no_room[class_idx]++; @@ -230,8 +243,8 @@ static inline int sll_refill_batch_from_ss(int class_idx, int max_take) { if (from_freelist > 0) { trc_splice_to_sll( class_idx, &chain, - &g_tls_sll_head[class_idx], - &g_tls_sll_count[class_idx]); + &g_tls_sll[class_idx].head, + &g_tls_sll[class_idx].count); ss_active_add(tls->ss, from_freelist); meta->used = (uint16_t)((uint32_t)meta->used + from_freelist); diff --git a/core/hakmem_tiny_superslab.c b/core/hakmem_tiny_superslab.c index 1f4efd01..0a2fec58 100644 --- a/core/hakmem_tiny_superslab.c +++ b/core/hakmem_tiny_superslab.c @@ -21,6 +21,7 @@ #include #include "hakmem_internal.h" // HAKMEM_LOG for release-silent logging #include "tiny_region_id.h" // For HEADER_MAGIC / HEADER_CLASS_MASK (restore header on remote-drain) +#include "hakmem_tiny_integrity.h" // HAK_CHECK_CLASS_IDX #include "box/tiny_next_ptr_box.h" // For tiny_next_write static int g_ss_force_lg = -1; @@ -111,6 +112,9 @@ void _ss_remote_drain_to_freelist_unsafe(SuperSlab* ss, int slab_idx, TinySlabMe { if (!ss || slab_idx < 0 || slab_idx >= ss_slabs_capacity(ss) || !meta) return; + static _Atomic uint32_t g_remote_drain_diag_once = 0; + static int g_remote_drain_diag_en = -1; + // Atomically take the whole remote list uintptr_t head = atomic_exchange_explicit(&ss->remote_heads[slab_idx], 0, memory_order_acq_rel); @@ -120,12 +124,69 @@ void _ss_remote_drain_to_freelist_unsafe(SuperSlab* ss, int slab_idx, TinySlabMe // and splice in front of current freelist preserving relative order. void* prev = meta->freelist; int cls = (int)meta->class_idx; + HAK_CHECK_CLASS_IDX(cls, "_ss_remote_drain_to_freelist_unsafe"); + if (__builtin_expect(cls < 0 || cls >= TINY_NUM_CLASSES, 0)) { + static _Atomic int g_remote_drain_cls_oob = 0; + if (atomic_fetch_add_explicit(&g_remote_drain_cls_oob, 1, memory_order_relaxed) == 0) { + fprintf(stderr, + "[REMOTE_DRAIN_CLASS_OOB] ss=%p slab_idx=%d meta=%p cls=%d head=%#lx\n", + (void*)ss, slab_idx, (void*)meta, cls, (unsigned long)head); + } + return; + } uintptr_t cur = head; while (cur != 0) { uintptr_t next = *(uintptr_t*)cur; // remote-next stored at offset 0 + if (__builtin_expect(g_remote_drain_diag_en == -1, 0)) { + const char* e = getenv("HAKMEM_TINY_SLL_DIAG"); + g_remote_drain_diag_en = (e && *e && *e != '0') ? 1 : 0; + } + if (__builtin_expect(g_remote_drain_diag_en, 0)) { + uintptr_t addr = (uintptr_t)next; + if (addr != 0 && (addr < 4096 || addr > 0x00007fffffffffffULL)) { + uint32_t shot = atomic_fetch_add_explicit(&g_remote_drain_diag_once, 1, memory_order_relaxed); + if (shot < 8) { + fprintf(stderr, + "[REMOTE_DRAIN_NEXT_INVALID] cls=%d slab=%d cur=%p next=%p head=%#lx prev=%p count=%u\n", + cls, + slab_idx, + (void*)cur, + (void*)next, + (unsigned long)head, + prev, + (unsigned)meta->used); + } + } +#if HAKMEM_TINY_HEADER_CLASSIDX + int hdr_cls = tiny_region_id_read_header((uint8_t*)cur + 1); + if (hdr_cls >= 0 && hdr_cls != cls) { + uint32_t shot = atomic_fetch_add_explicit(&g_remote_drain_diag_once, 1, memory_order_relaxed); + if (shot < 8) { + fprintf(stderr, + "[REMOTE_DRAIN_HDR_MISMATCH] cls=%d slab=%d cur=%p hdr_cls=%d meta_cls=%d head=%#lx\n", + cls, slab_idx, (void*)cur, hdr_cls, (int)meta->class_idx, (unsigned long)head); + } + } +#endif + } +#if HAKMEM_TINY_HEADER_CLASSIDX + // Cross-check header vs meta before writing next (even if diag is off) + { + int hdr_cls_pre = tiny_region_id_read_header((uint8_t*)cur + 1); + if (hdr_cls_pre >= 0 && hdr_cls_pre != cls) { + static _Atomic uint32_t g_hdr_meta_mismatch_rd = 0; + uint32_t n = atomic_fetch_add_explicit(&g_hdr_meta_mismatch_rd, 1, memory_order_relaxed); + if (n < 16) { + fprintf(stderr, + "[REMOTE_DRAIN_HDR_META_MISMATCH] cls=%d slab=%d cur=%p hdr_cls=%d meta_cls=%d\n", + cls, slab_idx, (void*)cur, hdr_cls_pre, (int)meta->class_idx); + } + } + } +#endif // Restore header for header-classes (class 1-6) which were clobbered by remote push #if HAKMEM_TINY_HEADER_CLASSIDX - if (cls != 0 && cls != 7) { + if (cls != 0) { uint8_t expected = (uint8_t)(HEADER_MAGIC | (cls & HEADER_CLASS_MASK)); *(uint8_t*)(uintptr_t)cur = expected; } @@ -432,6 +493,11 @@ static void* hak_tiny_alloc_superslab_backend_legacy(int class_idx) for (int slab_idx = 0; slab_idx < cap; slab_idx++) { TinySlabMeta* meta = &chunk->slabs[slab_idx]; + // Skip slabs that belong to a different class (or are uninitialized). + if (meta->class_idx != (uint8_t)class_idx) { + continue; + } + if (meta->capacity == 0) { continue; } @@ -532,11 +598,10 @@ static void* hak_tiny_alloc_superslab_backend_shared(int class_idx) superslab_init_slab(ss, slab_idx, block_size, 0); meta = &ss->slabs[slab_idx]; - // Ensure class_idx is bound to this class after init. superslab_init_slab - // does not touch class_idx by design; shared_pool owns that field. - if (meta->class_idx == 255) { - meta->class_idx = (uint8_t)class_idx; - } + // CRITICAL FIX: Always set class_idx after init to avoid C0/C7 confusion. + // New SuperSlabs start with meta->class_idx=0 (mmap zero-init). + // Must explicitly set to requested class, not just when class_idx==255. + meta->class_idx = (uint8_t)class_idx; } // Final contract check before computing addresses. @@ -590,6 +655,7 @@ static void* hak_tiny_alloc_superslab_backend_shared(int class_idx) void* hak_tiny_alloc_superslab_box(int class_idx) { static int g_ss_shared_mode = -1; + static _Atomic uint32_t g_ss_backend_log = 0; if (__builtin_expect(g_ss_shared_mode == -1, 0)) { const char* e = getenv("HAKMEM_TINY_SS_SHARED"); if (!e || !*e) { @@ -603,13 +669,25 @@ void* hak_tiny_alloc_superslab_box(int class_idx) if (g_ss_shared_mode == 1) { void* p = hak_tiny_alloc_superslab_backend_shared(class_idx); if (p != NULL) { + uint32_t n = atomic_fetch_add_explicit(&g_ss_backend_log, 1, memory_order_relaxed); + if (n < 4) { + fprintf(stderr, "[SS_BACKEND] shared cls=%d ptr=%p\n", class_idx, p); + } return p; } // shared backend が失敗した場合は安全側で legacy にフォールバック + uint32_t n = atomic_fetch_add_explicit(&g_ss_backend_log, 1, memory_order_relaxed); + if (n < 4) { + fprintf(stderr, "[SS_BACKEND] shared_fail→legacy cls=%d\n", class_idx); + } return hak_tiny_alloc_superslab_backend_legacy(class_idx); } // shared OFF 時は legacy のみ + uint32_t n = atomic_fetch_add_explicit(&g_ss_backend_log, 1, memory_order_relaxed); + if (n < 4) { + fprintf(stderr, "[SS_BACKEND] legacy cls=%d\n", class_idx); + } return hak_tiny_alloc_superslab_backend_legacy(class_idx); } @@ -1108,7 +1186,14 @@ void superslab_init_slab(SuperSlab* ss, int slab_idx, size_t block_size, uint32_ meta->capacity = capacity; meta->carved = 0; meta->owner_tid_low = (uint8_t)(owner_tid & 0xFFu); - // meta->class_idx is set by the caller (shared_pool / refill path) + // Fail-safe: stamp class_idx from geometry (stride → class). + // This ensures legacy/shared/legacy-refill paths all end with a correct class. + for (int i = 0; i < TINY_NUM_CLASSES; i++) { + if (g_tiny_class_sizes[i] == stride) { + meta->class_idx = (uint8_t)i; + break; + } + } superslab_activate_slab(ss, slab_idx); } diff --git a/core/tiny_nextptr.h b/core/tiny_nextptr.h index 94dc6df4..d69214fb 100644 --- a/core/tiny_nextptr.h +++ b/core/tiny_nextptr.h @@ -16,8 +16,9 @@ // → next_off = 1 // // Class 7: -// 大きなクラス、互換性と実装方針により next は base+0 扱い -// → next_off = 0 +// [1B header][payload 2047B] +// → C7アップグレード後も header保持、next は base+1 に格納 +// → next_off = 1 // // HAKMEM_TINY_HEADER_CLASSIDX == 0 のとき: // @@ -34,14 +35,22 @@ #include #include #include "hakmem_build_flags.h" +#include "tiny_region_id.h" // HEADER_MAGIC/HEADER_CLASS_MASK for header repair/logging +#include "hakmem_super_registry.h" // hak_super_lookup +#include "superslab/superslab_inline.h" // slab_index_for +#include +#include +#include +#include // backtrace for rare misalign diagnostics // Compute freelist next-pointer offset within a block for the given class. static inline __attribute__((always_inline)) size_t tiny_next_off(int class_idx) { #if HAKMEM_TINY_HEADER_CLASSIDX - // Phase E1-CORRECT finalized rule: - // Class 0,7 → offset 0 - // Class 1-6 → offset 1 - return (class_idx == 0 || class_idx == 7) ? 0u : 1u; + // Phase E1-CORRECT REVISED (C7 corruption fix): + // Class 0 → offset 0 (8B block、header後に8Bポインタは入らない) + // Class 1-7 → offset 1 (header保持、nextはheader直後) + // C7も header を保持して class 判別を壊さないことを優先 + return (class_idx == 0) ? 0u : 1u; #else (void)class_idx; return 0u; @@ -68,6 +77,69 @@ static inline __attribute__((always_inline)) void* tiny_next_load(const void* ba static inline __attribute__((always_inline)) void tiny_next_store(void* base, int class_idx, void* next) { size_t off = tiny_next_off(class_idx); +#if HAKMEM_TINY_HEADER_CLASSIDX + if (class_idx != 0) { + uint8_t expected = (uint8_t)(HEADER_MAGIC | (class_idx & HEADER_CLASS_MASK)); + uint8_t got = *(uint8_t*)base; + if (__builtin_expect(got != expected, 0)) { + static _Atomic uint32_t g_next_hdr_diag = 0; + uint32_t n = atomic_fetch_add_explicit(&g_next_hdr_diag, 1, memory_order_relaxed); + if (n < 16) { + fprintf(stderr, "[NXT_HDR_MISMATCH] cls=%d base=%p got=0x%02x expect=0x%02x\n", + class_idx, base, got, expected); + } + } + *(uint8_t*)base = expected; // Always restore header before writing next + } +#endif + + // Misalignment detector: class stride vs base offset + do { + static _Atomic uint32_t g_next_misalign_log = 0; + extern const size_t g_tiny_class_sizes[]; + size_t stride = (class_idx >= 0 && class_idx < 8) ? g_tiny_class_sizes[class_idx] : 0; + if (stride > 0) { + uintptr_t delta = ((uintptr_t)base) % stride; + if (__builtin_expect(delta != 0, 0)) { + void* ra = __builtin_return_address(0); + const char* sym = "(unknown)"; +#ifdef __GLIBC__ + do { + Dl_info info; + if (dladdr(ra, &info) && info.dli_sname) { + sym = info.dli_sname; + } + } while (0); +#endif + uint32_t n = atomic_fetch_add_explicit(&g_next_misalign_log, 1, memory_order_relaxed); + int meta_cls = -1; + int slab_idx = -1; + struct SuperSlab* ss = NULL; + if (class_idx >= 0 && class_idx < 8) { + ss = hak_super_lookup(base); + if (ss) { + slab_idx = slab_index_for(ss, base); + if (slab_idx >= 0) { + struct TinySlabMeta* m = &ss->slabs[slab_idx]; + meta_cls = m->class_idx; + } + } + } + if (n < 16) { + fprintf(stderr, + "[NXT_MISALIGN] cls=%d base=%p stride=%zu delta_mod=%zu next=%p ra=%p fn=%s meta_cls=%d slab_idx=%d ss=%p\n", + class_idx, base, stride, (size_t)delta, next, ra, sym, meta_cls, slab_idx, (void*)ss); + if (n < 4) { + void* bt[8]; + int frames = backtrace(bt, 8); + backtrace_symbols_fd(bt, frames, fileno(stderr)); + } + fflush(stderr); + } + } + } + } while (0); + if (off == 0) { // Aligned access at base. *(void**)base = next; diff --git a/core/tiny_superslab_alloc.inc.h b/core/tiny_superslab_alloc.inc.h index 119a9b55..9a9ec60c 100644 --- a/core/tiny_superslab_alloc.inc.h +++ b/core/tiny_superslab_alloc.inc.h @@ -127,6 +127,14 @@ static inline void* superslab_alloc_from_slab(SuperSlab* ss, int slab_idx) { fprintf(stderr, "[ALLOC_POP] cls=%u slab=%d block=%p offset=%zu (used=%u cap=%u)\n", meta->class_idx, slab_idx, block, offset, meta->used, meta->capacity); + // Misaligned freelist entry → drop this slab's freelist to force new slab. + if ((offset % blk) != 0) { + fprintf(stderr, "[ALLOC_POP_MISALIGN] cls=%u slab=%d offset_mod=%zu blk=%zu base=%p ss=%p\n", + meta->class_idx, slab_idx, (size_t)(offset % blk), blk, block, (void*)ss); + meta->freelist = NULL; + return NULL; + } + if (offset % blk != 0 || offset / blk >= meta->capacity) { fprintf(stderr, "[ALLOC_CORRUPT] Freelist head invalid\n"); @@ -206,6 +214,22 @@ SuperSlab* superslab_refill(int class_idx) g_tiny_class_sizes[class_idx], my_tid); + // CRITICAL FIX: Ensure class_idx is set after init. + // New SuperSlabs start with meta->class_idx=0 (mmap zero-init). + // superslab_init_slab() only sets it if meta->class_idx==255. + // We must explicitly set it to the requested class to avoid C0/C7 confusion. + TinySlabMeta* meta = &ss->slabs[slab_idx]; +#if !HAKMEM_BUILD_RELEASE + uint8_t old_cls = meta->class_idx; +#endif + meta->class_idx = (uint8_t)class_idx; +#if !HAKMEM_BUILD_RELEASE + if (class_idx == 7 && old_cls != class_idx) { + fprintf(stderr, "[SUPERSLAB_REFILL_FIX_C7] ss=%p slab=%d old_cls=%u new_cls=%d\n", + (void*)ss, slab_idx, old_cls, class_idx); + } +#endif + // Bind this slab to TLS for fast subsequent allocations. // tiny_tls_bind_slab は: // tls->ss, tls->slab_idx, tls->meta, tls->slab_base