Merge separate g_tls_sll_head[] and g_tls_sll_count[] arrays into unified TinyTLSSLL struct to improve L1D cache locality. Expected performance gain: +12-18% from reducing cache line splits (2 loads → 1 load per operation). Changes: - core/hakmem_tiny.h: Add TinyTLSSLL type (16B aligned, head+count+pad) - core/hakmem_tiny.c: Replace separate arrays with g_tls_sll[8] - core/box/tls_sll_box.h: Update Box API (13 sites) for unified access - Updated 32+ files: All g_tls_sll_head[i] → g_tls_sll[i].head - Updated 32+ files: All g_tls_sll_count[i] → g_tls_sll[i].count - core/hakmem_tiny_integrity.h: Unified canary guards - core/box/integrity_box.c: Simplified canary validation - Makefile: Added core/box/tiny_sizeclass_hist_box.o to link Build: ✅ PASS (10K ops sanity test) Warnings: Only pre-existing LTO type mismatches (unrelated) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
372 lines
12 KiB
C
372 lines
12 KiB
C
// tls_sll_box.h - Box TLS-SLL: Single-Linked List API (Unified Box version)
|
|
//
|
|
// Goal:
|
|
// - Single authoritative Box for TLS SLL operations.
|
|
// - All next pointer layout is decided by tiny_next_ptr_box.h (Box API).
|
|
// - Callers pass BASE pointers only; no local next_offset arithmetic.
|
|
// - Compatible with existing ptr_trace PTR_NEXT_* macros (off is logging-only).
|
|
//
|
|
// Invariants:
|
|
// - g_tiny_class_sizes[cls] is TOTAL stride (including 1-byte header when enabled).
|
|
// - For HEADER_CLASSIDX != 0, tiny_nextptr.h encodes:
|
|
// class 0: next_off = 0
|
|
// class 1-6: next_off = 1
|
|
// class 7: next_off = 0
|
|
// Callers MUST NOT duplicate this logic.
|
|
// - TLS SLL stores BASE pointers only.
|
|
// - Box provides: push / pop / splice with capacity & integrity checks.
|
|
|
|
#ifndef TLS_SLL_BOX_H
|
|
#define TLS_SLL_BOX_H
|
|
|
|
#include <stdint.h>
|
|
#include <stdbool.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
|
|
#include "../hakmem_tiny_config.h"
|
|
#include "../hakmem_build_flags.h"
|
|
#include "../tiny_remote.h"
|
|
#include "../tiny_region_id.h"
|
|
#include "../hakmem_tiny_integrity.h"
|
|
#include "../ptr_track.h"
|
|
#include "../ptr_trace.h"
|
|
#include "../tiny_debug_ring.h"
|
|
#include "tiny_next_ptr_box.h"
|
|
|
|
// Phase 3d-B: Unified TLS SLL (defined in hakmem_tiny.c)
|
|
extern __thread TinyTLSSLL g_tls_sll[TINY_NUM_CLASSES];
|
|
extern int g_tls_sll_class_mask; // bit i=1 → SLL allowed for class i
|
|
|
|
// ========== Debug guard ==========
|
|
|
|
#if !HAKMEM_BUILD_RELEASE
|
|
static inline void tls_sll_debug_guard(int class_idx, void* base, const char* where)
|
|
{
|
|
(void)class_idx;
|
|
if ((uintptr_t)base < 4096) {
|
|
fprintf(stderr,
|
|
"[TLS_SLL_GUARD] %s: suspicious ptr=%p cls=%d\n",
|
|
where, base, class_idx);
|
|
abort();
|
|
}
|
|
}
|
|
#else
|
|
static inline void tls_sll_debug_guard(int class_idx, void* base, const char* where)
|
|
{
|
|
(void)class_idx; (void)base; (void)where;
|
|
}
|
|
#endif
|
|
|
|
// Normalize helper: callers are required to pass BASE already.
|
|
// Kept as a no-op for documentation / future hardening.
|
|
static inline void* tls_sll_normalize_base(int class_idx, void* node)
|
|
{
|
|
(void)class_idx;
|
|
return node;
|
|
}
|
|
|
|
// ========== Push ==========
|
|
//
|
|
// Push BASE pointer into TLS SLL for given class.
|
|
// Returns true on success, false if capacity full or input invalid.
|
|
|
|
static inline bool tls_sll_push(int class_idx, void* ptr, uint32_t capacity)
|
|
{
|
|
HAK_CHECK_CLASS_IDX(class_idx, "tls_sll_push");
|
|
|
|
// Class mask gate (narrow triage): if disallowed, reject push
|
|
if (__builtin_expect(((g_tls_sll_class_mask & (1u << class_idx)) == 0), 0)) {
|
|
return false;
|
|
}
|
|
|
|
// Capacity semantics:
|
|
// - capacity == 0 → disabled (reject)
|
|
// - capacity > 1<<20 → treat as "unbounded" sentinel (no limit)
|
|
if (capacity == 0) {
|
|
return false;
|
|
}
|
|
const uint32_t kCapacityHardMax = (1u << 20);
|
|
const int unlimited = (capacity > kCapacityHardMax);
|
|
|
|
if (!ptr) {
|
|
return false;
|
|
}
|
|
|
|
// Base pointer only (callers must pass BASE; this is a no-op by design).
|
|
ptr = tls_sll_normalize_base(class_idx, ptr);
|
|
|
|
#if !HAKMEM_BUILD_RELEASE
|
|
// Minimal range guard before we touch memory.
|
|
if (!validate_ptr_range(ptr, "tls_sll_push_base")) {
|
|
fprintf(stderr,
|
|
"[TLS_SLL_PUSH] FATAL invalid BASE ptr cls=%d base=%p\n",
|
|
class_idx, ptr);
|
|
abort();
|
|
}
|
|
#endif
|
|
|
|
// Capacity check BEFORE any writes.
|
|
uint32_t cur = g_tls_sll[class_idx].count;
|
|
if (!unlimited && cur >= capacity) {
|
|
return false;
|
|
}
|
|
|
|
#if HAKMEM_TINY_HEADER_CLASSIDX
|
|
// Header handling for header classes (class != 0,7).
|
|
// Safe mode (HAKMEM_TINY_SLL_SAFEHEADER=1): never overwrite header; reject on magic mismatch.
|
|
// Default mode: restore expected header.
|
|
if (class_idx != 0 && class_idx != 7) {
|
|
static int g_sll_safehdr = -1;
|
|
static int g_sll_ring_en = -1; // optional ring trace for TLS-SLL anomalies
|
|
if (__builtin_expect(g_sll_safehdr == -1, 0)) {
|
|
const char* e = getenv("HAKMEM_TINY_SLL_SAFEHEADER");
|
|
g_sll_safehdr = (e && *e && *e != '0') ? 1 : 0;
|
|
}
|
|
if (__builtin_expect(g_sll_ring_en == -1, 0)) {
|
|
const char* r = getenv("HAKMEM_TINY_SLL_RING");
|
|
g_sll_ring_en = (r && *r && *r != '0') ? 1 : 0;
|
|
}
|
|
uint8_t* b = (uint8_t*)ptr;
|
|
uint8_t expected = (uint8_t)(HEADER_MAGIC | (class_idx & HEADER_CLASS_MASK));
|
|
if (g_sll_safehdr) {
|
|
uint8_t got = *b;
|
|
if ((got & 0xF0u) != HEADER_MAGIC) {
|
|
// Reject push silently (fall back to slow path at caller)
|
|
if (__builtin_expect(g_sll_ring_en, 0)) {
|
|
// aux encodes: high 8 bits = got, low 8 bits = expected
|
|
uintptr_t aux = ((uintptr_t)got << 8) | (uintptr_t)expected;
|
|
tiny_debug_ring_record(0x7F10 /*TLS_SLL_REJECT*/, (uint16_t)class_idx, ptr, aux);
|
|
}
|
|
return false;
|
|
}
|
|
} else {
|
|
PTR_TRACK_TLS_PUSH(ptr, class_idx);
|
|
PTR_TRACK_HEADER_WRITE(ptr, expected);
|
|
*b = expected;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
tls_sll_debug_guard(class_idx, ptr, "push");
|
|
|
|
#if !HAKMEM_BUILD_RELEASE
|
|
// Optional double-free detection: scan a bounded prefix of the list.
|
|
{
|
|
void* scan = g_tls_sll[class_idx].head;
|
|
uint32_t scanned = 0;
|
|
const uint32_t limit = (g_tls_sll[class_idx].count < 64)
|
|
? g_tls_sll[class_idx].count
|
|
: 64;
|
|
while (scan && scanned < limit) {
|
|
if (scan == ptr) {
|
|
fprintf(stderr,
|
|
"[TLS_SLL_PUSH] FATAL double-free: cls=%d ptr=%p already in SLL\n",
|
|
class_idx, ptr);
|
|
ptr_trace_dump_now("double_free");
|
|
abort();
|
|
}
|
|
void* next;
|
|
PTR_NEXT_READ("tls_sll_scan", class_idx, scan, 0, next);
|
|
scan = next;
|
|
scanned++;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// Link new node to current head via Box API (offset is handled inside tiny_nextptr).
|
|
PTR_NEXT_WRITE("tls_push", class_idx, ptr, 0, g_tls_sll[class_idx].head);
|
|
g_tls_sll[class_idx].head = ptr;
|
|
g_tls_sll[class_idx].count = cur + 1;
|
|
|
|
return true;
|
|
}
|
|
|
|
// ========== Pop ==========
|
|
//
|
|
// Pop BASE pointer from TLS SLL.
|
|
// Returns true on success and stores BASE into *out.
|
|
|
|
static inline bool tls_sll_pop(int class_idx, void** out)
|
|
{
|
|
HAK_CHECK_CLASS_IDX(class_idx, "tls_sll_pop");
|
|
// Class mask gate: if disallowed, behave as empty
|
|
if (__builtin_expect(((g_tls_sll_class_mask & (1u << class_idx)) == 0), 0)) {
|
|
return false;
|
|
}
|
|
atomic_fetch_add(&g_integrity_check_class_bounds, 1);
|
|
|
|
void* base = g_tls_sll[class_idx].head;
|
|
if (!base) {
|
|
return false;
|
|
}
|
|
|
|
// Sentinel guard: remote sentinel must never be in TLS SLL.
|
|
if (__builtin_expect((uintptr_t)base == TINY_REMOTE_SENTINEL, 0)) {
|
|
g_tls_sll[class_idx].head = NULL;
|
|
g_tls_sll[class_idx].count = 0;
|
|
#if !HAKMEM_BUILD_RELEASE
|
|
fprintf(stderr,
|
|
"[TLS_SLL_POP] Remote sentinel detected at head; SLL reset (cls=%d)\n",
|
|
class_idx);
|
|
#endif
|
|
{
|
|
static int g_sll_ring_en = -1;
|
|
if (__builtin_expect(g_sll_ring_en == -1, 0)) {
|
|
const char* r = getenv("HAKMEM_TINY_SLL_RING");
|
|
g_sll_ring_en = (r && *r && *r != '0') ? 1 : 0;
|
|
}
|
|
if (__builtin_expect(g_sll_ring_en, 0)) {
|
|
tiny_debug_ring_record(0x7F11 /*TLS_SLL_SENTINEL*/, (uint16_t)class_idx, base, 0);
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
#if !HAKMEM_BUILD_RELEASE
|
|
if (!validate_ptr_range(base, "tls_sll_pop_base")) {
|
|
fprintf(stderr,
|
|
"[TLS_SLL_POP] FATAL invalid BASE ptr cls=%d base=%p\n",
|
|
class_idx, base);
|
|
abort();
|
|
}
|
|
#endif
|
|
|
|
tls_sll_debug_guard(class_idx, base, "pop");
|
|
|
|
#if HAKMEM_TINY_HEADER_CLASSIDX
|
|
// Header validation for header-classes (class != 0,7).
|
|
if (class_idx != 0 && class_idx != 7) {
|
|
uint8_t got = *(uint8_t*)base;
|
|
uint8_t expect = (uint8_t)(HEADER_MAGIC | (class_idx & HEADER_CLASS_MASK));
|
|
PTR_TRACK_TLS_POP(base, class_idx);
|
|
PTR_TRACK_HEADER_READ(base, got);
|
|
if (__builtin_expect(got != expect, 0)) {
|
|
#if !HAKMEM_BUILD_RELEASE
|
|
fprintf(stderr,
|
|
"[TLS_SLL_POP] CORRUPTED HEADER cls=%d base=%p got=0x%02x expect=0x%02x\n",
|
|
class_idx, base, got, expect);
|
|
ptr_trace_dump_now("header_corruption");
|
|
abort();
|
|
#else
|
|
// In release, fail-safe: drop list.
|
|
g_tls_sll[class_idx].head = NULL;
|
|
g_tls_sll[class_idx].count = 0;
|
|
{
|
|
static int g_sll_ring_en = -1;
|
|
if (__builtin_expect(g_sll_ring_en == -1, 0)) {
|
|
const char* r = getenv("HAKMEM_TINY_SLL_RING");
|
|
g_sll_ring_en = (r && *r && *r != '0') ? 1 : 0;
|
|
}
|
|
if (__builtin_expect(g_sll_ring_en, 0)) {
|
|
// aux encodes: high 8 bits = got, low 8 bits = expect
|
|
uintptr_t aux = ((uintptr_t)got << 8) | (uintptr_t)expect;
|
|
tiny_debug_ring_record(0x7F12 /*TLS_SLL_HDR_CORRUPT*/, (uint16_t)class_idx, base, aux);
|
|
}
|
|
}
|
|
return false;
|
|
#endif
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// Read next via Box API.
|
|
void* next;
|
|
PTR_NEXT_READ("tls_pop", class_idx, base, 0, next);
|
|
|
|
#if !HAKMEM_BUILD_RELEASE
|
|
if (next && !validate_ptr_range(next, "tls_sll_pop_next")) {
|
|
fprintf(stderr,
|
|
"[TLS_SLL_POP] FATAL invalid next ptr cls=%d base=%p next=%p\n",
|
|
class_idx, base, next);
|
|
ptr_trace_dump_now("next_corruption");
|
|
abort();
|
|
}
|
|
#endif
|
|
|
|
g_tls_sll[class_idx].head = next;
|
|
if (g_tls_sll[class_idx].count > 0) {
|
|
g_tls_sll[class_idx].count--;
|
|
}
|
|
|
|
// Clear next inside popped node to avoid stale-chain issues.
|
|
tiny_next_write(class_idx, base, NULL);
|
|
|
|
*out = base;
|
|
return true;
|
|
}
|
|
|
|
// ========== Splice ==========
|
|
//
|
|
// Splice a pre-linked chain of BASE pointers into TLS SLL head.
|
|
// chain_head is BASE; links are via Box API-compatible next layout.
|
|
// Returns number of nodes actually moved (<= capacity remaining).
|
|
|
|
static inline uint32_t tls_sll_splice(int class_idx,
|
|
void* chain_head,
|
|
uint32_t count,
|
|
uint32_t capacity)
|
|
{
|
|
HAK_CHECK_CLASS_IDX(class_idx, "tls_sll_splice");
|
|
|
|
if (!chain_head || count == 0 || capacity == 0) {
|
|
return 0;
|
|
}
|
|
|
|
uint32_t cur = g_tls_sll[class_idx].count;
|
|
if (cur >= capacity) {
|
|
return 0;
|
|
}
|
|
|
|
uint32_t room = capacity - cur;
|
|
uint32_t to_move = (count < room) ? count : room;
|
|
|
|
// Traverse chain up to to_move, validate, and find tail.
|
|
void* tail = chain_head;
|
|
uint32_t moved = 1;
|
|
|
|
tls_sll_debug_guard(class_idx, chain_head, "splice_head");
|
|
|
|
#if HAKMEM_TINY_HEADER_CLASSIDX
|
|
// Restore header defensively on each node we touch.
|
|
{
|
|
uint8_t* b = (uint8_t*)chain_head;
|
|
uint8_t expected = (uint8_t)(HEADER_MAGIC | (class_idx & HEADER_CLASS_MASK));
|
|
*b = expected;
|
|
}
|
|
#endif
|
|
|
|
while (moved < to_move) {
|
|
tls_sll_debug_guard(class_idx, tail, "splice_traverse");
|
|
|
|
void* next;
|
|
PTR_NEXT_READ("tls_splice_trav", class_idx, tail, 0, next);
|
|
|
|
if (!next) {
|
|
break;
|
|
}
|
|
|
|
#if HAKMEM_TINY_HEADER_CLASSIDX
|
|
{
|
|
uint8_t* b = (uint8_t*)next;
|
|
uint8_t expected = (uint8_t)(HEADER_MAGIC | (class_idx & HEADER_CLASS_MASK));
|
|
*b = expected;
|
|
}
|
|
#endif
|
|
|
|
tail = next;
|
|
moved++;
|
|
}
|
|
|
|
// Link tail to existing head and install new head.
|
|
tls_sll_debug_guard(class_idx, tail, "splice_tail");
|
|
PTR_NEXT_WRITE("tls_splice_link", class_idx, tail, 0, g_tls_sll[class_idx].head);
|
|
|
|
g_tls_sll[class_idx].head = chain_head;
|
|
g_tls_sll[class_idx].count = cur + moved;
|
|
|
|
return moved;
|
|
}
|
|
|
|
#endif // TLS_SLL_BOX_H
|