143 lines
4.9 KiB
C
143 lines
4.9 KiB
C
|
|
// external_guard_box.h - Box ExternalGuard: Last Resort Safety Check
|
||
|
|
// Purpose: Handle pointers not recognized by Tiny/Pool/Mid boxes
|
||
|
|
// Design: ChatGPT consultation Phase 15 - Box Separation
|
||
|
|
//
|
||
|
|
// Responsibilities:
|
||
|
|
// 1. mincore safety check (ENV controlled for debug)
|
||
|
|
// 2. AllocHeader dispatch (for LD_PRELOAD / external allocations)
|
||
|
|
// 3. Logging (detect Box Tiny/Pool/Mid misses)
|
||
|
|
//
|
||
|
|
// ENV Flags:
|
||
|
|
// - HAKMEM_EXTERNAL_GUARD_MINCORE=0 # Default: OFF (bench optimization)
|
||
|
|
// - HAKMEM_EXTERNAL_GUARD_MINCORE=1 # Debug: ON (safety check)
|
||
|
|
// - HAKMEM_EXTERNAL_GUARD_LOG=0 # Default: Silent
|
||
|
|
// - HAKMEM_EXTERNAL_GUARD_LOG=1 # Debug: Log all calls
|
||
|
|
//
|
||
|
|
// Expected behavior:
|
||
|
|
// - Bench workload: Called 0-10 times (all hakmem managed)
|
||
|
|
// - If called frequently: Box Tiny/Pool/Mid has leak!
|
||
|
|
|
||
|
|
#ifndef HAK_BOX_EXTERNAL_GUARD_H
|
||
|
|
#define HAK_BOX_EXTERNAL_GUARD_H
|
||
|
|
|
||
|
|
#include <stdint.h>
|
||
|
|
#include <stdio.h>
|
||
|
|
#include <stdlib.h>
|
||
|
|
#include <sys/mman.h>
|
||
|
|
|
||
|
|
// ENV control: mincore enable/disable
|
||
|
|
static inline int external_guard_mincore_enabled(void) {
|
||
|
|
static int g_enabled = -1;
|
||
|
|
if (g_enabled == -1) {
|
||
|
|
const char* e = getenv("HAKMEM_EXTERNAL_GUARD_MINCORE");
|
||
|
|
g_enabled = (e && *e == '1') ? 1 : 0; // Default: OFF
|
||
|
|
}
|
||
|
|
return g_enabled;
|
||
|
|
}
|
||
|
|
|
||
|
|
// ENV control: logging
|
||
|
|
static inline int external_guard_log_enabled(void) {
|
||
|
|
static int g_enabled = -1;
|
||
|
|
if (g_enabled == -1) {
|
||
|
|
const char* e = getenv("HAKMEM_EXTERNAL_GUARD_LOG");
|
||
|
|
g_enabled = (e && *e == '1') ? 1 : 0; // Default: OFF
|
||
|
|
}
|
||
|
|
return g_enabled;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Statistics
|
||
|
|
typedef struct {
|
||
|
|
uint64_t total_calls;
|
||
|
|
uint64_t mincore_checks;
|
||
|
|
uint64_t mincore_failed;
|
||
|
|
uint64_t alloc_header_dispatch;
|
||
|
|
uint64_t unknown_ptr;
|
||
|
|
} external_guard_stats_t;
|
||
|
|
|
||
|
|
static __thread external_guard_stats_t g_external_guard_stats = {0};
|
||
|
|
|
||
|
|
// Check if pointer is safe to dereference (mincore-based, optional)
|
||
|
|
// Returns: 1 if mapped, 0 if not mapped
|
||
|
|
static inline int external_guard_is_mapped(void* ptr) {
|
||
|
|
if (!external_guard_mincore_enabled()) {
|
||
|
|
// Fast path: mincore disabled, assume mapped
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
|
||
|
|
g_external_guard_stats.mincore_checks++;
|
||
|
|
|
||
|
|
#ifdef __linux__
|
||
|
|
unsigned char vec;
|
||
|
|
void* page = (void*)((uintptr_t)ptr & ~0xFFFUL);
|
||
|
|
if (mincore(page, 1, &vec) == 0) {
|
||
|
|
return 1; // Mapped
|
||
|
|
}
|
||
|
|
#endif
|
||
|
|
|
||
|
|
g_external_guard_stats.mincore_failed++;
|
||
|
|
return 0; // Not mapped
|
||
|
|
}
|
||
|
|
|
||
|
|
// External guard entry point
|
||
|
|
// Contract:
|
||
|
|
// - Input: ptr (unknown origin)
|
||
|
|
// - Output: 1 if handled (freed), 0 if unrecognized (caller must handle)
|
||
|
|
// - Side effect: May call libc free, or return 0 for caller fallback
|
||
|
|
static inline int external_guard_try_free(void* ptr) {
|
||
|
|
g_external_guard_stats.total_calls++;
|
||
|
|
|
||
|
|
if (external_guard_log_enabled()) {
|
||
|
|
fprintf(stderr, "[ExternalGuard] ptr=%p (call #%lu)\n",
|
||
|
|
ptr, g_external_guard_stats.total_calls);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Safety check: is memory mapped?
|
||
|
|
if (!external_guard_is_mapped(ptr)) {
|
||
|
|
if (external_guard_log_enabled()) {
|
||
|
|
fprintf(stderr, "[ExternalGuard] ptr=%p NOT MAPPED (mincore failed)\n", ptr);
|
||
|
|
}
|
||
|
|
g_external_guard_stats.unknown_ptr++;
|
||
|
|
return 0; // Can't free unmapped memory
|
||
|
|
}
|
||
|
|
|
||
|
|
// TODO: AllocHeader dispatch (16-byte header for Mid/Large/LD_PRELOAD)
|
||
|
|
// For now, delegate to libc free as fallback
|
||
|
|
g_external_guard_stats.alloc_header_dispatch++;
|
||
|
|
|
||
|
|
if (external_guard_log_enabled()) {
|
||
|
|
fprintf(stderr, "[ExternalGuard] ptr=%p delegated to __libc_free\n", ptr);
|
||
|
|
}
|
||
|
|
|
||
|
|
// CRITICAL FIX: Use __libc_free() to avoid infinite loop via hakmem wrapper
|
||
|
|
extern void __libc_free(void*);
|
||
|
|
__libc_free(ptr);
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Print statistics (called at program exit)
|
||
|
|
static inline void external_guard_print_stats(void) {
|
||
|
|
static int g_stats_enable = -1;
|
||
|
|
if (g_stats_enable == -1) {
|
||
|
|
const char* e = getenv("HAKMEM_EXTERNAL_GUARD_STATS");
|
||
|
|
g_stats_enable = (e && *e && *e != '0') ? 1 : 0;
|
||
|
|
}
|
||
|
|
if (!g_stats_enable) return;
|
||
|
|
|
||
|
|
fprintf(stderr, "\n=== Box ExternalGuard Statistics ===\n");
|
||
|
|
fprintf(stderr, "Total calls: %lu\n", g_external_guard_stats.total_calls);
|
||
|
|
fprintf(stderr, "mincore checks: %lu\n", g_external_guard_stats.mincore_checks);
|
||
|
|
fprintf(stderr, "mincore failed: %lu\n", g_external_guard_stats.mincore_failed);
|
||
|
|
fprintf(stderr, "AllocHeader dispatch:%lu\n", g_external_guard_stats.alloc_header_dispatch);
|
||
|
|
fprintf(stderr, "Unknown ptrs: %lu\n", g_external_guard_stats.unknown_ptr);
|
||
|
|
|
||
|
|
if (g_external_guard_stats.total_calls > 100) {
|
||
|
|
fprintf(stderr, "\n⚠️ WARNING: ExternalGuard called %lu times!\n",
|
||
|
|
g_external_guard_stats.total_calls);
|
||
|
|
fprintf(stderr, " This suggests Box Tiny/Pool/Mid is missing pointers.\n");
|
||
|
|
fprintf(stderr, " Enable HAKMEM_EXTERNAL_GUARD_LOG=1 to debug.\n");
|
||
|
|
}
|
||
|
|
fprintf(stderr, "=====================================\n\n");
|
||
|
|
}
|
||
|
|
|
||
|
|
#endif // HAK_BOX_EXTERNAL_GUARD_H
|