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

@ -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 dispatchmmap/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: