diff --git a/core/box/fg_tiny_gate_box.h b/core/box/fg_tiny_gate_box.h new file mode 100644 index 00000000..46ad1d32 --- /dev/null +++ b/core/box/fg_tiny_gate_box.h @@ -0,0 +1,46 @@ +// fg_tiny_gate_box.h - Tiny Front Gate guard box +// 役割: FG_DOMAIN_TINY 判定が出た時に Superslab 登録を確認し、誤分類なら MIDCAND に切替 +// 境界: Tiny 経路へ入る前に 1 箇所でのみ実行する +#ifndef HAK_BOX_FG_TINY_GATE_H +#define HAK_BOX_FG_TINY_GATE_H + +#include "../hakmem_super_registry.h" // hak_super_lookup / SUPERSLAB_MAGIC +#include "front_gate_v2.h" +#include +#include + +typedef struct { + fg_classification_t fg; + int misclassified; // 1 when Tiny→MIDCAND に落とした +} fg_tiny_gate_result_t; + +// 簡易ログ(初回のみ) +static inline void fg_tiny_gate_log(void* ptr, uint8_t hdr, uintptr_t off, int use_ss, int reg_init) { + static _Atomic int log_budget = 4; + int n = atomic_fetch_add_explicit(&log_budget, 1, memory_order_relaxed); + if (n >= 4) return; + fprintf(stderr, + "[FG_TINY_MISCLASS] ptr=%p hdr=0x%02x off=0x%lx g_use_ss=%d reg_init=%d → reroute to MIDCAND\n", + ptr, hdr, (unsigned long)off, use_ss, reg_init); +} + +// Tiny判定のガード: Superslab登録が無ければ MIDCAND に変換 +static inline fg_tiny_gate_result_t fg_tiny_gate(void* ptr, fg_classification_t fg) { + fg_tiny_gate_result_t r = {.fg = fg, .misclassified = 0}; + if (fg.domain != FG_DOMAIN_TINY) return r; + + SuperSlab* fg_ss = hak_super_lookup(ptr); + if (__builtin_expect(!(fg_ss && fg_ss->magic == SUPERSLAB_MAGIC), 0)) { + extern int g_use_superslab; + extern int g_super_reg_initialized; + uint8_t hdrb = 0; + if ((uintptr_t)ptr > 0) { hdrb = *((uint8_t*)ptr - 1); } + uintptr_t off = (uintptr_t)ptr & 0xFFFu; + fg_tiny_gate_log(ptr, hdrb, off, g_use_superslab, g_super_reg_initialized); + r.fg.domain = FG_DOMAIN_MIDCAND; + r.misclassified = 1; + } + return r; +} + +#endif // HAK_BOX_FG_TINY_GATE_H diff --git a/core/box/hak_alloc_api.inc.h b/core/box/hak_alloc_api.inc.h index e525f6ac..de61fc1a 100644 --- a/core/box/hak_alloc_api.inc.h +++ b/core/box/hak_alloc_api.inc.h @@ -239,6 +239,8 @@ inline void* hak_alloc_at(size_t size, hak_callsite_t site) { if (hdr->magic != HAKMEM_MAGIC) { fprintf(stderr, "[hakmem] ERROR: Invalid magic in allocated header!\n"); return ptr; } hdr->alloc_site = site_id; hdr->class_bytes = (size >= threshold) ? threshold : 0; + // Guard byte for FrontGate V2: force ptr[-1] away from 0xA?/0xB? to avoid Tiny misclass + ((uint8_t*)hdr)[HEADER_SIZE - 1] = HAKMEM_FG_GUARD_BYTE; #if HAKMEM_DEBUG_TIMING HKM_TIME_END(HKM_CAT_HAK_ALLOC, t0); diff --git a/core/box/hak_free_api.inc.h b/core/box/hak_free_api.inc.h index 656ed317..76a0cc5d 100644 --- a/core/box/hak_free_api.inc.h +++ b/core/box/hak_free_api.inc.h @@ -9,6 +9,7 @@ #include "../ptr_trace.h" // Debug: pointer trace immediate dump on libc fallback #include "front_gate_v2.h" // Phase 15: Box FG V2 - 1-byte header classification #include "external_guard_box.h" // Phase 15: Box ExternalGuard - mincore (ENV controlled) +#include "fg_tiny_gate_box.h" // Tiny gate guard box (Superslab check) #ifdef HAKMEM_POOL_TLS_PHASE1 #include "../pool_tls.h" @@ -82,6 +83,7 @@ void hak_free_at(void* ptr, size_t size, hak_callsite_t site) { HKM_TIME_START(t0); #endif (void)site; (void)size; + int fg_misclass = 0; // Set when FG said Tiny but registry rejects // Optional lightweight trace of early free calls (first few only) #if !HAKMEM_BUILD_RELEASE static int free_trace_en = -1; static _Atomic int free_trace_count = 0; @@ -129,6 +131,11 @@ void hak_free_at(void* ptr, size_t size, hak_callsite_t site) { fg_classification_t fg = fg_classify_domain(ptr); hak_free_route_log(fg_domain_name(fg.domain), ptr); + // Fail-Fast: Tiny判定は Superslab 登録が必須。無ければ MIDCAND に戻す(箱化)。 + fg_tiny_gate_result_t fg_guard = fg_tiny_gate(ptr, fg); + fg = fg_guard.fg; + fg_misclass = fg_guard.misclassified; + switch (fg.domain) { case FG_DOMAIN_TINY: { // Fast path: Tiny (C0-C7) with 1-byte header (0xa0 | class_idx) @@ -199,18 +206,6 @@ void hak_free_at(void* ptr, size_t size, hak_callsite_t site) { } } - // ========== Box ExternalGuard: Last Resort ========== - // PHASE 15: Delegate to ExternalGuard (mincore + libc fallback) - // Expected: Called 0-10 times in bench (if >100 → box leak!) - { - if (external_guard_try_free(ptr)) { - goto done; - } - // ExternalGuard failed (unmapped) → skip free (leak) - hak_free_route_log("external_guard_skip", ptr); - goto done; - } - // Raw header dispatch(mmap/malloc/BigCacheなど) { void* raw = (char*)ptr - HEADER_SIZE; @@ -232,14 +227,7 @@ void hak_free_at(void* ptr, size_t size, hak_callsite_t site) { if (!is_mapped) { // Memory not accessible, ptr likely has no header hak_free_route_log("unmapped_header_fallback", ptr); - - // In direct-link mode, try tiny_free (handles headerless Tiny allocs) - if (!g_ldpreload_mode && g_invalid_free_mode) { - hak_tiny_free(ptr); - goto done; - } - - // LD_PRELOAD mode: route to libc (might be libc allocation) + // Always punt to libc; never route unmapped/unknown pointers to Tiny extern void __libc_free(void*); ptr_trace_dump_now("free_api_libc_invalid_hdr"); __libc_free(ptr); @@ -259,31 +247,32 @@ void hak_free_at(void* ptr, size_t size, hak_callsite_t site) { // One-shot request-trace to help diagnose SS registry lookups hak_super_reg_reqtrace_dump(ptr); - // In direct-link mode, try routing to tiny free as best-effort recovery - // This handles case #1 where SuperSlab lookup failed but allocation is valid - if (!g_ldpreload_mode && g_invalid_free_mode) { - // Attempt tiny free (will validate internally and handle gracefully if invalid) - hak_free_route_log("invalid_magic_tiny_recovery", ptr); - hak_tiny_free(ptr); - goto done; + // Fail-fast diagnostics: never hand bad headers to Tiny or libc silently + SuperSlab* ss_diag = hak_super_lookup(ptr); + int slab_diag = ss_diag ? slab_index_for(ss_diag, ptr) : -1; + fprintf(stderr, + "[INVALID_MAGIC_FREE] ptr=%p magic=0x%X mode=%d ss=%p slab=%d\n", + ptr, hdr->magic, g_invalid_free_mode, (void*)ss_diag, slab_diag); + tiny_guard_on_invalid(ptr, hdr->magic); + + // If this pointer was a misclassified Tiny header miss, punt to libc to avoid corrupting TLS + if (fg_misclass) { + fprintf(stderr, "[FREE_MISCLASS_SKIP] ptr=%p hdr=0x%x (ignored to avoid corruption)\n", + ptr, hdr->magic); + goto done; // leak-safe skip: not our allocation } - // LD_PRELOAD mode or fallback mode: route to libc - // IMPORTANT: Use ptr (not raw), as NO header exists + // Never route invalid headers into Tiny; fail-fast by default if (g_invalid_free_mode) { - // Skip mode: leak memory (original behavior, but logged) static int leak_warn = 0; if (!leak_warn) { fprintf(stderr, "[hakmem] WARNING: Skipping free of invalid pointer %p (may leak memory)\n", ptr); leak_warn = 1; } - goto done; + abort(); } else { - // Fallback mode: route to libc - extern void __libc_free(void*); - ptr_trace_dump_now("free_api_libc_invalid_magic_fallback"); - __libc_free(ptr); // Use ptr, not raw! - goto done; + ptr_trace_dump_now("free_api_invalid_magic_failfast"); + abort(); } } // Phase 5-Step3: Use Mid/Large Config Box (compile-time constant in PGO mode) @@ -305,6 +294,8 @@ void hak_free_at(void* ptr, size_t size, hak_callsite_t site) { hak_free_route_log("malloc_hdr", ptr); extern void __libc_free(void*); ptr_trace_dump_now("free_api_libc_malloc_hdr"); + fprintf(stderr, "[FREE_LIBC_HDR] raw=%p user=%p size=%zu method=%d magic=0x%X\n", + raw, ptr, hdr->size, (int)hdr->method, hdr->magic); __libc_free(raw); break; case ALLOC_METHOD_MMAP: diff --git a/core/hakmem_internal.h b/core/hakmem_internal.h index 3da5ac11..1f6d1da9 100644 --- a/core/hakmem_internal.h +++ b/core/hakmem_internal.h @@ -86,6 +86,7 @@ extern int g_ldpreload_mode; #define HAKMEM_MAGIC 0x48414B4D // "HAKM" in ASCII (uint32_t) #define HEADER_SIZE sizeof(AllocHeader) +#define HAKMEM_FG_GUARD_BYTE 0x5Au // Ensure front gate never misclassifies mid/large as Tiny // THP thresholds (from config) #define THP_THRESHOLD (2 * 1024 * 1024) // 2MB @@ -114,6 +115,9 @@ typedef struct { uintptr_t owner_tid; // Owning thread (for Mid/Tiny per-thread fast path). 0 if unknown } AllocHeader; +// Phase 10: Pointer Type Safety +#include "box/ptr_type_box.h" + typedef enum { FREE_THERMAL_HOT, // すぐ再利用 → 何もしない(KEEP) FREE_THERMAL_WARM, // 中間 → MADV_FREE(munmapしない) @@ -392,7 +396,8 @@ static inline void hak_header_set_class(void* user_ptr, size_t class_bytes) { // Args: raw - pointer to raw allocation (including header) static inline void hak_free_malloc_impl(void* raw) { if (!raw) return; // Safety check - free(raw); + extern void __libc_free(void*); + __libc_free(raw); } // Free mmap-allocated block @@ -403,7 +408,8 @@ static inline void hak_free_mmap_impl(void* raw, size_t size) { #ifdef __linux__ munmap(raw, size); #else - free(raw); // Fallback on non-Linux + extern void __libc_free(void*); + __libc_free(raw); // Fallback on non-Linux #endif }