1 ポイント 投稿者 GN⁺ 14 일 전 | 1件のコメント | WhatsAppで共有
  • 形式検証済みの zlib 実装をファジングした結果、Lean 4 ランタイムの lean_alloc_sarrayヒープバッファオーバーフロー が発見された
  • AI ファザー ClaudeAFL++AddressSanitizer などを用いて1億回以上テストした末に、4件のクラッシュと1件のメモリ脆弱性 が確認された
  • 見つかった問題は、ランタイムオーバーフローArchive.leanOut-of-Memory ベースのサービス拒否(DoS) の2種類に分けられる
  • 検証済みの圧縮・展開アルゴリズムは安全だったが、検証されていないアーカイブパーサーとランタイム層 に脆弱性が存在した
  • 結果として形式検証は強力だが、信頼基盤計算ベース(TCB) の安全性が確保されなければ、システム全体の安定性を保証することはできない

Lean の検証を通過したプログラムで見つかったバグ

  • Lean で形式検証された zlib 実装をファジングした結果、Lean 4 ランタイムで ヒープバッファオーバーフロー が発見された
    • 検証済みのアプリケーションコードにはメモリ脆弱性はなかった
    • しかし Lean ランタイムの lean_alloc_sarray 関数でオーバーフローが発生し、すべての Lean 4 バージョンに影響する
  • AI ベースのファザー ClaudeAFL++AddressSanitizerValgrindUBSan などを利用して1億回以上のファジングを実施
    • lean-zip の圧縮・展開・アーカイブ処理など6つの攻撃面を対象に16個の並列ファザーを稼働
    • 19時間で4件のクラッシュ入力と1件のメモリ脆弱性が見つかった
  • 2つの主要なバグ が確認された
    • Lean ランタイムの lean_alloc_sarray における ヒープバッファオーバーフロー
    • lean-zipArchive.lean モジュールにおける Out-of-Memory ベースのサービス拒否(DoS)
  • 形式検証の限界 が明らかになった
    • lean-zip の圧縮・展開アルゴリズムは完全に検証されていたが、アーカイブパーサー (Archive.lean) は検証されておらず、DoS 脆弱性が存在した
    • ランタイムバグは 信頼基盤計算ベース(Trusted Computing Base) 内部の問題であり、すべての Lean プログラムに影響する
  • 結論として、Lean の形式検証はアプリケーションコードの安定性を証明したが、検証範囲外の構成要素 には依然として脆弱性が存在する
    • 検証は適用された範囲内でのみ強力であり、基盤となる信頼層の安全性確保 が不可欠である

ファジング実験の概要

  • 対象コードベースlean-zip の検証済み zlib 実装
    • すべての定理 (theorem) と仕様 (specification)、ドキュメント、C FFI バインディングを削除し、純粋な Lean コードのみを残した
    • Claude がコードが検証済みである事実を認識できないようにしてバイアスを防いだ
  • 実験環境

    • 16個の並列ファザーが ZIP、gzip、DEFLATE、tar、tar.gz、compression という6つの攻撃面を対象に実行
    • AddressSanitizer、UndefinedBehaviorSanitizer、Valgrind memcheck、cppcheck、flawfinder などを併用
    • 48個の手作業エクスプロイトファイルを含め、合計 105,823,818回実行、359個のシードファイルを使用
    • 19時間で4件のクラッシュ入力と1件のメモリ脆弱性が発見された

バグ1: Lean ランタイムのヒープバッファオーバーフロー

  • 脆弱な関数: lean_alloc_sarray
    • すべての ByteArrayFloatArray などのスカラー配列を割り当てる関数
    • sizeof(lean_sarray_object) + elem_size * capacity の計算時に 整数オーバーフロー が発生する可能性がある
  • 問題の原因

    • capacitySIZE_MAX に近い場合、加算がオーバーフローして小さなバッファが割り当てられる
    • その後、呼び出し側が n バイトを読み込み、ヒープバッファオーバーフロー が発生する
  • トリガー条件

    • lean_io_prim_handle_readnbytes が非常に大きい値のときに発生
    • ZIP64 ヘッダーの compressedSize0xFFFFFFFFFFFFFFFF である156バイトのファイルで再現可能
    • Lean 4 のすべてのバージョン(最新 nightly を含む)に影響
    • 再現コード例
    def main : IO Unit := do
      IO.FS.writeFile "test.bin" "hello"
      let h ← IO.FS.Handle.mk "test.bin" .read
      let n : USize := (0 : USize) - (1 : USize)
      let _ ← h.read n
    
    • 上記コードを実行すると lean_alloc_sarray でオーバーフローが発生する
    • 修正 PR が提出されている

