# 📊 警告: 対処療法が増殖している (2025-12-03) **Status**: 🟡 **CAUTION** - 症状を増やしてる可能性 **Commit**: 19ce4c1ac (refcount pinning) **Assessment**: 多層防御は安定性を得たが、根本原因に蓋をしてる --- ## 🚨 問題認識 ### 現在の状況 ``` ✅ sh8bench: 60秒完走(SIGSEGV ゼロ) ❌ しかし: [TLS_SLL_NEXT_INVALID] / [UNIFIED_FREELIST_INVALID] が多発 ❌ つまり: 対処療法で「症状を見えなくしてる」だけ ``` ### 対処療法の積み重ね | 層 | 対策 | 内容 | 評価 | |----|----|------|------| | 1 | refcount pinning | SuperSlab 解放を遅延 | ⚠️ 治してない | | 2 | release guards | refcount > 0 なら skip | ⚠️ 隠蔽 | | 3 | next validation | 無効pointer を drop | ⚠️ リスト喪失 | | 4 | freelist validation | 無効head を drop | ⚠️ メモリ喪失 | | 5 | early decrement fix | refcount 過剰デクリメント削除 | ⚠️ 延期しただけ | --- ## 🔍 ログから見える本当の問題 ### パターン1: 無効なスラブインデックス ``` [TLS_SLL_NEXT_INVALID] head slab_idx=56 → next が slab_idx=0x45c相当(範囲外!) ``` **解釈**: - next ポインタが「同一 SuperSlab 内」だが「範囲外」 - SuperSlab 内メモリが「スラブ単位で破壊」されてる - または「スラブインデックス計算が間違ってる」 ### パターン2: 別スラブの混入 ``` [UNIFIED_FREELIST_INVALID] TLS スラブ(ss=0x…c00000, slab=3)のfreelist → ブロック pがss+0x1302(他スラブ相当)を指す ``` **解釈**: - freelist の next が「別のスラブ」を指してる - freelist が「スラブ境界を越えてリンク」されてる - または「ポインタ計算が完全に狂ってる」 ### パターン3: userptr の混入 ``` [TLS_SLL_NORMALIZE_USERPTR] 多数 ``` **解釈**: - userptr(offset +1 したもの)が TLS SLL に push されてる - base pointer として expected なのに user pointer が来てる - 型変換の誤り が多発 --- ## 🎯 根本原因の仮説(修正版) ### 仮説A: Freelist 自体が破壊されてる ⭐⭐⭐ ```c // どこかで freelist の next を誤って上書き? tls_slab->freelist->next = GARBAGE; ``` **証拠**: - [UNIFIED_FREELIST_INVALID] が多発 - スラブ境界を越えたポインタ - unified_cache_refill で検出 **対処療法との関係**: - validation で DROP → ✅ 安定 - 但し原因は未解決 → ❌ 対症療法 --- ### 仮説B: Pointer 変換の一貫性崩壊 ⭐⭐⭐ ```c // push 時: user ptr で進む tls_sll_push(user_ptr); // user offset = +1 // pop 時: base ptr で読む? base = tls_sll_pop(); // base offset = +0 offset = base + 1; // 再度 +1 ← 二重適用? ``` **証拠**: - [TLS_SLL_NORMALIZE_USERPTR] の多発 - class 1 限定で偏ってる? **対処療法との関係**: - refcount pinning で延期 → ❌ 根本じゃない --- ### 仮説C: Slab Index 計算の誤り ⭐⭐ ```c // slab_idx 計算で off-by-one? int idx = slab_index_for(ss, ptr); // 誤計算 → 0x45c? ``` **証拠**: - next が slab_idx=0x45c(明らかに範囲外) - 同一 SuperSlab 内だが「計算が狂ってる」 --- ### 仮説D: SuperSlab 再利用時のゴミ残し ⭐ ```c // 古い SuperSlab を解放 ss_free(ss_old); // 新しい ss で同じアドレスが再割当 ss_new = ss_alloc(); // ss_old と同じアドレス // → 古い freelist が残ってる? ``` --- ## 📋 次の調査(根本を目指す) ### Task 1: 無効 next の発生源特定 ⭐⭐⭐(**優先度最高**) **実行内容**: ```bash # [TLS_SLL_NEXT_INVALID] ログから: # 1. head slab_idx を記録 # 2. next が指すアドレス (p) から slab_idx_expected を逆算 # 3. 実際の slab_index_for(ss, p) と比較 # 例: # head p=0x... slab_idx=56 # next p=0x... slab_idx_expected=0x45c(範囲外) # → slab_index_for() が何を返すか? ``` **期待成果**: - slab_index_for() が間違ってるのか - freelist が壊れてるのか - 別の問題か ### Task 2: Freelist 破壊の瞬間検出 ⭐⭐⭐(**優先度最高**) **実行内容**: ```c // tiny_free_local_box に恒常的な prev 検証を追加: // push 時に: // prev_meta->next == current? // current_meta->prev == prev? // // prev が合わないなら即座に: // fprintf(stderr, "[FREELIST_CORRUPTION_DETECTED] ..."); // freelist を drop ``` **期待成果**: - freelist が「いつ、どのタイミングで」破壊されるか - どのコード経路で壊れるのか ### Task 3: userptr 混入源の絞込 ⭐⭐ **実行内容**: ```c // クラス1限定で、TLS SLL push 前に強制base化: hak_base_ptr_t base = ptr_user_to_base(ptr, class_idx); if (!hak_base_is_valid(base)) { // A/B test: drop か error か fprintf(stderr, "[CLASS1_INVALID_BASE] ptr=%p", ptr); } ``` **期待成果**: - userptr が push されてる頻度 - 発生源(どこから来た userptr か) --- ## ⚠️ 現在の対処療法の限界 ### 何が隠蔽されてるのか | 症状 | 対処方法 | 根本原因 | |------|--------|--------| | [TLS_SLL_NEXT_INVALID] | DROP list | freelist 破壊 | | [UNIFIED_FREELIST_INVALID] | DROP head | freelist 破壊 | | [NORMALIZE_USERPTR] | 自動変換 | pointer 変換 bug | | refcount > 0 skip | defer free | lifecycle bug | **共通点**: **メモリリーク または 長期的な破壊 の可能性** --- ## 🔬 科学的接近法 ### ステップ1: 問題の特性化 **Q**: 無効 next は「計算誤り」か「上書き」か? ```bash # ログから統計: # - next slab_idx が「特定の値に偏ってる」? # → 計算誤りの可能性 # - next slab_idx が「ランダム」? # → 上書きの可能性 ``` ### ステップ2: 発生源の限定 **Q**: freelist が壊れるのは「どのコード経路」か? ```bash # prev 検証で freelist corruption の正確な位置を特定 # → その付近の code を audit # → 原因が浮かぶ ``` ### ステップ3: 根本修正 **Q**: 修正は「1行」か「10行」か? ``` 対処療法: +3 層(refcount, validation, drop) 根本修正: -1 何か(freelist 計算 OR pointer 変換) ``` --- ## 📈 現在の実装の危険性 ### 1. Refcount 遅延解放 ``` ✅ SIGSEGV は防ぐ ⚠️ メモリ: refcount > 0 で永遠に free されない ⚠️ 結果: メモリリーク蓄積 ``` **リスク**: 長時間実行で RSS が増え続ける ### 2. Freelist DROP ``` ✅ 破損伝播は防ぐ ⚠️ メモリ: DROP されたブロックは回収されない ⚠️ 結果: Freelist 喪失 → 割り当て効率低下 ``` **リスク**: 長期実行でメモリ枯渇 ### 3. Next Validation DROP ``` ✅ リスト遍走のクラッシュは防ぐ ⚠️ メモリ: リスト全体が失われる可能性 ⚠️ 結果: 大量のメモリが「未割り当て」状態 ``` **リスク**: メモリ使用効率が低下 --- ## 🎯 推奨される進め方 ### 今すぐやるべき(根本重視) **Task 1A**: slab_index_for() の完全監査 - 計算ロジック正確か? - off-by-one ないか? - boundary check は十分? **Task 2A**: freelist→next の被上書き監視 - prev/next 一貫性チェック追加 - 破壊を「検知」「修復」から「予防」へ **Task 3A**: pointer 変換の audit - user ↔ base 変換が一貫してるか - クラス別に間違いないか ### 並行してやるべき(デバッグ) **Metrics 収集**: - [TLS_SLL_NEXT_INVALID] の発生頻度 - [FREELIST_INVALID] の発生頻度 - [NORMALIZE_USERPTR] の発生頻度 - → パターン認識 **Memory 監視**: - RSS の増加率(対処療法の影響) - refcount > 0 の SuperSlab 数 - dropped freelist の総バイト数 --- ## 📌 結論 ### 現状の評価 ``` ✅ 安定性: 達成(SIGSEGV ゼロ) ❌ 健全性: 低下(対処療法の積み重ね) ❌ 根本原因: 未解決(症状だけ見えなくしてる) ``` ### 危険信号 ``` ⚠️ [TLS_SLL_NEXT_INVALID] が「多発」 → freelist が頻繁に破壊されてる ⚠️ refcount > 0 のスラブが増殖 → メモリが「解放待ち」で蓄積 ⚠️ [NORMALIZE_USERPTR] の多数発生 → pointer 変換の根本問題が複数ある ``` ### 推奨アクション ``` 現在: 「症状を隠蔽して安定性を得た」 次へ: 「根本原因を特定して健全性を回復」 Timeline: Task 1: slab_index_for() audit (1-2h) Task 2: freelist corruption detection (1-2h) Task 3: pointer 変換 audit (1h) ↓ 根本原因の最終特定(1h) ↓ 根本修正(1-3 lines) ``` --- **Assessment**: 今は「安定性優先」から「健全性重視」へシフトする時期。 対処療法の層数が増えすぎる前に、根本原因に向き合おう。 --- *Document created: 2025-12-03* *Analysis: Symptoms are being suppressed, not cured* *Risk Level: MEDIUM (stability achieved, but long-term viability uncertain)*