fix: guard Tiny FG misclass and add fg_tiny_gate box

This commit is contained in:
Moe Charm (CI)
2025-12-01 16:05:55 +09:00
parent f32d996edb
commit 2bd8da9267
4 changed files with 83 additions and 38 deletions

View 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

View File

@ -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);

View File

@ -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 dispatchmmap/malloc/BigCacheなど // Raw header dispatchmmap/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:

View File

@ -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_FREEmunmapしない FREE_THERMAL_WARM, // 中間 → MADV_FREEmunmapしない
@ -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
} }