3.9 KiB
3.9 KiB
調査報告: TLS_SLL_HDR_RESET の根本原因と対策
結論
sh8bench が hakmem の返す奇数アドレス(Tiny Allocatorの仕様)を補正して使用し、その結果として隣接ブロックのヘッダーを破壊している(Buffer Overflow / Neighbor Corruption)。
hakmem 側で追加した Atomic Fence や ヘッダー強制書き込み は正常に機能しているが、メモリ破壊自体を防ぐことはできないため、エラーログ (TLS_SLL_HDR_RESET) は継続して発生する。しかし、このエラー検知とリセット機構により、Segmentation Fault を回避して実行を継続できている(HEADER_CLASSIDX=0 にすると即座にクラッシュすることから証明された)。
詳細分析
1. 発生メカニズム
- 奇数アドレスの返却:
hakmemの Tiny Class 1 (16バイト) は[Header 1B][Data 15B]の構成であり、mallocはBase + 1の奇数アドレスを返す(例:0x...e1)。 - sh8bench の挙動(推測):
sh8benchはポインタのアラインメント(偶数/16バイト境界)を期待し、返されたポインタを+1して使用している可能性が高い。 - ポインタのズレ:
free時に渡されるポインタも+1された状態(例:0x...e2)になっている。 これにより[TLS_SLL_NORMALIZE_USERPTR]ログが発生する。free(0x...e2)→base = 0x...e1→push(0x...e1)→normalize(0x...e1) = 0x...e0(正常な Base に復帰)- この「正規化」機能により、
free自体は成功する。
- 隣接ブロック破壊:
sh8benchがデータを書き込む際、+1されたアドレスから開始するため、割り当てサイズ(16バイト)を書き込むと、次のブロックの先頭(ヘッダー)まで溢れて書き込んでしまう。- 自身の領域:
offset 2~offset 16 - 次のブロック:
offset 16(Header) を上書き
- 自身の領域:
- エラー発生:
破壊された隣のブロックが
TLS SLLからpopされる際、ヘッダーが0xa1ではなく0xd1や0x51(書き込まれたデータの一部)になっているため、TLS_SLL_HDR_RESETが発生する。
2. 証拠
- [TLS_SLL_NORMALIZE_USERPTR]:
freeに渡されたポインタがズレていることを示す。 - [TLS_SLL_HDR_RESET]:
pop時にヘッダー破壊を検知。 - HEADER_CLASSIDX=0 での Segfault: ヘッダーチェック(防御壁)を外すと、破壊された
nextポインタ(offset 0)を参照してクラッシュする。 - SAFEHEADER テスト結果:
push時にはヘッダーが正常だった(SAFEHEADERで拒否されなかった)が、pop時に壊れていた。これはpush後に(隣接ブロックへの書き込みによって)破壊されたことを示す。
3. 修正と対策
今回適用した修正(core/box/tls_sll_box.h への Atomic Fence 追加)は、push 時の整合性を高める意味で有用であり、そのまま残すべきである。
しかし、根本的な「外部からのメモリ破壊」を防ぐには、hakmem のアーキテクチャ変更(アラインメント保証のためのパディング追加など)が必要となる。
推奨アクション:
現状の TLS_SLL_HDR_RESET は「正常な防御動作」であるため、このまま運用し、将来的には Tiny Allocator のアラインメント設計を見直す(ヘッダーサイズをアラインメントに合わせる等)。
実施した作業
- コード修正:
core/box/tls_sll_box.hに__atomic_thread_fence(__ATOMIC_RELEASE)とヘッダー強制書き込みを追加。 - 再現テスト:
sh8benchを用いてエラー再現とログ解析を実施。 - 原因特定: ログと挙動から、アラインメント起因の隣接ブロック破壊と特定。