Files
hakmem/docs/tls_sll_hdr_reset_investigation_report.md

3.9 KiB
Raw Blame History

調査報告: TLS_SLL_HDR_RESET の根本原因と対策

結論

sh8benchhakmem の返す奇数アドレスTiny Allocatorの仕様を補正して使用し、その結果として隣接ブロックのヘッダーを破壊しているBuffer Overflow / Neighbor Corruption

hakmem 側で追加した Atomic Fenceヘッダー強制書き込み は正常に機能しているが、メモリ破壊自体を防ぐことはできないため、エラーログ (TLS_SLL_HDR_RESET) は継続して発生する。しかし、このエラー検知とリセット機構により、Segmentation Fault を回避して実行を継続できている(HEADER_CLASSIDX=0 にすると即座にクラッシュすることから証明された)。


詳細分析

1. 発生メカニズム

  1. 奇数アドレスの返却: hakmem の Tiny Class 1 (16バイト) は [Header 1B][Data 15B] の構成であり、mallocBase + 1 の奇数アドレスを返す(例: 0x...e1)。
  2. sh8bench の挙動(推測): sh8bench はポインタのアラインメント(偶数/16バイト境界を期待し、返されたポインタを +1 して使用している可能性が高い。
  3. ポインタのズレ: free 時に渡されるポインタも +1 された状態(例: 0x...e2)になっている。 これにより [TLS_SLL_NORMALIZE_USERPTR] ログが発生する。
    • free(0x...e2)base = 0x...e1push(0x...e1)normalize(0x...e1) = 0x...e0 (正常な Base に復帰)
    • この「正規化」機能により、free 自体は成功する。
  4. 隣接ブロック破壊: sh8bench がデータを書き込む際、+1 されたアドレスから開始するため、割り当てサイズ16バイトを書き込むと、次のブロックの先頭(ヘッダー)まで溢れて書き込んでしまう
    • 自身の領域: offset 2 offset 16
    • 次のブロック: offset 16 (Header) を上書き
  5. エラー発生: 破壊された隣のブロックが TLS SLL から pop される際、ヘッダーが 0xa1 ではなく 0xd10x51 (書き込まれたデータの一部)になっているため、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 のアラインメント設計を見直す(ヘッダーサイズをアラインメントに合わせる等)。

実施した作業

  1. コード修正: core/box/tls_sll_box.h__atomic_thread_fence(__ATOMIC_RELEASE) とヘッダー強制書き込みを追加。
  2. 再現テスト: sh8bench を用いてエラー再現とログ解析を実施。
  3. 原因特定: ログと挙動から、アラインメント起因の隣接ブロック破壊と特定。