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>
This commit is contained in:
Moe Charm (CI)
2025-12-04 03:09:28 +09:00
parent e1a867fe52
commit 9dbe008f13

View File

@ -0,0 +1,360 @@
# 📊 警告: 対処療法が増殖している (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)*