Add sentinel detection guards (defense-in-depth)
PARTIAL FIX: Add sentinel detection at 3 critical push points to prevent sentinel-poisoned nodes from entering TLS caches. These guards provide defense-in-depth against remote free sentinel leaks. Sentinel Attack Vector (from Task agent analysis): 1. Remote free writes SENTINEL (0xBADA55BADA55BADA) to node->next 2. Node propagates through: freelist → TLS list → fast cache 3. Fast cache pop tries to dereference sentinel → SEGV Fixes Applied: 1. **tls_sll_pop()** (core/box/tls_sll_box.h:235-252) - Check if TLS SLL head == SENTINEL before dereferencing - Reset TLS state and log detection - Trigger refill path instead of crash 2. **tiny_fast_push()** (core/hakmem_tiny_fastcache.inc.h:105-130) - Check both `ptr` and `ptr->next` for sentinel before pushing to fast cache - Reject sentinel-poisoned nodes with logging - Prevents sentinel from reaching the critical pop path 3. **tls_list_push()** (core/hakmem_tiny_tls_list.h:69-91) - Check both `node` and `node->next` for sentinel before pushing to TLS list - Defense-in-depth layer to catch sentinel earlier in the pipeline - Prevents propagation to downstream caches Logging Strategy: - Limited to 5 occurrences per thread (prevents log spam) - Identifies which class and pointer triggered detection - Helps trace sentinel leak source Current Status: ⚠️ Sentinel checks added but NOT yet effective - bench_random_mixed 100K: Still crashes at iteration 66152 - NO sentinel detection logs appear - Suggests either: 1. Sentinel is not the root cause 2. Crash happens before checks are reached 3. Different code path is active Further Investigation Needed: - Disassemble crash location to identify exact code path - Check if HAKMEM_TINY_AGGRESSIVE_INLINE uses different code - Investigate alternative crash causes (buffer overflow, use-after-free, etc.) Testing: - bench_random_mixed_hakmem 1K-66K: PASS (8M ops/s) - bench_random_mixed_hakmem 67K+: FAIL (crashes at 66152) - Sentinel logs: NONE (checks not triggered) Related: Previous commit fixed 8 USER/BASE conversion bugs (14K→66K stability) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@ -18,6 +18,8 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdatomic.h>
|
||||
#include "tiny_remote.h" // For TINY_REMOTE_SENTINEL detection
|
||||
#include "box/tiny_next_ptr_box.h" // For tiny_next_read()
|
||||
|
||||
// External TLS variables
|
||||
extern int g_fast_enable;
|
||||
@ -81,16 +83,14 @@ static inline __attribute__((always_inline)) void* tiny_fast_pop(int class_idx)
|
||||
if (!head) return NULL;
|
||||
// Phase 7: header-aware next pointer (C0-C6: base+1, C7: base)
|
||||
#if HAKMEM_TINY_HEADER_CLASSIDX
|
||||
const size_t next_offset = (class_idx == 7) ? 0 : 1;
|
||||
// Phase E1-CORRECT: ALL classes have 1-byte header, next ptr at offset 1
|
||||
const size_t next_offset = 1;
|
||||
#else
|
||||
const size_t next_offset = 0;
|
||||
#endif
|
||||
// Use safe unaligned load for "next" to avoid UB when offset==1
|
||||
void* next = NULL;
|
||||
{
|
||||
#include "tiny_nextptr.h"
|
||||
next = tiny_next_load(head, class_idx);
|
||||
}
|
||||
// Phase E1-CORRECT: Use Box API for next pointer read
|
||||
#include "box/tiny_next_ptr_box.h"
|
||||
void* next = tiny_next_read(class_idx, head);
|
||||
g_fast_head[class_idx] = next;
|
||||
uint16_t count = g_fast_count[class_idx];
|
||||
if (count > 0) {
|
||||
@ -98,17 +98,38 @@ static inline __attribute__((always_inline)) void* tiny_fast_pop(int class_idx)
|
||||
} else {
|
||||
g_fast_count[class_idx] = 0;
|
||||
}
|
||||
// CRITICAL FIX: Convert base -> user pointer for classes 0-6
|
||||
// Headerless class (1KB): clear embedded next pointer before returning to user
|
||||
if (__builtin_expect(class_idx == 7, 0)) {
|
||||
*(void**)head = NULL;
|
||||
return head; // C7: return base (headerless)
|
||||
}
|
||||
// C0-C6: return user pointer (base+1)
|
||||
// Phase E1-CORRECT: All classes return user pointer (base+1)
|
||||
return (void*)((uint8_t*)head + 1);
|
||||
}
|
||||
|
||||
static inline __attribute__((always_inline)) int tiny_fast_push(int class_idx, void* ptr) {
|
||||
// ✅ CRITICAL FIX: Prevent sentinel-poisoned nodes from entering fast cache
|
||||
// Remote free operations can write SENTINEL to node->next, which eventually
|
||||
// propagates through freelist → TLS list → fast cache. If we push such a node,
|
||||
// the next pop will try to dereference the sentinel → SEGV!
|
||||
if (__builtin_expect((uintptr_t)ptr == TINY_REMOTE_SENTINEL, 0)) {
|
||||
static __thread int sentinel_ptr_logged = 0;
|
||||
if (sentinel_ptr_logged < 5) {
|
||||
fprintf(stderr, "[FAST_PUSH_SENTINEL] cls=%d ptr=%p BLOCKED (ptr is sentinel)!\n",
|
||||
class_idx, ptr);
|
||||
sentinel_ptr_logged++;
|
||||
}
|
||||
return 0; // Reject push
|
||||
}
|
||||
|
||||
// ✅ CRITICAL FIX #2: Also check if node's NEXT pointer is sentinel (defense-in-depth)
|
||||
// This catches nodes that have sentinel in their next field (from remote free)
|
||||
void* next_check = tiny_next_read(class_idx, ptr);
|
||||
if (__builtin_expect((uintptr_t)next_check == TINY_REMOTE_SENTINEL, 0)) {
|
||||
static __thread int sentinel_next_logged = 0;
|
||||
if (sentinel_next_logged < 5) {
|
||||
fprintf(stderr, "[FAST_PUSH_NEXT_SENTINEL] cls=%d ptr=%p next=%p BLOCKED (next is sentinel)!\n",
|
||||
class_idx, ptr, next_check);
|
||||
sentinel_next_logged++;
|
||||
}
|
||||
return 0; // Reject push
|
||||
}
|
||||
|
||||
if (!g_fast_enable) {
|
||||
g_fast_push_disabled[class_idx]++;
|
||||
tiny_fast_debug_log(class_idx, "disabled", 0, 0);
|
||||
@ -128,14 +149,14 @@ static inline __attribute__((always_inline)) int tiny_fast_push(int class_idx, v
|
||||
}
|
||||
// Phase 7: header-aware next pointer (C0-C6: base+1, C7: base)
|
||||
#if HAKMEM_TINY_HEADER_CLASSIDX
|
||||
const size_t next_offset2 = (class_idx == 7) ? 0 : 1;
|
||||
// Phase E1-CORRECT: ALL classes have 1-byte header, next ptr at offset 1
|
||||
const size_t next_offset2 = 1;
|
||||
#else
|
||||
const size_t next_offset2 = 0;
|
||||
#endif
|
||||
{
|
||||
#include "tiny_nextptr.h"
|
||||
tiny_next_store(ptr, class_idx, g_fast_head[class_idx]);
|
||||
}
|
||||
// Phase E1-CORRECT: Use Box API for next pointer write
|
||||
#include "box/tiny_next_ptr_box.h"
|
||||
tiny_next_write(class_idx, ptr, g_fast_head[class_idx]);
|
||||
g_fast_head[class_idx] = ptr;
|
||||
g_fast_count[class_idx] = (uint16_t)(count + 1);
|
||||
g_fast_push_hits[class_idx]++;
|
||||
|
||||
Reference in New Issue
Block a user