474 lines
14 KiB
Markdown
474 lines
14 KiB
Markdown
|
|
# FREE_TO_SS=1 SEGV原因調査レポート
|
|||
|
|
|
|||
|
|
## 調査日時
|
|||
|
|
2025-11-06
|
|||
|
|
|
|||
|
|
## 問題概要
|
|||
|
|
`HAKMEM_TINY_FREE_TO_SS=1` (環境変数) を有効にすると、必ずSEGVが発生する。
|
|||
|
|
|
|||
|
|
## 調査方法論
|
|||
|
|
1. hakmem.c の FREE_TO_SS 経路を全て特定
|
|||
|
|
2. hak_super_lookup() と hak_tiny_free_superslab() の実装を検証
|
|||
|
|
3. メモリ安全性とTOCTOU競合を分析
|
|||
|
|
4. 配列境界チェックの完全性を確認
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 第1部: FREE_TO_SS経路の全体像
|
|||
|
|
|
|||
|
|
### 発見:リソース管理に1つ明らかなバグあり(後述)
|
|||
|
|
|
|||
|
|
**FREE_TO_SSは2つのエントリポイント:**
|
|||
|
|
|
|||
|
|
#### エントリポイント1: `hakmem.c:914-938`(外側ルーティング)
|
|||
|
|
```c
|
|||
|
|
// SS-first (A/B): only when FREE_TO_SS=1
|
|||
|
|
{
|
|||
|
|
if (s_free_to_ss_env) { // 行921
|
|||
|
|
extern int g_use_superslab;
|
|||
|
|
if (g_use_superslab != 0) { // 行923
|
|||
|
|
SuperSlab* ss = hak_super_lookup(ptr); // 行924
|
|||
|
|
if (ss && ss->magic == SUPERSLAB_MAGIC) {
|
|||
|
|
int sidx = slab_index_for(ss, ptr); // 行927
|
|||
|
|
int cap = ss_slabs_capacity(ss); // 行928
|
|||
|
|
if (sidx >= 0 && sidx < cap) { // 行929: 範囲ガード
|
|||
|
|
hak_tiny_free(ptr); // 行931
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**呼び出し結果:** `hak_tiny_free(ptr)` → hak_tiny_free.inc:1459
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
#### エントリポイント2: `hakmem.c:967-980`(内側ルーティング)
|
|||
|
|
```c
|
|||
|
|
// A/B: Force precise Tiny slow free (SS freelist path + publish on first-free)
|
|||
|
|
#ifdef HAKMEM_TINY_PHASE6_BOX_REFACTOR // デフォルト有効(=1)
|
|||
|
|
{
|
|||
|
|
if (s_free_to_ss) { // 行967
|
|||
|
|
SuperSlab* ss = hak_super_lookup(ptr); // 行969
|
|||
|
|
if (ss && ss->magic == SUPERSLAB_MAGIC) {
|
|||
|
|
int sidx = slab_index_for(ss, ptr); // 行971
|
|||
|
|
int cap = ss_slabs_capacity(ss); // 行972
|
|||
|
|
if (sidx >= 0 && sidx < cap) { // 行973: 範囲ガード
|
|||
|
|
hak_tiny_free(ptr); // 行974
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
// Fallback: if SS not resolved or invalid, keep normal tiny path below
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**呼び出し結果:** `hak_tiny_free(ptr)` → hak_tiny_free.inc:1459
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### hak_tiny_free() の内部ルーティング
|
|||
|
|
|
|||
|
|
**エントリポイント3:** `hak_tiny_free.inc:1469-1487`(BENCH_SLL_ONLY)
|
|||
|
|
```c
|
|||
|
|
if (g_use_superslab) {
|
|||
|
|
SuperSlab* ss = hak_super_lookup(ptr); // 1471行
|
|||
|
|
if (ss && ss->magic == SUPERSLAB_MAGIC) {
|
|||
|
|
class_idx = ss->size_class;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**エントリポイント4:** `hak_tiny_free.inc:1490-1512`(Ultra)
|
|||
|
|
```c
|
|||
|
|
if (g_tiny_ultra) {
|
|||
|
|
if (g_use_superslab) {
|
|||
|
|
SuperSlab* ss = hak_super_lookup(ptr); // 1494行
|
|||
|
|
if (ss && ss->magic == SUPERSLAB_MAGIC) {
|
|||
|
|
class_idx = ss->size_class;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**エントリポイント5:** `hak_tiny_free.inc:1517-1524`(メイン)
|
|||
|
|
```c
|
|||
|
|
if (g_use_superslab) {
|
|||
|
|
fast_ss = hak_super_lookup(ptr); // 1518行
|
|||
|
|
if (fast_ss && fast_ss->magic == SUPERSLAB_MAGIC) {
|
|||
|
|
fast_class_idx = fast_ss->size_class; // 1520行 ★★★ BUG1
|
|||
|
|
} else {
|
|||
|
|
fast_ss = NULL;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**最終処理:** `hak_tiny_free.inc:1554-1566`
|
|||
|
|
```c
|
|||
|
|
SuperSlab* ss = fast_ss;
|
|||
|
|
if (!ss && g_use_superslab) {
|
|||
|
|
ss = hak_super_lookup(ptr);
|
|||
|
|
if (!(ss && ss->magic == SUPERSLAB_MAGIC)) {
|
|||
|
|
ss = NULL;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
if (ss && ss->magic == SUPERSLAB_MAGIC) {
|
|||
|
|
hak_tiny_free_superslab(ptr, ss); // 1563行: 最終的な呼び出し
|
|||
|
|
HAK_STAT_FREE(ss->size_class); // 1564行 ★★★ BUG2
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 第2部: hak_tiny_free_superslab() 実装分析
|
|||
|
|
|
|||
|
|
**位置:** `hakmem_tiny_free.inc:1160`
|
|||
|
|
|
|||
|
|
### 関数シグネチャ
|
|||
|
|
```c
|
|||
|
|
static inline void hak_tiny_free_superslab(void* ptr, SuperSlab* ss)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 検証ステップ
|
|||
|
|
|
|||
|
|
#### ステップ1: slab_idx の導出 (1164行)
|
|||
|
|
```c
|
|||
|
|
int slab_idx = slab_index_for(ss, ptr);
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**slab_index_for() の実装** (`hakmem_tiny_superslab.h:141`):
|
|||
|
|
```c
|
|||
|
|
static inline int slab_index_for(const SuperSlab* ss, const void* p) {
|
|||
|
|
uintptr_t base = (uintptr_t)ss;
|
|||
|
|
uintptr_t addr = (uintptr_t)p;
|
|||
|
|
uintptr_t off = addr - base;
|
|||
|
|
int idx = (int)(off >> 16); // 64KB単位で除算
|
|||
|
|
int cap = ss_slabs_capacity(ss); // 1MB=16, 2MB=32
|
|||
|
|
return (idx >= 0 && idx < cap) ? idx : -1;
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### ステップ2: slab_idx の範囲ガード (1167-1172行)
|
|||
|
|
```c
|
|||
|
|
if (__builtin_expect(slab_idx < 0, 0)) {
|
|||
|
|
// ...エラー処理...
|
|||
|
|
if (g_tiny_safe_free_strict) { raise(SIGUSR2); return; }
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**問題:** slab_idx がメモリ管理下の外でオーバーフローしている可能性がある
|
|||
|
|
- slab_index_for() は -1 を返す場合を正しく処理しているが、
|
|||
|
|
- 上位ビットのオーバーフローは検出していない。
|
|||
|
|
|
|||
|
|
例: slab_idx が 10000(32超)の場合、以下でバッファオーバーフローが発生:
|
|||
|
|
```c
|
|||
|
|
TinySlabMeta* meta = &ss->slabs[slab_idx]; // 1173行
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### ステップ3: メタデータアクセス (1173行)
|
|||
|
|
```c
|
|||
|
|
TinySlabMeta* meta = &ss->slabs[slab_idx];
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**配列定義** (`hakmem_tiny_superslab.h:90`):
|
|||
|
|
```c
|
|||
|
|
TinySlabMeta slabs[SLABS_PER_SUPERSLAB_MAX]; // Max = 32
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**危険: slab_idx がこの検証をスキップできる場合:**
|
|||
|
|
- slab_index_for() は (`idx >= 0 && idx < cap`) をチェックしているが、
|
|||
|
|
- **下位呼び出しで hak_super_lookup() が不正なSSを返す可能性がある**
|
|||
|
|
- **TOCTOU: lookup 後に SS が解放される可能性がある**
|
|||
|
|
|
|||
|
|
#### ステップ4: SAFE_FREE チェック (1188-1213行)
|
|||
|
|
```c
|
|||
|
|
if (__builtin_expect(g_tiny_safe_free, 0)) {
|
|||
|
|
size_t blk = g_tiny_class_sizes[ss->size_class]; // ★★★ BUG3
|
|||
|
|
// ...
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**BUG3: ss->size_class の範囲チェックなし!**
|
|||
|
|
- `ss->size_class` は 0..7 であるべき (TINY_NUM_CLASSES=8)
|
|||
|
|
- しかし検証されていない
|
|||
|
|
- 腐ったSSメモリを読むと、任意の値を持つ可能性
|
|||
|
|
- `g_tiny_class_sizes[ss->size_class]` にアクセスすると OOB (Out-Of-Bounds)
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 第3部: バグ・脆弱性・TOCTOU分析
|
|||
|
|
|
|||
|
|
### BUG #1: size_class の範囲チェック欠落 ★★★ CRITICAL
|
|||
|
|
|
|||
|
|
**位置:**
|
|||
|
|
- `hakmem_tiny_free.inc:1520` (fast_class_idx の導出)
|
|||
|
|
- `hakmem_tiny_free.inc:1189` (g_tiny_class_sizes のアクセス)
|
|||
|
|
- `hakmem_tiny_free.inc:1564` (HAK_STAT_FREE)
|
|||
|
|
|
|||
|
|
**根本原因:**
|
|||
|
|
```c
|
|||
|
|
if (fast_ss && fast_ss->magic == SUPERSLAB_MAGIC) {
|
|||
|
|
fast_class_idx = fast_ss->size_class; // チェックなし!
|
|||
|
|
}
|
|||
|
|
// ...
|
|||
|
|
if (g_tiny_safe_free, 0)) {
|
|||
|
|
size_t blk = g_tiny_class_sizes[ss->size_class]; // OOB!
|
|||
|
|
}
|
|||
|
|
// ...
|
|||
|
|
HAK_STAT_FREE(ss->size_class); // OOB!
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**問題:**
|
|||
|
|
- `size_class` は SuperSlab 初期化時に設定される
|
|||
|
|
- しかしメモリ破損やTOCTOUで腐った値を持つ可能性
|
|||
|
|
- チェック: `ss->size_class >= 0 && ss->size_class < TINY_NUM_CLASSES` が不足
|
|||
|
|
|
|||
|
|
**影響:**
|
|||
|
|
1. `g_tiny_class_sizes[bad_size_class]` → OOB read → SEGV
|
|||
|
|
2. `HAK_STAT_FREE(bad_size_class)` → グローバル配列 OOB write → SEGV/無言破損
|
|||
|
|
3. `meta->capacity` で計算時に wrong class size → 無言メモリリーク
|
|||
|
|
|
|||
|
|
**修正案:**
|
|||
|
|
```c
|
|||
|
|
if (ss && ss->magic == SUPERSLAB_MAGIC) {
|
|||
|
|
// ADD: Validate size_class
|
|||
|
|
if (ss->size_class >= TINY_NUM_CLASSES) {
|
|||
|
|
// Invalid size class
|
|||
|
|
tiny_debug_ring_record(TINY_RING_EVENT_REMOTE_INVALID,
|
|||
|
|
0x99, ptr, ss->size_class);
|
|||
|
|
if (g_tiny_safe_free_strict) { raise(SIGUSR2); }
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
hak_tiny_free_superslab(ptr, ss);
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### BUG #2: hak_super_lookup() の TOCTOU 競合 ★★ HIGH
|
|||
|
|
|
|||
|
|
**位置:** `hakmem_super_registry.h:73-106`
|
|||
|
|
|
|||
|
|
**実装:**
|
|||
|
|
```c
|
|||
|
|
static inline SuperSlab* hak_super_lookup(void* ptr) {
|
|||
|
|
if (!g_super_reg_initialized) return NULL;
|
|||
|
|
|
|||
|
|
// Try both 1MB and 2MB alignments
|
|||
|
|
for (int lg = 20; lg <= 21; lg++) {
|
|||
|
|
// ... linear probing ...
|
|||
|
|
SuperRegEntry* e = &g_super_reg[(h + i) & SUPER_REG_MASK];
|
|||
|
|
uintptr_t b = atomic_load_explicit((_Atomic uintptr_t*)&e->base,
|
|||
|
|
memory_order_acquire);
|
|||
|
|
|
|||
|
|
if (b == base && e->lg_size == lg) {
|
|||
|
|
SuperSlab* ss = atomic_load_explicit(&e->ss, memory_order_acquire);
|
|||
|
|
if (!ss) return NULL; // Entry cleared by unregister
|
|||
|
|
|
|||
|
|
if (ss->magic != SUPERSLAB_MAGIC) return NULL; // Being freed
|
|||
|
|
|
|||
|
|
return ss;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return NULL;
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**TOCTOU シナリオ:**
|
|||
|
|
```
|
|||
|
|
Thread A: ss = hak_super_lookup(ptr) ← NULL チェック + magic チェック成功
|
|||
|
|
↓
|
|||
|
|
↓ (Context switch)
|
|||
|
|
↓
|
|||
|
|
Thread B: hak_super_unregister() 呼び出し
|
|||
|
|
↓ base = 0 を書き込み (release semantics)
|
|||
|
|
↓ munmap() を呼び出し
|
|||
|
|
↓
|
|||
|
|
Thread A: TinySlabMeta* meta = &ss->slabs[slab_idx] ← SEGV!
|
|||
|
|
(ss が unmapped memory のため)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**根本原因:**
|
|||
|
|
- `hak_super_lookup()` は magic チェック時の SS validity をチェックしているが、
|
|||
|
|
- **チェック後、メタデータアクセス時にメモリが unmapped される可能性**
|
|||
|
|
- atomic_load で acquire したのに、その後の memory access order が保証されない
|
|||
|
|
|
|||
|
|
**修正案:**
|
|||
|
|
- `hak_super_unregister()` の前に refcount 検証
|
|||
|
|
- または: `hak_tiny_free_superslab()` 内で再度 magic チェック
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### BUG #3: ss->lg_size の範囲検証欠落 ★ MEDIUM
|
|||
|
|
|
|||
|
|
**位置:** `hakmem_tiny_free.inc:1165`
|
|||
|
|
|
|||
|
|
**コード:**
|
|||
|
|
```c
|
|||
|
|
size_t ss_size = (size_t)1ULL << ss->lg_size; // lg_size が 20..21 であると仮定
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**問題:**
|
|||
|
|
- `ss->lg_size` が腐った値 (22+) を持つと、オーバーフロー
|
|||
|
|
- 例: `1ULL << 64` → undefined behavior (シフト量 >= 64)
|
|||
|
|
- 結果: `ss_size` が 0 または corrupt
|
|||
|
|
|
|||
|
|
**修正案:**
|
|||
|
|
```c
|
|||
|
|
if (ss->lg_size < 20 || ss->lg_size > 21) {
|
|||
|
|
// Invalid SuperSlab size
|
|||
|
|
tiny_debug_ring_record(TINY_RING_EVENT_REMOTE_INVALID,
|
|||
|
|
0x9A, ptr, ss->lg_size);
|
|||
|
|
if (g_tiny_safe_free_strict) { raise(SIGUSR2); }
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
size_t ss_size = (size_t)1ULL << ss->lg_size;
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### TOCTOU #1: slab_index_for 後の pointer validity
|
|||
|
|
|
|||
|
|
**流れ:**
|
|||
|
|
```
|
|||
|
|
1. hak_super_lookup() ← lock-free, acquire semantics
|
|||
|
|
2. slab_index_for() ← pointer math, local calculation
|
|||
|
|
3. hak_tiny_free_superslab(ptr, ss) ← ss は古い可能性
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**競合シナリオ:**
|
|||
|
|
```
|
|||
|
|
Thread A: ss = hak_super_lookup(ptr) ✓ valid
|
|||
|
|
sidx = slab_index_for(ss, ptr) ✓ valid
|
|||
|
|
hak_tiny_free_superslab(ptr, ss)
|
|||
|
|
↓ (Context switch)
|
|||
|
|
↓
|
|||
|
|
Thread B: [別プロセス] SuperSlab が MADV_FREE される
|
|||
|
|
↓ pages が reclaim される
|
|||
|
|
↓
|
|||
|
|
Thread A: TinySlabMeta* meta = &ss->slabs[sidx] ← SEGV!
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 第4部: 発見したバグの優先度
|
|||
|
|
|
|||
|
|
| ID | 場所 | 種類 | 深刻度 | 原因 |
|
|||
|
|
|----|------|------|--------|------|
|
|||
|
|
| BUG#1 | hakmem_tiny_free.inc:1520, 1189, 1564 | OOB | CRITICAL | size_class 未検証 |
|
|||
|
|
| BUG#2 | hakmem_super_registry.h:73 | TOCTOU | HIGH | lookup 後の mmap/munmap 競合 |
|
|||
|
|
| BUG#3 | hakmem_tiny_free.inc:1165 | OOB | MEDIUM | lg_size オーバーフロー |
|
|||
|
|
| TOCTOU#1 | hakmem.c:924, 969 | Race | HIGH | pointer invalidation |
|
|||
|
|
| Missing | hakmem.c:927-929, 971-973 | Logic | HIGH | cap チェックのみ、size_class 検証なし |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 第5部: SEGV の最も可能性が高い原因
|
|||
|
|
|
|||
|
|
### 最確と思われる原因チェーン
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
1. HAKMEM_TINY_FREE_TO_SS=1 を有効化
|
|||
|
|
↓
|
|||
|
|
2. Free call → hakmem.c:967-980 (内側ルーティング)
|
|||
|
|
↓
|
|||
|
|
3. hak_super_lookup(ptr) で SS を取得
|
|||
|
|
↓
|
|||
|
|
4. slab_index_for(ss, ptr) で sidx チェック ← OK (範囲内)
|
|||
|
|
↓
|
|||
|
|
5. hak_tiny_free(ptr) → hak_tiny_free.inc:1554-1564
|
|||
|
|
↓
|
|||
|
|
6. ss->magic == SUPERSLAB_MAGIC ← OK
|
|||
|
|
↓
|
|||
|
|
7. hak_tiny_free_superslab(ptr, ss) を呼び出し
|
|||
|
|
↓
|
|||
|
|
8. TinySlabMeta* meta = &ss->slabs[slab_idx] ← ✓
|
|||
|
|
↓
|
|||
|
|
9. if (g_tiny_safe_free, 0) {
|
|||
|
|
size_t blk = g_tiny_class_sizes[ss->size_class];
|
|||
|
|
↑↑↑ ss->size_class が [0, 8) 外の値
|
|||
|
|
↓
|
|||
|
|
SEGV! (OOB read または OOB write)
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### または (別シナリオ):
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
1. HAKMEM_TINY_FREE_TO_SS=1
|
|||
|
|
↓
|
|||
|
|
2. hak_super_lookup() で SS を取得して magic チェック ← OK
|
|||
|
|
↓
|
|||
|
|
3. Context switch → 別スレッドが hak_super_unregister() 呼び出し
|
|||
|
|
↓
|
|||
|
|
4. SuperSlab が munmap される
|
|||
|
|
↓
|
|||
|
|
5. TinySlabMeta* meta = &ss->slabs[slab_idx]
|
|||
|
|
↓
|
|||
|
|
SEGV! (unmapped memory access)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 推奨される修正順序
|
|||
|
|
|
|||
|
|
### 優先度 1 (即座に修正):
|
|||
|
|
```c
|
|||
|
|
// hakmem_tiny_free.inc:1553-1566 に追加
|
|||
|
|
if (ss && ss->magic == SUPERSLAB_MAGIC) {
|
|||
|
|
// CRITICAL FIX: Validate size_class
|
|||
|
|
if (ss->size_class >= TINY_NUM_CLASSES) {
|
|||
|
|
tiny_debug_ring_record(TINY_RING_EVENT_REMOTE_INVALID,
|
|||
|
|
(uint16_t)0xBAD_SIZE_CLASS, ptr, ss->size_class);
|
|||
|
|
if (g_tiny_safe_free_strict) { raise(SIGUSR2); }
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
// CRITICAL FIX: Validate lg_size
|
|||
|
|
if (ss->lg_size < 20 || ss->lg_size > 21) {
|
|||
|
|
tiny_debug_ring_record(TINY_RING_EVENT_REMOTE_INVALID,
|
|||
|
|
(uint16_t)0xBAD_LG_SIZE, ptr, ss->lg_size);
|
|||
|
|
if (g_tiny_safe_free_strict) { raise(SIGUSR2); }
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
hak_tiny_free_superslab(ptr, ss);
|
|||
|
|
HAK_STAT_FREE(ss->size_class);
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 優先度 2 (TOCTOU対策):
|
|||
|
|
```c
|
|||
|
|
// hakmem_tiny_free_superslab() 内冒頭に追加
|
|||
|
|
if (ss->magic != SUPERSLAB_MAGIC) {
|
|||
|
|
// Re-check magic in case of TOCTOU
|
|||
|
|
tiny_debug_ring_record(TINY_RING_EVENT_REMOTE_INVALID,
|
|||
|
|
(uint16_t)0xTOCTOU_MAGIC, ptr, 0);
|
|||
|
|
if (g_tiny_safe_free_strict) { raise(SIGUSR2); }
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 優先度 3 (防御的プログラミング):
|
|||
|
|
```c
|
|||
|
|
// hakmem.c:924-932, 969-976 の両方で、size_class も検証
|
|||
|
|
if (sidx >= 0 && sidx < cap && ss->size_class < TINY_NUM_CLASSES) {
|
|||
|
|
hak_tiny_free(ptr);
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 結論
|
|||
|
|
|
|||
|
|
FREE_TO_SS=1 で SEGV が発生する最主要な理由は、**size_class の範囲チェック欠落**である。
|
|||
|
|
|
|||
|
|
腐った SuperSlab メモリ (corruption, TOCTOU) を指す場合でも、
|
|||
|
|
proper validation の欠落が root cause。
|
|||
|
|
|
|||
|
|
修正後は厳格なメモリ検証 (magic + size_class + lg_size) で安全性を確保できる。
|