Add defensive layers mapping and diagnostic logging enhancements
Documentation: - Created docs/DEFENSIVE_LAYERS_MAPPING.md documenting all 5 defensive layers - Maps which symptoms each layer suppresses - Defines safe removal order after root cause fix - Includes test methods for each layer removal Diagnostic Logging Enhancements (ChatGPT work): - TLS_SLL_HEAD_SET log with count and backtrace for NORMALIZE_USERPTR - tiny_next_store_log with filtering capability - Environment variables for log filtering: - HAKMEM_TINY_SLL_NEXTCLS: class filter for next store (-1 disables) - HAKMEM_TINY_SLL_NEXTTAG: tag filter (substring match) - HAKMEM_TINY_SLL_HEADCLS: class filter for head trace Current Investigation Status: - sh8bench 60/120s: crash-free, zero NEXT_INVALID/HDR_RESET/SANITIZE - BUT: shot limit (256) exhausted by class3 tls_push before class1/drain - Need: Add tags to pop/clear paths, or increase shot limit for class1 Purpose of this commit: - Document defensive layers for safe removal later - Enable targeted diagnostic logging - Prepare for final root cause identification Next Steps: 1. Add tags to tls_sll_pop tiny_next_write (e.g., "tls_pop_clear") 2. Re-run with HAKMEM_TINY_SLL_NEXTTAG=tls_pop 3. Capture class1 writes that lead to corruption 🤖 Generated with Claude Code (https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@ -47,6 +47,14 @@
|
||||
#include "box/tiny_layout_box.h"
|
||||
#include "box/tiny_header_box.h"
|
||||
|
||||
// Per-thread trace context injected by PTR_NEXT_WRITE macro (for triage)
|
||||
static __thread const char* g_tiny_next_tag = NULL;
|
||||
static __thread const char* g_tiny_next_file = NULL;
|
||||
static __thread int g_tiny_next_line = 0;
|
||||
static __thread void* g_tiny_next_ra0 = NULL;
|
||||
static __thread void* g_tiny_next_ra1 = NULL;
|
||||
static __thread void* g_tiny_next_ra2 = NULL;
|
||||
|
||||
// Compute freelist next-pointer offset within a block for the given class.
|
||||
// P0.1 updated: C0 and C7 use offset 0, C1-C6 use offset 1 (header preserved)
|
||||
// Rationale for C0: 8B stride cannot fit [1B header][8B next pointer] without overflow
|
||||
@ -54,6 +62,90 @@ static inline __attribute__((always_inline)) size_t tiny_next_off(int class_idx)
|
||||
return tiny_user_offset(class_idx);
|
||||
}
|
||||
|
||||
// Optional: log next-pointer writes for triage (env: HAKMEM_TINY_SLL_HEADLOG=1)
|
||||
static inline void tiny_next_store_log(int class_idx, void* base, void* next, size_t off)
|
||||
{
|
||||
static int g_nextlog_en = 1; // default ON for triage; disable with HAKMEM_TINY_SLL_HEADLOG=0
|
||||
static int g_nextlog_env_checked = 0;
|
||||
static int g_nextlog_cls = -2; // -1 = no filter; >=0 = only that class
|
||||
static const char* g_nextlog_tag_filter = NULL; // substring match; NULL = no filter
|
||||
if (!g_nextlog_env_checked) {
|
||||
const char* e = getenv("HAKMEM_TINY_SLL_HEADLOG");
|
||||
if (e && *e == '0') {
|
||||
g_nextlog_en = 0;
|
||||
}
|
||||
const char* c = getenv("HAKMEM_TINY_SLL_NEXTCLS");
|
||||
if (c && *c) {
|
||||
g_nextlog_cls = atoi(c);
|
||||
} else {
|
||||
g_nextlog_cls = -1;
|
||||
}
|
||||
g_nextlog_tag_filter = getenv("HAKMEM_TINY_SLL_NEXTTAG");
|
||||
g_nextlog_env_checked = 1;
|
||||
}
|
||||
if (!__builtin_expect(g_nextlog_en, 0)) return;
|
||||
if (g_nextlog_cls >= 0 && class_idx != g_nextlog_cls) return;
|
||||
|
||||
// Pull tag/callsite from TLS and clear immediately to avoid stale reuse
|
||||
const char* tag = g_tiny_next_tag;
|
||||
const char* file = g_tiny_next_file;
|
||||
int line = g_tiny_next_line;
|
||||
void* ra0 = g_tiny_next_ra0;
|
||||
void* ra1 = g_tiny_next_ra1;
|
||||
void* ra2 = g_tiny_next_ra2;
|
||||
g_tiny_next_tag = NULL;
|
||||
g_tiny_next_file = NULL;
|
||||
g_tiny_next_line = 0;
|
||||
g_tiny_next_ra0 = NULL;
|
||||
g_tiny_next_ra1 = NULL;
|
||||
g_tiny_next_ra2 = NULL;
|
||||
if (!tag) return;
|
||||
if (g_nextlog_tag_filter && !strstr(tag, g_nextlog_tag_filter)) return;
|
||||
|
||||
static _Atomic uint32_t g_nextlog_shot = 0;
|
||||
uint32_t shot = atomic_fetch_add_explicit(&g_nextlog_shot, 1, memory_order_relaxed);
|
||||
if (shot >= 256) return;
|
||||
|
||||
SuperSlab* ss = hak_super_lookup(base);
|
||||
int cap = ss ? ss_slabs_capacity(ss) : 0;
|
||||
int idx = (ss && ss->magic == SUPERSLAB_MAGIC) ? slab_index_for(ss, base) : -1;
|
||||
uint8_t cls = (idx >= 0 && idx < cap) ? ss->slabs[idx].class_idx : 0xff;
|
||||
void* ra = __builtin_return_address(0);
|
||||
fprintf(stderr,
|
||||
"[TINY_NEXT_STORE] shot=%u cls=%d base=%p next=%p off=%zu ss=%p idx=%d meta_cls=%u caller=%p tag=%s site=%s:%d ra0=%p ra1=%p ra2=%p\n",
|
||||
shot + 1,
|
||||
class_idx,
|
||||
base,
|
||||
next,
|
||||
off,
|
||||
(void*)ss,
|
||||
idx,
|
||||
(unsigned)cls,
|
||||
ra,
|
||||
tag,
|
||||
file,
|
||||
line,
|
||||
ra0,
|
||||
ra1,
|
||||
ra2);
|
||||
// Early frames for offline addr2line when caller symbols are missing
|
||||
if (shot < 24) {
|
||||
void* bt[16];
|
||||
int frames = backtrace(bt, 16);
|
||||
backtrace_symbols_fd(bt, frames, fileno(stderr));
|
||||
}
|
||||
// Backtrace only for clearly misaligned bases (likely user pointers)
|
||||
if (((uintptr_t)base & 0xF) != 0) {
|
||||
static _Atomic uint32_t g_next_bt = 0;
|
||||
uint32_t bt_shot = atomic_fetch_add_explicit(&g_next_bt, 1, memory_order_relaxed);
|
||||
if (bt_shot < 8) {
|
||||
void* bt[16];
|
||||
int frames = backtrace(bt, 16);
|
||||
backtrace_symbols_fd(bt, frames, fileno(stderr));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Safe load of next pointer from a block base.
|
||||
static inline __attribute__((always_inline)) void* tiny_next_load(const void* base, int class_idx) {
|
||||
size_t off = tiny_next_off(class_idx);
|
||||
@ -109,12 +201,14 @@ static inline __attribute__((always_inline)) void tiny_next_store(void* base, in
|
||||
if (off == 0) {
|
||||
// Aligned access at base (overwrites header for C7).
|
||||
*(void**)base = next;
|
||||
tiny_next_store_log(class_idx, base, next, off);
|
||||
return;
|
||||
}
|
||||
|
||||
// off != 0: use memcpy for portability / UB-avoidance.
|
||||
uint8_t* p = (uint8_t*)base + off;
|
||||
memcpy(p, &next, sizeof(void*));
|
||||
tiny_next_store_log(class_idx, base, next, off);
|
||||
}
|
||||
|
||||
#endif // TINY_NEXTPTR_H
|
||||
|
||||
Reference in New Issue
Block a user