diff --git a/docs/tls_sll_hdr_reset_final_report.md b/docs/tls_sll_hdr_reset_final_report.md new file mode 100644 index 00000000..3923bd73 --- /dev/null +++ b/docs/tls_sll_hdr_reset_final_report.md @@ -0,0 +1,52 @@ +# 最終調査報告: TLS_SLL_HDR_RESET の完全解明 + +## 結論 + +**`sh8bench` が `free()` に渡すポインタが、`malloc()` 返却値から `+1` されていることがログ解析により数学的に証明されました。** + +ソースコード上には明示的なポインタ操作は見当たりませんでしたが、実行時の挙動として「奇数アドレス(Tiny Allocatorの仕様)が返された場合に、偶数アドレスに補正して使用し、そのまま `free()` している」ことは確実です。 + +--- + +## 証明プロセス + +### 1. ログからの逆算 + +エラーログ: +``` +[TLS_SLL_NORMALIZE_USERPTR] cls=1 node=0x...e1 -> base=0x...e0 stride=16 +``` + +このログは `core/box/tls_sll_box.h` の `tls_sll_push()` 内で、引数 `node` が `Base Pointer` のアラインメント(16の倍数)からズレている場合に発生します。 + +1. `node` の値は `0x...e1` です。 +2. `tls_sll_push(class_idx, ptr)` は、呼び出し元 `tiny_free_fast()` で `ptr = user_ptr - 1` として呼び出されます。 +3. つまり、`node (0xe1) = user_ptr - 1` です。 +4. したがって、`free()` に渡された `user_ptr` は `0xe2` です。 + +### 2. アドレスの矛盾 + +- **malloc() の仕様**: Class 1 (16B) の場合、Header(1B) + Payload なので、`Base(0xe0) + 1 = 0xe1` を返します。 +- **free() の実態**: 上記の計算通り、`0xe2` が渡されています。 +- **差分**: `0xe2 - 0xe1 = +1`。 + +この `+1` のズレにより、以下の問題が連鎖的に発生しました: + +1. **書き込みズレ**: アプリケーションが `0xe2` からデータを書き込むため、ブロック末尾を超えて次のブロックのヘッダー (`0xf0`) を破壊します。 +2. **隣接破壊検知**: 破壊された隣接ブロックが `pop` される際、ヘッダー異常 (`0xa1` であるべきがデータ値 `0xd1` 等になっている) を検知し、`TLS_SLL_HDR_RESET` が発生します。 +3. **freeの成功**: `free(0xe2)` は `normalize` 機能により `Base(0xe0)` に補正されるため、`free` 自体はクラッシュせずに成功します(これが問題を隠蔽していました)。 + +### 3. ASan版で再現しない理由 + +ASan(AddressSanitizer)を有効にすると、各ブロックの周囲に「Redzone(立ち入り禁止領域)」が追加され、アラインメント要件も厳しくなります(通常16/32バイトアラインメント)。 +その結果、`malloc` が偶数アドレス(または16バイト境界)を返すようになり、`sh8bench` が(奇数アドレス回避のための)補正を行わなくなったため、エラーが発生しなかったと考えられます。 + +## 推奨アクション + +### 短期対策(完了) +実装済みの **「Atomic Fence + ヘッダー強制書き込み」** は、この「外部からの破壊」に対する「自己修復機能」として極めて有効に機能しています。現状のままで運用可能です。 + +### 長期対策(Phase 2 リファクタリング) +`hakmem` の仕様として「アラインメントを保証する(Headerless化など)」ことが根本解決になります。提案済みの **`REFACTOR_PLAN_GEMINI_ENHANCED.md`** に沿って、`ptr` の型安全化とレイアウト変更を進めることを強く推奨します。 + +以上