HN 日本語サマリー

← 一覧へ戻る
プログラミング

Gleamコンパイラのファジング

Fuzzing the Gleam Compiler (kurz.net)

56 pointsby crowdhailer6 コメント

要約

この記事では、Gleamコンパイラにおけるバグ発見のために構造を意識したファジング手法を探求しています。LLMによる試みが限定的な結果に終わった後、コンパイラのような構造化された入力に適したファジングが採用されました。Gleamは、ErlangとJavaScriptの両方のターゲットを持つこと、ミニマルな構文、静的型付け、そしてRustで書かれていることから、ファジングに適した言語であると説明されています。

全文翻訳

ファジング Gleamコンパイラのファジング ランダムなプログラムを生成することでコンパイラのバグを見つけられるか? 公開日: 2026年8月25日 火曜日 はじめに 私は定期的にGleamの変更ログと課題トラッカーをチェックしています。このプロジェクトとそれに貢献する人々が大好きです。しかし、コード生成やErlangとJavaScript間の出力の違いに関連する課題を見るたびに、「すべてのGleamプログラムを計算する」方法がなく、それらを実行して問題がないか確認できないことが気になっていました。チェス盤を想像してみてください。そこにはほぼ無限の可能な盤面がありますが、私たちはその盤面がGleamプログラムを含み、それらがテストされていないエッジケースを明らかにするために、それらのプログラムの無限に大きなデータベースを求めています。これに隣接する何かを試みた最初の試みは、実際にLLMに指示することでした。過去の多くのGleamの課題を読み込み、「深く考えて」より多くのエッジケースを見つけるように指示しました。ビット配列の組み合わせ、ネストされた無名関数、ネストされたuseパターンなど、あらゆる種類のものが生成されました。予想通り、このアプローチはあまり多くの結果をもたらしませんでした。20ドルのトークンを費やした後、それはちょうど1つの課題を見つけ、それはすぐに報告され修正されました: https://github.com/gleam-lang/gleam/issues/5613。1つは間違いなくゼロより多いです。しかし、「LLMファジング」には多くの問題があります。それは高価で、決定的ではなく、スロットマシンのレバーを引くようなものです。しかし、私が避けてきたもう一つのアイデアがありました。正直に言うと、それは単に多くの作業のように聞こえたからです。構造を意識したファジング。ソフトウェアを書くのは難しく、人間はそれにあまり長けていません。助けとして、バグの探索を部分的に自動化できる他のソフトウェアを開発しました。これらのプログラムの1つがファザーです。それらはプログラムにフィードするためのランダム化された入力を生成します。その前提は、大規模に、これらのランダムな入力が、まだ私たちが考えていないエッジケースを表面化するような方法で分布することです。ファザーは、完全にランダムなスクランブルされたバイトから、高度に構造化された文法を意識したASTまで様々です。プログラムに完全にランダムなバイトをフィードすることは、画像、ファイル、ネットワークリクエスト、プロトコルなどを扱うユースケースでよく行われます。オープンソースソフトウェアで実際のセキュリティ上の欠陥やバグを発見したファジングの例はたくさんあります。例えば、zzufによるFirefoxでの発見では、画像ファイルの一部のビットを反転させるとブラウザがクラッシュしました: https://nvd.nist.gov/vuln/detail/CVE-2007-6715。しかし、ファザーはバッファオーバーフローを通じて実際の悪用可能なセキュリティ上の欠陥も発見しています。Googleの「OSS Fuzz」というプログラムがあり、多くの重要なオープンソースプロジェクトを継続的にファジングしています: https://google.github.io/oss-fuzz/。私たちのケースでは、ブラウザやネットワークプロトコルを扱っているわけではありません。コンパイラを扱っています。そしてそれは構造を意識したファジングの可能性を開きます。つまり、ランダムなバイトのストリームを生成するのではなく、ソースコードまたはASTの形式のコードのストリームを生成します。Gleamを導入 Gleamには、ファジングの特に興味深い候補となるいくつかの特徴があります。それは2つのターゲット、JavaScriptとErlangのためにコードを生成します。同じプログラムの出力を両方のターゲットで比較し、違いがあればフラグを立てることができます。Gleamはミニマルな構文を持っています。少なくとも、ほとんどの他の人気のあるプログラミング言語と比較して。比較的少ないコードで、言語が提供するほぼすべての概念をカバーする有効なプログラムを生成できます。静的型。言うまでもなく、これはプログラムが実行時にクラッシュしないことを保証できる素晴らしい機能です。それは型システムにバグがないことを意味するわけではありません。過去には型推論に関連する問題がありました。しかし、後で学ぶように、言語の各側面は独自のテストアプローチを必要とします。関数型であり、すべてが式であるという事実は、プログラムの構成と構造化を非常に便利にします。Rust。これは見過ごされやすいかもしれませんが、Gleamコンパイラ自体がRustで書かれているという事実は、既存のファジングツールを統合することを非常に容易にします。1つの.gleamファイルを実行することなく、コンパイラの一部をテストできます。参考資料 私はファザーのより技術的な側面に飛び込みます。しかし、コードや詳細にはあまり触れません。もしそれについてもっと読みたい場合は、Nick Fitzgeraldによるこの投稿とブログをチェックしてください。これはこのプロジェクトの主なインスピレーションとなりました: https://fitzgen.com/2020/08/24/writing-a-test-case-generator.html 私たちのファザーは、ミューテーションベースではなく、ジェネレーションベースになります。違いをよりよく理解したい場合は、この記事を読むことをお勧めします: https://fitzgen.com/2026/06/01/structure-aware-fuzzing-experiment.html その記事では、著者は、少なくともwasmについては、ミューテーションベースのアプローチがジェネレーションベースのアプローチよりもはるかに多くの問題を発見したと結論付けています。したがって、将来的にはこのプロジェクトに実装する価値があるでしょう!トピックについてさらに深く掘り下げるには、このリソースをチェックしてください: https://www.fuzzingbook.org/。Gleamファザーの完全なコードは、私のGleamフォークのこのブランチで見つけることができます: https://github.com/daniellionel01/gleam/tree/fuzzing フェーズ1: パーサー ファザーの重要な設計上の選択: 公開コンパイラAPIを使用する。コンパイラAPIには安定性の保証がないかもしれませんが、これにより将来のGleamバージョンとの互換性を維持しやすくなります。また、実装の詳細に触れることを避け、誤検知や偽陰性を作成していないことを確認する良い方法です。私たちのパーサーがどのように出力をキャッチして分類するかを示す例をご覧ください: $ cargo run -p fuzzing-core --example classify "pub fn main() { 1 }" -> compiled (js: 39B, ts: 32B, erl: 238B) "pub fn main() { let f = fn(x) { x + 1 }; f(41) }" -> parse error "pub fn main() { 1 +. \"x\" }" -> analysis rejected (javascript) "pub fn main() {" -> parse error fuzz crateといくつかのラッパーコードを使用することで、コンパイラをクラッシュさせることができるかどうかを確認するために、ランダムに生成された入力(まだ構造化されていない)でGleamコンパイラを非常に迅速にスパムできます。出力が非常に大きいため、1秒間しか実行しません: $ cargo +nightly fuzz run parse_only --fuzz-dir fuzzing-harness -- -max_total_time=1 -timeout=10 INFO: Running with entropic power schedule (0xFF, 100). INFO: Seed: 302379076 INFO: Loaded 1 modules (740945 inline 8-bit counters): 740945 [0x105eeac70, 0x105f9fac1), INFO: Loaded 1 PC tables (740945 PCs): 740945 [0x105f9fac8,0x106aedfd8), INFO: 2466 files found in fuzzing-harness/corpus/parse_only INFO: -max_len is not provided; libFuzzer will not generate inputs larger than 4096 bytes INFO: seed corpus: files: 2466 min: 1b max: 4046b total: 425422b rss: 62Mb #2467 INITED cov: 2434 ft: 8563 corp: 1249/171Kb exec/s: 0 rss: 108Mb #2513 REDUCE cov: 2434 ft: 8563 corp: 1249/171Kb lim: 3764 exec/s: 0 rss: 108Mb L: 48/3753 MS: 1 EraseBytes- #2645 REDUCE cov: 2434 ft: 8563 corp: 1249/171Kb lim: 3764 exec/s: 0 rss: 108Mb L: 8/3753 MS: 2 ChangeBit-EraseBytes- #2656 REDUCE cov: 2434 ft: 8563 corp: 1249/171Kb lim: 3764 exec/s: 0 rss: 109Mb L: 2/3753 MS: 1 EraseBytes- #2937 REDUCE cov: 2434 ft: 8563 corp: 1249/171Kb lim: 3764 exec/s: 0 rss: 109Mb L: 314/3753 MS: 1 EraseBytes- #3183 NEW cov: 2434 ft: 8578 corp: 1250/172Kb lim: 3764 exec/s: 0 rss: 110Mb L: 1054/3753 MS: 1 CopyPart- #3591 REDUCE cov: 2434 ft: 8578 corp: 1250/172Kb lim: 3764 exec/s: 0 rss: 111Mb L: 99/3753 MS: 3 ShuffleBytes-CrossOver-EraseBytes- #3934 NEW cov: 2434 ft: 8585 corp: 1251/173Kb lim: 3764 exec/s: 0 rss: 112Mb L: 399/3753 MS: 3 CMP-CopyPart-CopyPart- DE: "\010\000\000\000\000\000\000\000"- # ... NEW_FUNC[1/7]: 0x0001031cfb88 in _RINvNtCs3kGMwX4aip8_4core3ptr9drop_glueINtNtCshX1O598ANu2_5alloc3vec3VecINtNtNtCs846PmCUGaYz_10gleam_core3ast8constant8ConstantuEEEB1f_+0x0 (parse_only:arm64+0x1002abb88) NEW_FUNC[2/7]: 0x00010323bff4 in _RINvNtCs3kGMwX4aip8_4core3ptr9