根治修正: unified_cache_refill SEGVAULT + コンパイラ最適化対策

問題:
  - リリース版sh8benchでunified_cache_refill+0x46fでSEGVAULT
  - コンパイラ最適化により、ヘッダー書き込みとtiny_next_read()の
    順序が入れ替わり、破損したポインタをout[]に格納

根本原因:
  - ヘッダー書き込みがtiny_next_read()の後にあった
  - volatile barrierがなく、コンパイラが自由に順序を変更
  - ASan版では最適化が制限されるため問題が隠蔽されていた

修正内容(P1-P3):

P1: unified_cache_refill SEGVAULT修正 (core/front/tiny_unified_cache.c:341-350)
  - ヘッダー書き込みをtiny_next_read()の前に移動
  - __atomic_thread_fence(__ATOMIC_RELEASE)追加
  - コンパイラ最適化による順序入れ替えを防止

P2: 二重書き込み削除 (core/box/tiny_front_cold_box.h:75-82)
  - tiny_region_id_write_header()削除
  - unified_cache_refillが既にヘッダー書き込み済み
  - 不要なメモリ操作を削除して効率化

P3: tiny_next_read()安全性強化 (core/tiny_nextptr.h:73-86)
  - __atomic_thread_fence(__ATOMIC_ACQUIRE)追加
  - メモリ操作の順序を保証

P4: ヘッダー書き込みデフォルトON (core/tiny_region_id.h - ChatGPT修正)
  - g_write_headerのデフォルトを1に変更
  - HAKMEM_TINY_WRITE_HEADER=0で旧挙動に戻せる

テスト結果:
   unified_cache_refill SEGVAULT: 解消(sh8bench実行可能に)
   TLS_SLL_HDR_RESET: まだ発生中(別の根本原因、調査継続)

🤖 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-12-03 09:57:12 +09:00
parent 4cc2d8addf
commit 6154e7656c
4 changed files with 28 additions and 14 deletions

View File

@ -72,9 +72,10 @@ static inline void* tiny_cold_refill_and_alloc(int class_idx) {
return NULL; return NULL;
} }
// Success: Write header + return USER pointer // Success: return USER pointer
// NOTE: Header already written by unified_cache_refill()
// (Removed redundant tiny_region_id_write_header() - P2 fix)
#ifdef HAKMEM_TINY_HEADER_CLASSIDX #ifdef HAKMEM_TINY_HEADER_CLASSIDX
tiny_region_id_write_header(base, class_idx);
return (void*)((char*)base + 1); // USER pointer return (void*)((char*)base + 1); // USER pointer
#else #else
return base; return base;

View File

@ -337,6 +337,17 @@ void* unified_cache_refill(int class_idx) {
if (m->freelist) { if (m->freelist) {
// Freelist pop // Freelist pop
void* p = m->freelist; void* p = m->freelist;
// ROOT CAUSE FIX: Write header BEFORE tiny_next_read()
// Without this, compiler can reorder header write after out[] assignment
// causing SEGVAULT in release builds (unified_cache_refill+0x46f)
#if HAKMEM_TINY_HEADER_CLASSIDX
*(uint8_t*)p = (uint8_t)(0xa0 | (class_idx & 0x0f));
// Prevent compiler from reordering operations
__atomic_thread_fence(__ATOMIC_RELEASE);
#endif
m->freelist = tiny_next_read(class_idx, p); m->freelist = tiny_next_read(class_idx, p);
unified_refill_validate_base(class_idx, tls, m, p, unified_refill_validate_base(class_idx, tls, m, p,
@ -345,11 +356,6 @@ void* unified_cache_refill(int class_idx) {
// PageFaultTelemetry: record page touch for this BASE // PageFaultTelemetry: record page touch for this BASE
pagefault_telemetry_touch(class_idx, p); pagefault_telemetry_touch(class_idx, p);
// ✅ CRITICAL: Restore header (overwritten by freelist link)
#if HAKMEM_TINY_HEADER_CLASSIDX
*(uint8_t*)p = (uint8_t)(0xa0 | (class_idx & 0x0f));
#endif
m->used++; m->used++;
out[produced++] = p; out[produced++] = p;

View File

@ -68,7 +68,11 @@ static inline __attribute__((always_inline)) void* tiny_next_load(const void* ba
if (off == 0) { if (off == 0) {
// Aligned access at base (header無し or C7 freelist時) // Aligned access at base (header無し or C7 freelist時)
return *(void* const*)base; void* next = *(void* const*)base;
// P3: Prevent compiler from reordering this load
__atomic_thread_fence(__ATOMIC_ACQUIRE);
return next;
} }
// off != 0: use memcpy to avoid UB on architectures that forbid unaligned loads. // off != 0: use memcpy to avoid UB on architectures that forbid unaligned loads.
@ -76,6 +80,9 @@ static inline __attribute__((always_inline)) void* tiny_next_load(const void* ba
void* next = NULL; void* next = NULL;
const uint8_t* p = (const uint8_t*)base + off; const uint8_t* p = (const uint8_t*)base + off;
memcpy(&next, p, sizeof(void*)); memcpy(&next, p, sizeof(void*));
// P3: Prevent compiler from reordering this load
__atomic_thread_fence(__ATOMIC_ACQUIRE);
return next; return next;
} }

View File

@ -238,16 +238,16 @@ static inline void* tiny_region_id_write_header(void* base, int class_idx) {
} while (0); } while (0);
#endif // !HAKMEM_BUILD_RELEASE #endif // !HAKMEM_BUILD_RELEASE
// P3: Skip header write when class_map is active (default) // P3: Header writeをデフォルトONTLS SLL向けに常時復元、ENVでOFF可能
// class_map provides class_idx lookup, so header byte is no longer needed // class_map があっても TLS SLL 境界でヘッダーが必要になるため、A/B 切替は
// ENV: HAKMEM_TINY_WRITE_HEADER=1 to force header write (legacy mode) // HAKMEM_TINY_WRITE_HEADER=0 でのみ OFF旧デフォルトにする。
// Memory layout preserved: user = base + 1 (1B unused when skipped) // Memory layout preserved: user = base + 1(ヘッダー領域は常に予約)
static int g_write_header = -1; static int g_write_header = -1;
if (__builtin_expect(g_write_header == -1, 0)) { if (__builtin_expect(g_write_header == -1, 0)) {
const char* e = getenv("HAKMEM_TINY_WRITE_HEADER"); const char* e = getenv("HAKMEM_TINY_WRITE_HEADER");
g_write_header = (e && *e && *e != '0') ? 1 : 0; g_write_header = (e && *e && *e == '0') ? 0 : 1;
} }
if (__builtin_expect(g_write_header, 0)) { if (__builtin_expect(g_write_header, 1)) {
// Legacy mode: write header for debugging or compatibility // Legacy mode: write header for debugging or compatibility
*header_ptr = HEADER_MAGIC | (class_idx & HEADER_CLASS_MASK); *header_ptr = HEADER_MAGIC | (class_idx & HEADER_CLASS_MASK);
PTR_TRACK_HEADER_WRITE(base, HEADER_MAGIC | (class_idx & HEADER_CLASS_MASK)); PTR_TRACK_HEADER_WRITE(base, HEADER_MAGIC | (class_idx & HEADER_CLASS_MASK));