Files
hakmem/docs/ANALYSIS_SYMPTOM_PROLIFERATION.md
Moe Charm (CI) 9dbe008f13 Critical analysis: symptom suppression vs root cause elimination
Assessment of current approach:
 Stability achieved (no SIGSEGV)
 Symptoms proliferating ([TLS_SLL_NEXT_INVALID], [FREELIST_INVALID], etc.)
 Root causes remain untouched (multiple defensive layers accumulating)

Warning Signs:
- [TLS_SLL_NEXT_INVALID]: Freelist corruption happening frequently
- refcount > 0 deferred releases: Memory accumulating
- [NORMALIZE_USERPTR]: Pointer conversion bugs widespread

Three Root Cause Hypotheses:
A. Freelist next corruption (slab_idx calculation? bounds?)
B. Pointer conversion inconsistency (user vs base mixing)
C. SuperSlab reuse leaving garbage (lifecycle issue)

Recommended Investigation Path:
1. Audit slab_index_for() calculation (potential off-by-one)
2. Add persistent prev/next validation to detect freelist corruption
3. Limit class 1 with forced base conversion (isolate userptr source)

Key Insight:
Current approach: Hide symptoms with layers of guards
Better approach: Find and fix root cause (1-3 line fix expected)

Risk Assessment:
- Current: Stability OK, but memory safety uncertain
- Long-term: Memory leak + efficiency degradation likely
- Urgency: Move to root cause investigation NOW

Timeline for root cause fix:
- Task 1: slab_index_for audit (1-2h)
- Task 2: freelist detection (1-2h)
- Task 3: pointer audit (1h)
- Final fix: (1-3 lines)

Philosophy:
Don't suppress symptoms forever. Find the disease.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-04 03:09:28 +09:00

361 lines
9.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 📊 警告: 対処療法が増殖している (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] 多数
```
**解釈**:
- userptroffset +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)*