Rustでファジングテスト
こんにちは、株式会社LabBase(旧POL)でエンジニアをしているミズノです。
Cargoには便利なツールチェインがありますが、今回はファジングをサポートするcargo-fuzzを紹介します。
ファジングとは
ファジングはセキュリティの調査などでも利用される方法ですが、ランダムなインプットを作成し、予期せぬバグを見つけることに役立ちます。
cargo-fuzz
cargo-fuzzはLLVMのlibFuzzerをベースにしたファジングサポートを提供します。インストールは以下のコマンド。
$ cargo install cargo-fuzz
初期化
ツールチェインを入れたら、開発中のクレートで以下のコマンドを実行します。このコマンドでプロジェクト内にfuzzフォルダが作られると思います。これでファジングのコードをかける準備ができます。
$ cargo fuzz init
fuzz/fuzz_targetsフォルダ内のファイルがテストコードです。今回はi32型へのパースする処理に対してファジングを試します。サンプルコードは以下の通りです。
#![no_main]
use arbitrary::Arbitrary;
use libfuzzer_sys::fuzz_target;
#[derive(Arbitrary, Debug)]
struct Input {
s: String,
}
fuzz_target!(|input: Input| {
use fuzziest::parse_i32;
parse_i32(&input.s);
});
Nightlyバージョンで実行
ファジングはstableバージョンのrustcではなく、Nightlyバージョンで動作します。事前にNightlyバージョンに変更しましょう。
Nightlyバージョンのインストールはこちら
$ rustup install nightly
デフォルトを切り替えます。
$ rustup default nightly
これで準備が整いました。それでは実行してみましょう。
$ cargo fuzz run [ターゲット名]
ターゲット名は以下のコードで調べることができます。
$ cargo fuzz list
ターミナル上で、次々にテストが実行されます。エラーが発生すると、以下のような表示が出ます。
────────────────────────────────────────────────────────────────────────────────
Failing input:
fuzz/artifacts/fuzz_target_1/crash-32b2dcbdb3688e6114fabbf2a754789d8997efa2
Output of `std::fmt::Debug`:
Input {
s: "エラーが起こった入力値",
}
Reproduce with:
cargo fuzz run fuzz_target_1 fuzz/artifacts/fuzz_target_1/crash-32b2dcbdb3688e6114fabbf2a754789d8997efa2
Minimize test case with:
cargo fuzz tmin fuzz_target_1 fuzz/artifacts/fuzz_target_1/crash-32b2dcbdb3688e6114fabbf2a754789d8997efa2
────────────────────────────────────────────────────────────────────────────────
Minimize test case with の下にコマンドが表示されていますが、これを実行すると今回エラーを発見したテストケースを保存し、ピンポイントで実行できるようになります。ファジングはマシン自体にかなり負荷をかけたり、時間がかかる場合があります。毎回テストを待つのは開発速度を落とすことにもなるので、効率よく試せるのはありがたいですね。
Rustの開発体験はCargoのおかげでとても良いですね。意外と知らないツールチェインがあるので開発体験を改善したい人はぜひ調べてみると良いと思います。
そして弊社ではRustゴリゴリ描きたいエンジニアを絶賛募集しています。お気軽にカジュアル面談を申し込んでください。カジュアル面談は選考要素0ですので、本当に気軽にご利用いただいて大丈夫です。
お待ちしています。
この記事が気に入ったらサポートをしてみませんか?