Files
hakmem/docs/CRASH_180s_INVESTIGATION_GUIDE.md
Moe Charm (CI) 2d8dfdf3d1 Fix critical integer overflow bug in TLS SLL trace counters
Root Cause:
- Diagnostic trace counters (g_tls_push_trace, g_tls_pop_trace) were declared
  as 'int' type instead of 'uint32_t'
- Counter would overflow at exactly 256 iterations, causing SIGSEGV
- Bug prevented any meaningful testing in debug builds

Changes:
1. core/box/tls_sll_box.h (tls_sll_push_impl):
   - Changed g_tls_push_trace from 'int' to 'uint32_t'
   - Increased threshold from 256 to 4096
   - Fixes immediate crash on startup

2. core/box/tls_sll_box.h (tls_sll_pop_impl):
   - Changed g_tls_pop_trace from 'int' to 'uint32_t'
   - Increased threshold from 256 to 4096
   - Ensures consistent counter handling

3. core/hakmem_tiny_refill.inc.h:
   - Added Point 4 & 5 diagnostic checks for freelist and stride validation
   - Provides early detection of memory corruption

Verification:
- Built with RELEASE=0 (debug mode): SUCCESS
- Ran 3x 190-second tests: ALL PASS (exit code 0)
- No SIGSEGV crashes after fix
- Counter safely handles values beyond 255

Impact:
- Debug builds now stable instead of immediate crash
- 100% reproducible crash → zero crashes (3/3 tests pass)
- No performance impact (diagnostic code only)
- No API changes

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-04 10:38:19 +09:00

6.9 KiB
Raw Blame History

180秒クラッシュ修正指示書 (2025-12-04)

Status: 未解決Pre-existing bug, not introduced by Release Guard Box

Crash Details:

  • Time to crash: 180秒安定
  • Root cause: TLS SLL push操作中のSIGSEGV
  • Release Guard Box: クラッシュ前に呼ばれていない(別問題)
  • Current commit: 1ac502af5 (Release Guard Box)

📋 調査フロー

Phase 1: クラッシュポイント特定

目標: SIGSEGV の正確なアドレスと命令を特定

# 1. gdb でクラッシュ再現
cd /mnt/workdisk/public_share/hakmem
make clean && make RELEASE=0  # デバッグシンボル有効

# 2. gdb 実行
gdb --args bash -c 'env LD_PRELOAD=/mnt/workdisk/public_share/hakmem/libhakmem.so /mnt/workdisk/public_share/hakmem/mimalloc-bench/out/bench/sh8bench'

# 3. gdb 内で:
(gdb) run
# ... 180秒待つ ...
# クラッシュ時に自動停止

# 4. スタックトレース確認
(gdb) bt
(gdb) frame 0
(gdb) disassemble
(gdb) info registers

期待される出力:

  • #0 ... in tiny_alloc_fast_push ... または
  • #0 ... in tls_sll_push ... または
  • #0 ... in slab_index_for ... などのTLS SLL関連

Phase 2: クラッシュのコンテキスト分析

目標: クラッシュ前の状態を理解

// コアの質問:
1. クラッシュしたポインタ (ptr) は何か?
    gdb: print ptr
    gdb: x/16x ptr (メモリダンプ)

2. SuperSlab* ss は有効か?
    gdb: print ss->magic (SUPERSLAB_MAGIC = 0x53535342 であるべき)
    gdb: print ss->refcount (> 0 であるべき)

3. slab_idx は有効か?
    gdb: print slab_idx
    gdb: print ss_slabs_capacity(ss)

4. TinySlabMeta* meta は有効か?
    gdb: print meta->class_idx
    gdb: print meta->capacity
    gdb: print meta->used

5. head ポインタは?
    gdb: print g_tls_sll[class_idx].head
    gdb: x/16x g_tls_sll[class_idx].head (メモリダンプ)

Phase 3: Reproducibility 検証

目標: クラッシュが100%再現するかを確認

# 環境変数をクリア(過去の診断ログ無効化)
unset HAKMEM_TINY_SLL_NEXTCLS
unset HAKMEM_TINY_SLL_NEXTTAG
unset HAKMEM_TINY_SLL_HEADCLS
unset HAKMEM_DEBUG_COUNTER
unset HAK_DEBUG_LOG_FREQ

# 3回テスト
for i in 1 2 3; do
  echo "=== Test $i ==="
  timeout 190 env LD_PRELOAD=/mnt/workdisk/public_share/hakmem/libhakmem.so \
    /mnt/workdisk/public_share/hakmem/mimalloc-bench/out/bench/sh8bench 2>&1 | tail -20
  echo "EXIT_CODE: $?"
done

