背景
- C/C++ で画像やレンダリングシーンデータのバイナリデータを読んだり, 文字列処理をしたりしている
- バグがないかテストケースをたくさん書くのがめんどくさい
- fuzzer についてはなんとなく知っている
- 時々 tinyexr とかで fuzzer でレポートがきます https://github.com/syoyo/tinyexr/issues/107
- fuzzer についてはなんとなく知っている
fuzzer があります.
ご注文は American Fuzzy Lop ですか?
https://qiita.com/binzume/items/8e985e7bd01a6e4000d3
Clangでファジング (-fsanitize=fuzzer)
https://qiita.com/snsinfu/items/67bd5196a53e5575c4b9
最近は Clang にデフォルトで取り込まれました(以前は libFuzzer.a をリンクなどが必要だった)
Ubuntu 18.04 では, apt で入る clang-8 でいけます. 便利な世の中になりましたね
$ sudo apt install clang++-8
$ sudo apt install libfuzzer-8-dev
必要に応じて, clang++ で clang++-8 が呼ばれるようにしておきます.
$ sudo update-alternatives --install /usr/bin/clang clang /usr/bin/clang-8 10
$ sudo update-alternatives --install /usr/bin/clang++ clang++ /usr/bin/clang++-8 10
あとは
あたりを参考にして, fuzzer テストを回すプログラムを書いていけばよいです.
基本的には address sanitizer, memory sanitizer を有効にします.
cxx option いろいろ変えたりするので, サードパーティライブラリとたくさんリンクしている場合はビルド設定が面倒になりますので, うまく最小で再現するようなコードにするのがよいでしょう.
注意点
ubuntu apt などで入る clang では runtime/abi を既存の gcc toolchain で賄うためか, うまく log(backtrace) を出してくれないケースがあります
(segmentation fault だけ出す. たとえば関数を無限に再帰的に呼んで stack overflow になるときなど)
から得られるパッケージを使うとよさそうです.
実行する
- -jobs=8 などで並列実行するとよいでしょう.
- コードにもよりますが, 画像やシーンデータとかですと入力データサイズは GB も普通なので,
-rss_limit_mb=10000
などとして, でメモリ上限をあげましょう.
Copus
PNG, JPEG などフォーマットが決まっているような場合, Copus データとしてそれらを与えることでより効率的に fuzzing test することができます.
Fork mode
2022/09 時点では experimental ですが, fork モードでいくつかパターンを分岐させて処理できます.
oss-fuzz
OSS であれば github で PR でリクエストを送れば, GCE あたりでクラウドで実行してくれる oss-fuzz サービスがあるようです.
fuzzer で不都合が見つかったら?...
fuzzer はバグが再現するテストデータを作ってくれるだけなので, コードをどう修正するかはコード作成者次第です.
何度か fuzzer で見つかる不都合を見ていると, どうコードを直すか(値の範囲チェック, out-of-bounds アクセス, etc)がわかってきますので, それを参考にロバストなコード(きちんとエラーチェックとかする)を書いていきましょう.
tinyexr のときに経験しましたが, 毎回 fuzzer で見つかった不都合部分にチェックを入れてその場その場で直すのは手間で, 一から書き直したほうが手っ取り早く感じるケースもあります(tinyexr の場合は書き直すのも, めんどうなのでなかなかとりかかりには至っていませんが...)
本来起きないはずだったり, 正しいのではあるが fuzzer, sanitizer が検出できるメモリサイズを超えている(e.g. 4GB)ででエラーになっているだったりとか, 原因は third-party のコードにあり修正が難しい(他人のコードの解析がめんどい), など, どう判断するのがよいのかに困るものもあったりします.
TODO
- 見つかった crash レポートを入力させて regression させる仕組みをつくる(fuzzer でビルドされたプログラムには指定できないっぽい?)
- ClusterFuzzer を手元計算機環境で回したい(デフォルトの設定は GCE 限定っぽいがいろいろ書き換えればいけるはず)
- 機械学習あたりでこのコードはこう直すとよいよ, という助言とかしてほしい