バグ2: lean-zip アーカイブパーサーのサービス拒否(DoS)

  • 脆弱な関数: readExact (Archive.lean)
    • ZIP 中央ディレクトリの compressedSize フィールドを検証なしにそのまま使用
    • h.read 呼び出し時に異常に大きなサイズを要求し、過剰なメモリ割り当てと OOM 発生 を引き起こす
  • 問題の再現

    • 156バイトの ZIP ファイルが数エクサバイト級のサイズを主張すると、 INTERNAL PANIC: out of memory でプロセスが終了する
    • システムの unzip はヘッダーサイズを検証するが、lean-zip はこれを行わない

形式検証が見落とした部分

  • DoS 脆弱性の原因

    • Archive.lean モジュールは未検証の領域
    • 圧縮・展開パイプライン(例: DEFLATE、Huffman、CRC32)は完全に検証されており問題はない
    • 検証が適用されていない部分で脆弱性が発生
  • ランタイムオーバーフローの本質

    • Lean ランタイムは 信頼基盤計算ベース(TCB) に属する
    • すべての Lean の証明はランタイムの正しさを前提としている
    • したがってランタイムバグは、すべての Lean プログラムの安全性に影響する

検証済みコードの安定性

  • 1億500万回超の実行結果

    • Lean が生成した C コードでは ヒープオーバーフロー、use-after-free、スタックオーバーフロー、UB、配列境界超過 はいずれもなかった
    • Claude の評価によれば、最もメモリ安全なコードベースの1つ と分析された
  • Lean の型システムの効果

    • 依存型 (dependent types)well-founded 再帰 の構造により、 C/C++ ベースの zlib 実装で一般的な脆弱性(CVE クラス)を構造的に防いでいる
  • 結論

    • 検証済みコード領域は非常に堅牢で、ファザーがエラーを見つけるのは難しかった
    • しかし未検証の部分とランタイム層には依然として脆弱性が存在する
    • 検証は適用範囲と信頼基盤の安全性によって制約される

核心的な教訓

  • 形式検証は 適用されたコード領域内では非常に強力 だが、 未検証の周辺コンポーネントやランタイム層 が全体の安定性を脅かす可能性がある
  • 信頼基盤計算ベースの安全性確保 が必須であり、 検証済みシステムであっても「誰が監視者を監視するのか(Quis custodiet ipsos custodes)」という問いは残る

参考リンク

