Fix BASE/USER pointer double conversion bugs in alloc/free fast paths

Root Cause:
- TINY_ALLOC_FAST_POP_INLINE returned USER pointer (base+1), but all other
  frontend layers return BASE pointer → HAK_RET_ALLOC wrote header/region
  at wrong offset (off-by-one)
- tiny_free_fast_ss() performed BASE conversion twice (ptr-1 then base-1)
  → Corrupted TLS SLL chain, causing SEGV at iteration 66151

Fixes:
1. tiny_alloc_fast_inline.h (Line 62):
   - Change POP macro to return BASE pointer (not USER)
   - Update PUSH macro to convert USER→BASE and restore header at BASE
   - Unify all frontend layers to "BASE world"

2. tiny_free_fast.inc.h (Line 125, 228):
   - Remove double conversion in tiny_free_fast_ss()
   - Pass BASE pointer from caller (already converted via ptr-1)
   - Add comments to prevent future regressions

Impact:
- Before: Crash at iteration 66151 (stack corruption)
- After: 100K iterations  (1.95M ops/s), 1M iterations  (840K ops/s)

Verified: Random mixed benchmark (WS=256, seeds 42-44), all tests pass.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Moe Charm (CI)
2025-11-13 07:43:30 +09:00
parent 72b38bc994
commit c28314fb96
2 changed files with 27 additions and 15 deletions

View File

@ -58,8 +58,8 @@ extern __thread uint32_t g_tls_sll_count[TINY_NUM_CLASSES];
if (g_tls_sll_count[(class_idx)] > 0) { \
g_tls_sll_count[(class_idx)]--; \
} \
/* Phase E1-CORRECT: All classes return user pointer (base+1) */ \
(ptr_out) = (void*)((uint8_t*)_head + 1); \
/* Phase 7: Fast path returns BASE pointer; HAK_RET_ALLOC does BASE→USER */ \
(ptr_out) = _head; \
} \
} else { \
(ptr_out) = NULL; \
@ -88,9 +88,14 @@ extern __thread uint32_t g_tls_sll_count[TINY_NUM_CLASSES];
// byte 0 for HEADER_MAGIC. Without restoration, it finds 0x00 → uses wrong offset → SEGV.
// COST: 1 byte write (~1-2 cycles per free, negligible).
#define TINY_ALLOC_FAST_PUSH_INLINE(class_idx, ptr) do { \
*(uint8_t*)(ptr) = HEADER_MAGIC | ((class_idx) & HEADER_CLASS_MASK); \
tiny_next_write(class_idx, (ptr), g_tls_sll_head[(class_idx)]); \
g_tls_sll_head[(class_idx)] = (ptr); \
if (!(ptr)) break; \
/* Phase E1-CORRECT: API ptr is USER pointer (= base+1). Convert back to BASE. */ \
uint8_t* _base = (uint8_t*)(ptr) - 1; \
/* Restore header at BASE (not at user). */ \
*_base = HEADER_MAGIC | ((class_idx) & HEADER_CLASS_MASK); \
/* Link node using BASE as the canonical SLL node address. */ \
tiny_next_write((class_idx), _base, g_tls_sll_head[(class_idx)]); \
g_tls_sll_head[(class_idx)] = _base; \
g_tls_sll_count[(class_idx)]++; \
} while(0)
#else