117 lines
8.3 KiB
Markdown
117 lines
8.3 KiB
Markdown
|
|
# TLS SLL Header Corruption Investigation
|
|||
|
|
|
|||
|
|
## 症状とスコープ
|
|||
|
|
- sh8bench 実行時に `[TLS_SLL_HDR_RESET] cls=1 base=0x715210fbf858 got=0x51 expect=0xa1 count=0` などが発生(class 6 の `got=0x00 expect=0xa6` も報告)。
|
|||
|
|
- 発生地点: `core/box/tls_sll_box.h` の TLS SLL pop 境界(Header Box による検証失敗 → SLL をリセット)。
|
|||
|
|
- 再現範囲: sh8bench でほぼ毎回。Larson/cfrac では再現しにくい。
|
|||
|
|
|
|||
|
|
## 箱マップ(Box Theory)
|
|||
|
|
- **Header Box (`core/box/tiny_header_box.h`)**: C1-C6 はヘッダー保持(HEADER_MAGIC|class_idx)。`tiny_header_write_if_preserved()` / `tiny_header_validate()` が単一の境界 API。
|
|||
|
|
- **Freelist Box**: freelist ↔ TLS SLL の橋渡し。ここでヘッダーを復元しないと TLS SLL pop で Fail-Fast する。
|
|||
|
|
- **TLS SLL Box (`core/box/tls_sll_box.h`)**: push でヘッダーを復元・検証、pop でヘッダー検証&リセット。リングイベント `TINY_RING_EVENT_TLS_SLL_HDR_CORRUPT` あり。
|
|||
|
|
- **Class Map Box**: class_idx をヘッダー無しで特定する高速経路。デフォルトで header write をスキップするため、TLS SLL がヘッダーに依存する場合は境界不整合が起きる。
|
|||
|
|
|
|||
|
|
## TLS_SLL_HDR_RESET 詳細分析
|
|||
|
|
- pop 経路は `tiny_class_preserves_header(class_idx)` が true (C1-C6) の場合に必ず `tiny_header_validate()` を呼ぶ。失敗時は SLL をリセットして `[TLS_SLL_HDR_RESET]` を 10k 回に 1 回だけ出力。
|
|||
|
|
- `core/tiny_region_id.h` では **デフォルト `g_write_header=1`**(A/B ガードで `HAKMEM_TINY_WRITE_HEADER=0` による切替可)。ヘッダーを書かずに freelist に入ると、Freelist→TLS SLL の移送時に復元漏れがあると pop で 0x00/0x51/0x61 を読み出してリセットする。
|
|||
|
|
- 既知の漏れ(修正済みの根治ポイント)
|
|||
|
|
- `core/box/carve_push_box.c: box_carve_and_push_with_freelist()` で freelist pop→push 前にヘッダー未復元(commit `3c6c76cb1` で修正)。
|
|||
|
|
- `core/hakmem_tiny_free.inc: tiny_drain_freelist_to_sll_once()` のデッドパスでも同様に未復元(commit `a94344c1a` で修正)。
|
|||
|
|
- 破損パターン
|
|||
|
|
- `got=0x00`: mmap zero 由来の stale data
|
|||
|
|
- `got=0x51 / 0x61`: 前世の payload 断片(ヘッダー未書き込み時に漏れる)
|
|||
|
|
|
|||
|
|
## 3つの仮説
|
|||
|
|
- **a. ヘッダー書き込みの未実行(最有力)**
|
|||
|
|
- デフォルトで header write OFF(`g_write_header=0`)+ freelist→TLS SLL での復元漏れが重なると、pop 側の検証で Fail-Fast する。上記 2 箇所の修正がこれを潰す。
|
|||
|
|
- **b. TLS SLL と Class Map の境界不整合**
|
|||
|
|
- Class Map は「ヘッダー不要」を前提、一方 TLS SLL pop はヘッダー必須。境界(freelist→TLS SLL)でヘッダーを必ず再構成するか、Class Map 情報に切り替えるかのどちらかに統一すべき。
|
|||
|
|
- **c. sh8bench 固有パターン**
|
|||
|
|
- 8 スレッドで class 1 の高速 churn により freelist→TLS SLL デッドパス(drain/carve)の頻度が高い。ヘッダー復元漏れが顕在化しやすい。
|
|||
|
|
|
|||
|
|
## 今回の実行結果
|
|||
|
|
- `LD_PRELOAD=./libhakmem.so`(リリースビルド)は `unified_cache_refill+0x46f` で SEGFAULT(`mov 0x1(%r15),%rdx`)。`r15` は TLS freelist から読みだしたポインタで、無効値を指していた。→ リリース版で sh8bench を回す前に unified cache 側のクラッシュ修正が必要。
|
|||
|
|
- `LD_PRELOAD="/usr/lib/x86_64-linux-gnu/libasan.so.6 ./libhakmem_asan.so"` で代替実行(LeakSanitizer のリーク警告付きだが完走):
|
|||
|
|
- ベースライン + `HAKMEM_TINY_SLL_RING=1`: `[TLS_SLL_HDR_RESET]` ゼロ件(`sh8bench_baseline.log`)。
|
|||
|
|
- `HAKMEM_TINY_WRITE_HEADER=1` A/B(5 回)+ Ring: すべて `[TLS_SLL_HDR_RESET]` ゼロ件(`sh8bench_header_on_run*.log`)。
|
|||
|
|
- `HAKMEM_TINY_SLL_SAFEHEADER=1 HAKMEM_TINY_SLL_VALIDATE_HDR=1 HAKMEM_DEBUG_LEVEL=3` + Ring: `[TLS_SLL_PUSH_BAD_HDR]` / `[TLS_SLL_REJECT]` ゼロ件(`sh8bench_safeheader.log`)。
|
|||
|
|
- 現時点では TLS_SLL_HDR_RESET は再現せず。リリース版の unified cache 落ちを直した上で再確認が必要。
|
|||
|
|
|
|||
|
|
## デバッグ手順(4ステップ)
|
|||
|
|
1. **現状再現とリング取得**
|
|||
|
|
```bash
|
|||
|
|
cd /mnt/workdisk/public_share/hakmem
|
|||
|
|
HAKMEM_TINY_SLL_RING=1 LD_PRELOAD=./libhakmem.so \
|
|||
|
|
./mimalloc-bench/out/bench/sh8bench 2>&1 | \
|
|||
|
|
tee sh8bench_baseline.log | grep -E "TLS_SLL_HDR_RESET|TLS_SLL_HDR_CORRUPT|Total elapsed"
|
|||
|
|
```
|
|||
|
|
- 目的: `[TLS_SLL_HDR_RESET]` 発生位置と ring payload (got/expect) を確定。
|
|||
|
|
|
|||
|
|
2. **ヘッダー強制書き込みで A/B**(仮説 a の確認)
|
|||
|
|
```bash
|
|||
|
|
for i in {1..5}; do
|
|||
|
|
echo "=== header_on run $i ==="
|
|||
|
|
HAKMEM_TINY_WRITE_HEADER=1 HAKMEM_TINY_SLL_RING=1 LD_PRELOAD=./libhakmem.so \
|
|||
|
|
./mimalloc-bench/out/bench/sh8bench 2>&1 | \
|
|||
|
|
grep -E "TLS_SLL_HDR_RESET|Total elapsed"
|
|||
|
|
done
|
|||
|
|
```
|
|||
|
|
- 消えれば「ヘッダー未復元」が原因と確定。
|
|||
|
|
|
|||
|
|
3. **push 側の境界検証を強化**
|
|||
|
|
```bash
|
|||
|
|
HAKMEM_TINY_SLL_SAFEHEADER=1 HAKMEM_TINY_SLL_VALIDATE_HDR=1 HAKMEM_DEBUG_LEVEL=3 \
|
|||
|
|
LD_PRELOAD=./libhakmem.so ./mimalloc-bench/out/bench/sh8bench \
|
|||
|
|
2>&1 | tee sh8bench_safeheader.log
|
|||
|
|
```
|
|||
|
|
- `TLS_SLL_PUSH_BAD_HDR` / `TLS_SLL_REJECT` が出れば freelist→TLS SLL 境界で復元漏れが残っている。ring で callsite を特定。
|
|||
|
|
|
|||
|
|
4. **修正後のリグレッション確認**
|
|||
|
|
```bash
|
|||
|
|
# sh8bench 10 回
|
|||
|
|
for i in {1..10}; do LD_PRELOAD=./libhakmem.so ./mimalloc-bench/out/bench/sh8bench \
|
|||
|
|
2>&1 | grep -E "TLS_SLL_HDR_RESET|Total elapsed"; done
|
|||
|
|
|
|||
|
|
# Larson / cfrac で広めに確認
|
|||
|
|
LD_PRELOAD=./libhakmem.so ./mimalloc-bench/out/bench/larson 10 7 8 100 10000
|
|||
|
|
LD_PRELOAD=./libhakmem.so ./mimalloc-bench/out/bench/cfrac 17545186520507317056371138836327483792789528
|
|||
|
|
```
|
|||
|
|
- 目的: sh8bench だけでなく他ワークロードでも Fail-Fast が出ないことを確認。
|
|||
|
|
|
|||
|
|
## 修正パターン(根治案)
|
|||
|
|
- **パターンA: 境界一箇所でヘッダー復元を徹底(推奨)**
|
|||
|
|
- Freelist→TLS SLL を 1 つの Box 境界として `tiny_header_write_if_preserved()` を必ず実行(現行の `box_carve_and_push_with_freelist()` / `tiny_drain_freelist_to_sll_once()` の形を維持)。
|
|||
|
|
- 追加ガード: `HAKMEM_TINY_SLL_SAFEHEADER=1` を運用フラグとして残し、違反時は push 拒否+リング記録。
|
|||
|
|
|
|||
|
|
- **パターンB: ヘッダー常時書き込みに戻す**
|
|||
|
|
- `core/tiny_region_id.h` の `g_write_header` をデフォルト `1` にして Class Map 依存を減らす。
|
|||
|
|
- **実装済み**: デフォルト ON に変更(`HAKMEM_TINY_WRITE_HEADER=0` で旧挙動に戻せる)。
|
|||
|
|
- A/B 切替: 環境変数 `HAKMEM_TINY_WRITE_HEADER=0` でオフにできるよう保持(切り戻し容易)。
|
|||
|
|
|
|||
|
|
- **パターンC: TLS SLL を Class Map 準拠にリファクタ**
|
|||
|
|
- `tls_sll_pop()` でヘッダー検証を `tiny_header_validate()`→`class_map_lookup()` の優先順に変更(ヘッダーが無い場合は Class Map で検証)。
|
|||
|
|
- `tiny_header_write_if_preserved()` を「Best Effort」扱いにしつつ、リングで Class Map vs header の不一致を Fail-Fast ログする。
|
|||
|
|
|
|||
|
|
## 環境変数 / ビルド / テスト
|
|||
|
|
- **デバッグフラグ**
|
|||
|
|
- `HAKMEM_TINY_WRITE_HEADER=1` : ヘッダー強制書き込み(仮説 a 検証)
|
|||
|
|
- `HAKMEM_TINY_SLL_RING=1` : TLS SLL イベントをリング出力
|
|||
|
|
- `HAKMEM_TINY_SLL_VALIDATE_HDR=1` または `HAKMEM_DEBUG_LEVEL=3` : push でのヘッダー検証を常時 ON
|
|||
|
|
- `HAKMEM_TINY_SLL_SAFEHEADER=1` : ヘッダー不一致時は push 拒否(Fail-Fast)
|
|||
|
|
- `HAKMEM_WRAP_DIAG=1` : Wrapper トレース
|
|||
|
|
- **ビルド**
|
|||
|
|
```bash
|
|||
|
|
cd /mnt/workdisk/public_share/hakmem
|
|||
|
|
rm -f *.o libhakmem.so
|
|||
|
|
make shared -j8 # Release
|
|||
|
|
make shared -j8 EXTRA_CFLAGS="-g -O0 -UHAKMEM_BUILD_RELEASE" # Debug
|
|||
|
|
```
|
|||
|
|
- **テスト**(再掲)
|
|||
|
|
- `LD_PRELOAD=./libhakmem.so ./mimalloc-bench/out/bench/sh8bench`
|
|||
|
|
- `LD_PRELOAD=./libhakmem.so ./mimalloc-bench/out/bench/larson 10 7 8 100 10000`
|
|||
|
|
- いずれも `grep -E "TLS_SLL_HDR_RESET|TLS_SLL_HDR_CORRUPT"` でゼロ件を確認。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
根治の原則: 「ヘッダーを書かないなら検証しない」「検証するなら境界で必ず書く」を Box 境界 1 箇所に閉じ込め、Fail-Fast とリング可視化で sh8bench パターンでも再発しないことを確認する。
|