Files
hakmem/CURRENT_TASK.md

94 lines
3.5 KiB
Markdown
Raw Normal View History

# Current Task 2025-11-08
## ✅ 完了: 二重割り当てバグの修正
Fix: SuperSlab guess loop & header magic SEGV (random_mixed/mid_large_mt) ## Problem bench_random_mixed_hakmem and bench_mid_large_mt_hakmem crashed with SEGV: - random_mixed: Exit 139 (SEGV) ❌ - mid_large_mt: Exit 139 (SEGV) ❌ - Larson: 838K ops/s ✅ (worked fine) Error: Unmapped memory dereference in free path ## Root Causes (2 bugs found by Ultrathink Task) ### Bug 1: Guess Loop (core/box/hak_free_api.inc.h:92-95) ```c for (int lg=21; lg>=20; lg--) { SuperSlab* guess=(SuperSlab*)((uintptr_t)ptr & ~mask); if (guess && guess->magic==SUPERSLAB_MAGIC) { // ← SEGV // Dereferences unmapped memory } } ``` ### Bug 2: Header Magic Check (core/box/hak_free_api.inc.h:115) ```c void* raw = (char*)ptr - HEADER_SIZE; AllocHeader* hdr = (AllocHeader*)raw; if (hdr->magic != HAKMEM_MAGIC) { // ← SEGV // Dereferences unmapped memory if ptr has no header } ``` **Why SEGV:** - Registry lookup fails (allocation not from SuperSlab) - Guess loop calculates 1MB/2MB aligned address - No memory mapping validation - Dereferences unmapped memory → SEGV **Why Larson worked but random_mixed failed:** - Larson: All from SuperSlab → registry hit → never reaches guess loop - random_mixed: Diverse sizes (8-4096B) → registry miss → enters buggy paths **Why LD_PRELOAD worked:** - hak_core_init.inc.h:119-121 disables SuperSlab by default - → SS-first path skipped → buggy code never executed ## Fix (2-part) ### Part 1: Remove Guess Loop File: core/box/hak_free_api.inc.h:92-95 - Deleted unsafe guess loop (4 lines) - If registry lookup fails, allocation is not from SuperSlab ### Part 2: Add Memory Safety Check File: core/hakmem_internal.h:277-294 ```c static inline int hak_is_memory_readable(void* addr) { unsigned char vec; return mincore(addr, 1, &vec) == 0; // Check if mapped } ``` File: core/box/hak_free_api.inc.h:115-131 ```c if (!hak_is_memory_readable(raw)) { // Not accessible → route to appropriate handler // Prevents SEGV on unmapped memory goto done; } // Safe to dereference now AllocHeader* hdr = (AllocHeader*)raw; ``` ## Verification | Test | Before | After | Result | |------|--------|-------|--------| | random_mixed (2KB) | ❌ SEGV | ✅ 2.22M ops/s | 🎉 Fixed | | random_mixed (4KB) | ❌ SEGV | ✅ 2.58M ops/s | 🎉 Fixed | | Larson 4T | ✅ 838K | ✅ 838K ops/s | ✅ No regression | **Performance Impact:** 0% (mincore only on fallback path) ## Investigation - Complete analysis: SEGV_ROOT_CAUSE_COMPLETE.md - Fix report: SEGV_FIX_REPORT.md - Previous investigation: SEGFAULT_INVESTIGATION_REPORT.md 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-07 17:34:24 +09:00
### 根本原因
`trc_linear_carve()``meta->used` をカーソルとして使用していたが、`meta->used` はブロック解放時に減少するため、既に割り当て済みのブロックが再度カーブされる**二重割り当てバグ**が発生していた。
Fix: SuperSlab guess loop & header magic SEGV (random_mixed/mid_large_mt) ## Problem bench_random_mixed_hakmem and bench_mid_large_mt_hakmem crashed with SEGV: - random_mixed: Exit 139 (SEGV) ❌ - mid_large_mt: Exit 139 (SEGV) ❌ - Larson: 838K ops/s ✅ (worked fine) Error: Unmapped memory dereference in free path ## Root Causes (2 bugs found by Ultrathink Task) ### Bug 1: Guess Loop (core/box/hak_free_api.inc.h:92-95) ```c for (int lg=21; lg>=20; lg--) { SuperSlab* guess=(SuperSlab*)((uintptr_t)ptr & ~mask); if (guess && guess->magic==SUPERSLAB_MAGIC) { // ← SEGV // Dereferences unmapped memory } } ``` ### Bug 2: Header Magic Check (core/box/hak_free_api.inc.h:115) ```c void* raw = (char*)ptr - HEADER_SIZE; AllocHeader* hdr = (AllocHeader*)raw; if (hdr->magic != HAKMEM_MAGIC) { // ← SEGV // Dereferences unmapped memory if ptr has no header } ``` **Why SEGV:** - Registry lookup fails (allocation not from SuperSlab) - Guess loop calculates 1MB/2MB aligned address - No memory mapping validation - Dereferences unmapped memory → SEGV **Why Larson worked but random_mixed failed:** - Larson: All from SuperSlab → registry hit → never reaches guess loop - random_mixed: Diverse sizes (8-4096B) → registry miss → enters buggy paths **Why LD_PRELOAD worked:** - hak_core_init.inc.h:119-121 disables SuperSlab by default - → SS-first path skipped → buggy code never executed ## Fix (2-part) ### Part 1: Remove Guess Loop File: core/box/hak_free_api.inc.h:92-95 - Deleted unsafe guess loop (4 lines) - If registry lookup fails, allocation is not from SuperSlab ### Part 2: Add Memory Safety Check File: core/hakmem_internal.h:277-294 ```c static inline int hak_is_memory_readable(void* addr) { unsigned char vec; return mincore(addr, 1, &vec) == 0; // Check if mapped } ``` File: core/box/hak_free_api.inc.h:115-131 ```c if (!hak_is_memory_readable(raw)) { // Not accessible → route to appropriate handler // Prevents SEGV on unmapped memory goto done; } // Safe to dereference now AllocHeader* hdr = (AllocHeader*)raw; ``` ## Verification | Test | Before | After | Result | |------|--------|-------|--------| | random_mixed (2KB) | ❌ SEGV | ✅ 2.22M ops/s | 🎉 Fixed | | random_mixed (4KB) | ❌ SEGV | ✅ 2.58M ops/s | 🎉 Fixed | | Larson 4T | ✅ 838K | ✅ 838K ops/s | ✅ No regression | **Performance Impact:** 0% (mincore only on fallback path) ## Investigation - Complete analysis: SEGV_ROOT_CAUSE_COMPLETE.md - Fix report: SEGV_FIX_REPORT.md - Previous investigation: SEGFAULT_INVESTIGATION_REPORT.md 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-07 17:34:24 +09:00
### 発見プロセス
1. Fail-Fast 診断ログで TLS SLL head の破壊を検出 (`0x...fcf1` = 241バイトの misalignment)
2. 配布ポインタの next 読み取り時にユーザーデータ (`0x6261` = ASCII "ab") を検出
3. ログ解析で同じブロック (offset 0xFC00 = 64512) が 2回割り当てられていることを確認
4. LINEAR_CARVE ログで `used=61 batch=1``used=59 batch=3` の矛盾を発見
### 証拠ログ
```
[LINEAR_CARVE] base=0x...600800 used=61 batch=1 → ブロック 61 を作成
(いくつかのブロックが解放され、used が 62 → 59 に減少)
[LINEAR_CARVE] base=0x...600800 used=59 batch=3 → ブロック 59, 60, 61 を再作成!
```
### 実装した修正
**1. `TinySlabMeta` 構造体に `carved` フィールド追加** (`core/superslab/superslab_types.h`)
```c
typedef struct TinySlabMeta {
void* freelist;
uint16_t used; // 現在使用中のブロック数(増減両方)
uint16_t capacity;
uint16_t carved; // 線形領域からカーブしたブロック数(単調増加のみ)
uint16_t owner_tid; // uint32_t → uint16_t に変更
} TinySlabMeta;
```
**2. `trc_linear_carve()` を修正** (`core/tiny_refill_opt.h`)
```c
// Before: meta->used をカーソルとして使用(バグ!)
uint8_t* cursor = base + ((size_t)meta->used * bs);
meta->used += batch;
// After: meta->carved をカーソルとして使用(修正版)
uint8_t* cursor = base + ((size_t)meta->carved * bs);
meta->carved += batch; // 単調増加のみ
meta->used += batch; // 使用中カウントも更新
```
**3. 初期化コード追加** (`core/hakmem_tiny_superslab.c`)
```c
meta->carved = 0; // carved カウンター初期化
meta->owner_tid = (uint16_t)owner_tid; // uint16_t にキャスト
```
### テスト結果
```bash
# Fail-fast モード(診断ログあり)
HAKMEM_TINY_REFILL_FAILFAST=2 ./bench_random_mixed_hakmem 50000 2048 1234567
→ ✅ 651,627 ops/s (クラッシュなし)
# 通常モード(診断ログなし)
./bench_random_mixed_hakmem 50000 2048 1234567
→ ✅ 950,037 ops/s (クラッシュなし)
```
### 修正されたファイル
- `core/superslab/superslab_types.h` - `TinySlabMeta``carved` フィールド追加
- `core/tiny_refill_opt.h` - `trc_linear_carve()``carved` 使用に修正
- `core/hakmem_tiny_superslab.c` - `carved` 初期化
- `core/tiny_alloc_fast.inc.h` - 診断ログ追加 (次ポインタ検証)
- `core/hakmem_tiny_free.inc` - 診断ログ追加 (drain/free 検証)
## 次のステップ
1. **診断ログのクリーンアップ** (Optional)
- Fail-Fast ログを本番向けに最適化
- デバッグ用のログを削除
2. **性能ベンチマーク**
- `bench_random_mixed` でスループット計測
- Larson ベンチマークで検証
- System/mimalloc との比較
3. **追加テスト**
- マルチスレッドストレステスト
- 長時間実行テスト
## 実行コマンド
```bash
# 通常テスト
HAKMEM_TINY_USE_SUPERSLAB=1 ./bench_random_mixed_hakmem 50000 2048 1234567
# Fail-fast 診断モード
HAKMEM_TINY_REFILL_FAILFAST=2 HAKMEM_TINY_USE_SUPERSLAB=1 \
./bench_random_mixed_hakmem 50000 2048 1234567
```