1件のコメント

 
GN⁺ 14 일 전
Hacker Newsのコメント
  • この記事のタイトルとフレーミングは少しおかしいと感じた
    実際、著者自身が証明済みコードの中ではバグや不具合は見つからなかったと明記している
    見つかった2つのバグは証明の範囲外にあり、1つは仕様の欠落、もう1つはC++ランタイムのヒープオーバーフローだった
    バグが見つかったのはLeanのランタイムであって、検証を行うカーネルではないことを強調したい
    Leanドキュメントへのリンク を参照

    • 検証済みシステムのバグを論じるなら、システム全体のバイナリを考慮すべきだと思う
      バッファオーバーフローでビットコインを失ったとしたら、そのバグがランタイムにあったという事実は慰めにならない
      また、プログラムがクラッシュするなら、ほとんどのユーザーはそれをバグだとみなすだろう
      Leanを実際の本番環境で動かしている人もかなりいるはずだ
    • タイトルだけ見てLean カーネルにバグがあるのかと思ったが、実際にはLeanランタイムとlean-zipで見つかった問題だった
      カーネルでないなら、証明そのものの信頼性には大きな影響はない
    • lean-zipの仕様の欠落は、検証の哲学という点で重要な問題だ
      「Hello world」プログラムを検証するにしても、出力だけでなく副作用まで仕様化しなければならない
      ランタイムとカーネルの境界でメモリ破損が起きるなら、証明の信頼性は下がりうる
      結局のところ、何を検証すべきかを明確に定義することが核心だ
    • このサイトの投稿一覧を見ると、どんどんクリックベイトっぽくなっているのが面白かった
      関連記事一覧
    • Leanランタイムのバグをlean-zipのバグと呼ぶのは、JREのバグをJavaアプリのバグと呼ぶようなものだ
      著者は意図的に誤解を誘っているように見える
  • 私も証明済みコードで似たような経験をしたことがある
    私の場合、オーバーフローよりも仕様の欠陥(spec bug) が問題だった
    仕様が間違っていれば、コードと証明が完璧でも意図した動作とは違う結果になる
    結局、検証の難しさは人間の意図を正確に表現することにある
    AIが検証を完全に解決してくれるという信念は、誤った確信を与えかねない

    • 私にも似た経験がある
      Lean、TLA+、Z3のようなツールで証明可能な仕様は単純化されていて、多くの仮定が必要だ
      それでもこれは強力なツールだ
      複雑なアルゴリズムのバグを絞り込み、仕様の境界を明確に見えるようにしてくれる
      AIのおかげで、こうした証明作業はずっと簡単になった
    • いつも抱く疑問は、検証システム自体をどう信頼するのかということだ
      プログラムを検証する別のプログラムにも、結局はバグがありうる
      完全な自己検証は不可能だという点で、ハイゼンベルクの不確定性原理を思い出させる
      結局、検証とは「第二の独立した実装」によって信頼度を高めるプロセスだ
    • 私のコードのバグは2種類に分けられる
      (1) 意図した動作と違う動きをする場合
      (2) 意図どおりに動くが、その意図自体が間違っている場合
      証明支援系は(1)は防げるが、(2)は防げない
      つまり、設計の健全性は証明できない
      seL4のようにすべてを検証するのは、あまりに高コストで時間もかかる
    • 仕様そのものを検証する方法が必要だと思う
      形式論理 + 敵対的思考(adversarial thinking) を組み合わせて、プログラムがすべきことと、してはいけないことを完全に列挙するやり方が理想的だ
  • タイトルがクリックベイトのように感じられた
    証明された部分にはバグがないのに、なぜこう書いたのかわからない
    HNでは事実に基づく投稿を読みたいのであって、これは時間の無駄だった

    • 検証済みソフトウェアで「証明に反するバグだけをバグと見なす」というのは公平なのか?
      「バグなし」と宣伝していても、実際には「仕様が完全に表現された範囲内でのみ」だ
      完全に正しくても、現実では正しいまま死ぬことがありうる — つまり、理論上は正しくても実際には壊れうる
    • タイトルは技術的には正しいが、lean-zipの未検証コードで起きた問題を、あたかも証明済み部分のバグのように表現している
    • 結果として、「証明済み部分の範囲を明確にせよ」というメッセージ自体は興味深く妥当だ
    • 2つ目のバグは、根拠なくでっち上げられたように見える
      コード参照もなく、実際のコードを見れば誤解だとわかる
    • 結局、lean-zipは単なるZlibバインディングにすぎない
  • この問題は分散システムの検証でもよく現れる
    証明は特定の条件(運用範囲)の中でのみ有効で、面白い故障はその境界で起こる
    TLA+も同様で、現実が仮定から外れれば証明は無意味になる
    私が欲しいのは、運用範囲を機械的に明示し、ランタイムでそれを監視する機能だ

    • 私もCPUのハードウェアバグを直接見つけたことがある
      しかし、ほとんどのバグはハードウェアではなく、人が文書を読まないことから生じる
      形式モデリングだけでも、バグの数は大幅に減らせる
  • これは予想可能な良いニュースだ
    LLMが形式検証を通るコードを作れるという意味だからだ
    今後はバグがますますハードウェア層へ移っていくだろう
    最終的にはハードウェア仕様の形式化が必要になるはずだ
    メーカーがそれを公開しなければ、コミュニティとの対立が起きるかもしれない
    長期的には、脆弱性の絶滅意図的な挿入かという二つの方向に分かれるだろう

  • 最初は著者が証明済み部分をテストしていなかったのかと思った
    だが読んでいくと、バグは証明の範囲外で見つかったものだった
    なので、タイトルはやや誇張した表現に感じられた

    • 記事の著者本人が登場している
      検証済みシステムのバグなら、対象はシステム全体のバイナリであるべきだと主張している
      実際にArchive.leanの圧縮ヘッダ解析部分でクラッシュを起こす入力を見つけたと述べている
  • Donald Knuthの有名な言葉を思い出した
    「上のコードにはバグがあるかもしれないので注意せよ。私はそれが正しいと証明しただけで、実行してはいない」という警告だ

  • パーサを検証していないことは大きなミスに見える
    バイナリ形式の解析は常に危険な領域だ

  • 要点は、AIエージェントがfuzzerと協調してLeanでヒープバッファオーバーフローを発見したということだ
    これはかなり大きな成果だ

    • 本当に有用な発見だと思う
  • Claudeが「これは私が分析したコードの中でも最もメモリ安全なコードベースの一つだ」と言っていた部分が印象的だった
    ただ、これではClaudeが初めて分析したコードであるかのような冗談にも聞こえる

    • RLで広範に訓練されたClaudeが、初めてコードを分析したはずはない
      彼がこれほどうまくやれる理由は、まさにそこにある
    • もしかするとClaudeは、私たちの知らない何かをひそかにやっているのかもしれない 😄