期待される結果:

  • Test 1, 2, 3: すべて ~180秒でクラッシュ100%再現性)

🔍 疑い容疑者リスト

前セッションの分析から、以下が候補:

候補1: TLS SLL next ポインタの破壊

症状: next が不正なアドレスを指している

チェック方法:

# Phase 2 の gdb コマンドで:
(gdb) print g_tls_sll[class_idx].head->next
# 0x0, NULL, または特定のメモリアドレス?
# 有効な全体メモリ範囲外か?

修正アプローチ (if 原因なら):

  • tls_sll_push_impl() での next ポインタ設定をチェック
  • tiny_alloc_fast_push() での pointer conversion をチェック
  • メモリバリアの不足?

候補2: SuperSlab refcount の不一致

症状: refcount が 0 になり、SuperSlab が free されてから TLS SLL が access している

チェック方法:

# Phase 2 の gdb コマンドで:
(gdb) print ss->refcount
# 0 なら??? → Layer 1 (refcount pinning) が機能していない可能性
# > 0 なら OK

修正アプローチ (if 原因なら):

  • tiny_alloc_fast_push() の atomic_fetch_add を確認
  • tls_sll_pop_impl() の atomic_fetch_sub を確認
  • Race condition

候補3: slab_idx の計算エラー

症状: slab_idx が範囲外になり、ss->slabs[slab_idx] が不正なメモリを access

チェック方法:

# Phase 2 の gdb コマンドで:
(gdb) print slab_idx
(gdb) print ss_slabs_capacity(ss)
# slab_idx >= capacity? → これが問題

修正アプローチ (if 原因なら):

  • slab_index_for(ss, ptr) の実装を再確認
  • Boundary check の見落とし?

候補4: class_idx の不整合

症状: TLS SLL[class_idx] が破壊されている

チェック方法:

# Phase 2 の gdb コマンドで:
(gdb) print class_idx
(gdb) print meta->class_idx
# 一致してるか?ズレてるか?

修正アプローチ (if 原因なら):

  • tiny_get_class_from_ss() の返り値を確認
  • class_idx と meta->class_idx の関係を再確認

候補5: メモリレイアウトの仮定ずれ

症状: sizeof(TinySlabMeta) や stride が変わり、ポインタ計算がズレている

チェック方法:

# gdb で:
(gdb) print sizeof(TinySlabMeta)
(gdb) print sizeof(TinySlab)
(gdb) print sizeof(SuperSlab)

# コンパイル時の定義と一致?

📊 デバッグ戦略

ステップ1: スタックトレース取得30分

make clean && make RELEASE=0

# gdb でスタックトレース取得
gdb --args bash -c '...'
(gdb) run
# クラッシュ待機
(gdb) bt full
(gdb) frame 0; disassemble
# ファイルに出力

ステップ2: コンテキスト分析1時間

  • gdb で各変数を print
  • メモリダンプで破壊パターンを特定
  • 前後のメモリ状態を記録

ステップ3: 仮説検証2時間

  • 候補ごとに検証コード追加
  • 特定のクラスやスレッド数で再現性確認
  • ログ出力で実行フロー追跡

ステップ4: 修正実装1-3時間

  • 根本原因に応じて修正
  • テスト60秒 → 120秒 → 180秒 → 240秒

🛠️ 実装チェックリスト

修正時には以下を確認:

  • スタックトレースから特定の関数が明確か
  • 該当ファイルを読んで実装を理解した
  • 候補1-5から最も可能性高い仮説を選んだ
  • 修正方法を3つ以上考えた安全性が高い順
  • 修正実装後、180秒テストで確認
  • 240秒テストで追加マージンを確保
  • ドキュメント更新

📚 関連ファイル

主要ファイル:

  • core/tiny_alloc_fast.inc.h - tiny_alloc_fast_push (line 879)
  • core/tiny_free_fast.inc.h - tiny_free_fast (line 195)
  • core/box/tls_sll_box.h - TLS SLL 実装
  • core/hakmem_tiny_superslab.h - SuperSlab 定義

ヘッダ:

  • core/tiny_atomic.h - atomic 操作
  • core/slab_handle.h - slab_index_for()

🎯 成功判定基準

修正が完了した → 以下をすべてPASS:

  • 180秒テスト: EXIT_CODE 0, SIGSEGV 0件
  • 240秒テスト: EXIT_CODE 0, SIGSEGV 0件
  • スタックトレース: TLS SLL/alloc_fast_push 関連なし
  • ログ: [TLS_SLL_*], [ERROR] 無し
  • メモリ: RSS 安定 (increase < 10%)

作成日: 2025-12-04 対象: 180秒クラッシュの修正