fix: guard Tiny FG misclass and add fg_tiny_gate box
This commit is contained in:
46
core/box/fg_tiny_gate_box.h
Normal file
46
core/box/fg_tiny_gate_box.h
Normal file
@ -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 <stdatomic.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
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
|
||||||
@ -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; }
|
if (hdr->magic != HAKMEM_MAGIC) { fprintf(stderr, "[hakmem] ERROR: Invalid magic in allocated header!\n"); return ptr; }
|
||||||
hdr->alloc_site = site_id;
|
hdr->alloc_site = site_id;
|
||||||
hdr->class_bytes = (size >= threshold) ? threshold : 0;
|
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
|
#if HAKMEM_DEBUG_TIMING
|
||||||
HKM_TIME_END(HKM_CAT_HAK_ALLOC, t0);
|
HKM_TIME_END(HKM_CAT_HAK_ALLOC, t0);
|
||||||
|
|||||||
@ -9,6 +9,7 @@
|
|||||||
#include "../ptr_trace.h" // Debug: pointer trace immediate dump on libc fallback
|
#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 "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 "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
|
#ifdef HAKMEM_POOL_TLS_PHASE1
|
||||||
#include "../pool_tls.h"
|
#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);
|
HKM_TIME_START(t0);
|
||||||
#endif
|
#endif
|
||||||
(void)site; (void)size;
|
(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)
|
// Optional lightweight trace of early free calls (first few only)
|
||||||
#if !HAKMEM_BUILD_RELEASE
|
#if !HAKMEM_BUILD_RELEASE
|
||||||
static int free_trace_en = -1; static _Atomic int free_trace_count = 0;
|
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);
|
fg_classification_t fg = fg_classify_domain(ptr);
|
||||||
hak_free_route_log(fg_domain_name(fg.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) {
|
switch (fg.domain) {
|
||||||
case FG_DOMAIN_TINY: {
|
case FG_DOMAIN_TINY: {
|
||||||
// Fast path: Tiny (C0-C7) with 1-byte header (0xa0 | class_idx)
|
// 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など)
|
// Raw header dispatch(mmap/malloc/BigCacheなど)
|
||||||
{
|
{
|
||||||
void* raw = (char*)ptr - HEADER_SIZE;
|
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) {
|
if (!is_mapped) {
|
||||||
// Memory not accessible, ptr likely has no header
|
// Memory not accessible, ptr likely has no header
|
||||||
hak_free_route_log("unmapped_header_fallback", ptr);
|
hak_free_route_log("unmapped_header_fallback", ptr);
|
||||||
|
// Always punt to libc; never route unmapped/unknown pointers to Tiny
|
||||||
// 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)
|
|
||||||
extern void __libc_free(void*);
|
extern void __libc_free(void*);
|
||||||
ptr_trace_dump_now("free_api_libc_invalid_hdr");
|
ptr_trace_dump_now("free_api_libc_invalid_hdr");
|
||||||
__libc_free(ptr);
|
__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
|
// One-shot request-trace to help diagnose SS registry lookups
|
||||||
hak_super_reg_reqtrace_dump(ptr);
|
hak_super_reg_reqtrace_dump(ptr);
|
||||||
|
|
||||||
// In direct-link mode, try routing to tiny free as best-effort recovery
|
// Fail-fast diagnostics: never hand bad headers to Tiny or libc silently
|
||||||
// This handles case #1 where SuperSlab lookup failed but allocation is valid
|
SuperSlab* ss_diag = hak_super_lookup(ptr);
|
||||||
if (!g_ldpreload_mode && g_invalid_free_mode) {
|
int slab_diag = ss_diag ? slab_index_for(ss_diag, ptr) : -1;
|
||||||
// Attempt tiny free (will validate internally and handle gracefully if invalid)
|
fprintf(stderr,
|
||||||
hak_free_route_log("invalid_magic_tiny_recovery", ptr);
|
"[INVALID_MAGIC_FREE] ptr=%p magic=0x%X mode=%d ss=%p slab=%d\n",
|
||||||
hak_tiny_free(ptr);
|
ptr, hdr->magic, g_invalid_free_mode, (void*)ss_diag, slab_diag);
|
||||||
goto done;
|
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
|
// Never route invalid headers into Tiny; fail-fast by default
|
||||||
// IMPORTANT: Use ptr (not raw), as NO header exists
|
|
||||||
if (g_invalid_free_mode) {
|
if (g_invalid_free_mode) {
|
||||||
// Skip mode: leak memory (original behavior, but logged)
|
|
||||||
static int leak_warn = 0;
|
static int leak_warn = 0;
|
||||||
if (!leak_warn) {
|
if (!leak_warn) {
|
||||||
fprintf(stderr, "[hakmem] WARNING: Skipping free of invalid pointer %p (may leak memory)\n", ptr);
|
fprintf(stderr, "[hakmem] WARNING: Skipping free of invalid pointer %p (may leak memory)\n", ptr);
|
||||||
leak_warn = 1;
|
leak_warn = 1;
|
||||||
}
|
}
|
||||||
goto done;
|
abort();
|
||||||
} else {
|
} else {
|
||||||
// Fallback mode: route to libc
|
ptr_trace_dump_now("free_api_invalid_magic_failfast");
|
||||||
extern void __libc_free(void*);
|
abort();
|
||||||
ptr_trace_dump_now("free_api_libc_invalid_magic_fallback");
|
|
||||||
__libc_free(ptr); // Use ptr, not raw!
|
|
||||||
goto done;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Phase 5-Step3: Use Mid/Large Config Box (compile-time constant in PGO mode)
|
// 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);
|
hak_free_route_log("malloc_hdr", ptr);
|
||||||
extern void __libc_free(void*);
|
extern void __libc_free(void*);
|
||||||
ptr_trace_dump_now("free_api_libc_malloc_hdr");
|
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);
|
__libc_free(raw);
|
||||||
break;
|
break;
|
||||||
case ALLOC_METHOD_MMAP:
|
case ALLOC_METHOD_MMAP:
|
||||||
|
|||||||
@ -86,6 +86,7 @@ extern int g_ldpreload_mode;
|
|||||||
|
|
||||||
#define HAKMEM_MAGIC 0x48414B4D // "HAKM" in ASCII (uint32_t)
|
#define HAKMEM_MAGIC 0x48414B4D // "HAKM" in ASCII (uint32_t)
|
||||||
#define HEADER_SIZE sizeof(AllocHeader)
|
#define HEADER_SIZE sizeof(AllocHeader)
|
||||||
|
#define HAKMEM_FG_GUARD_BYTE 0x5Au // Ensure front gate never misclassifies mid/large as Tiny
|
||||||
|
|
||||||
// THP thresholds (from config)
|
// THP thresholds (from config)
|
||||||
#define THP_THRESHOLD (2 * 1024 * 1024) // 2MB
|
#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
|
uintptr_t owner_tid; // Owning thread (for Mid/Tiny per-thread fast path). 0 if unknown
|
||||||
} AllocHeader;
|
} AllocHeader;
|
||||||
|
|
||||||
|
// Phase 10: Pointer Type Safety
|
||||||
|
#include "box/ptr_type_box.h"
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
FREE_THERMAL_HOT, // すぐ再利用 → 何もしない(KEEP)
|
FREE_THERMAL_HOT, // すぐ再利用 → 何もしない(KEEP)
|
||||||
FREE_THERMAL_WARM, // 中間 → MADV_FREE(munmapしない)
|
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)
|
// Args: raw - pointer to raw allocation (including header)
|
||||||
static inline void hak_free_malloc_impl(void* raw) {
|
static inline void hak_free_malloc_impl(void* raw) {
|
||||||
if (!raw) return; // Safety check
|
if (!raw) return; // Safety check
|
||||||
free(raw);
|
extern void __libc_free(void*);
|
||||||
|
__libc_free(raw);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Free mmap-allocated block
|
// Free mmap-allocated block
|
||||||
@ -403,7 +408,8 @@ static inline void hak_free_mmap_impl(void* raw, size_t size) {
|
|||||||
#ifdef __linux__
|
#ifdef __linux__
|
||||||
munmap(raw, size);
|
munmap(raw, size);
|
||||||
#else
|
#else
|
||||||
free(raw); // Fallback on non-Linux
|
extern void __libc_free(void*);
|
||||||
|
__libc_free(raw); // Fallback on non-Linux
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user