こんにちは初めまして!
watnowに所属しています、立命館大学3回生のumiyuriと申します。
僕は開発経験もあまりなく、ほかのメンバーのような技術的な話はあまりできないのです....
最近研究室に配属されたこともあり、研究してみたいなとおぼろげに思っているゲームに対するファジングの適用についてお話ししようかなと思います!
ゲームへのファジングの適用については、世界でも例が少なく、まだまだ発展途上の分野となっています。
ぜひ頭の片隅に残しておいていただけると嬉しいです!
そもそもファジングって何???
ファジングという単語を聞いたことがない人も多いのではないかのと思います。
(そもそも一般的なものではない)
IPA(情報処理推進機構)の資料では、
検査対象のソフトウェア製品に「ファズ(英名:fuzz)」と呼ばれる問題を引き起こしそうなデータを大量に送り込み、その応答や挙動を監視することで脆弱性を検出する検査手法です。
と述べられています
つまりもっと簡単かつ適当にいうなれば、「ランダムにいろいろ入力してみて、おかしな出力が出てないか検証する」ってこと
ソフトウェアのテスト手法の一つなんだなあと思ってもらえたらと思います。
ファジングを用いることの利点
ファジングを用いる利点としては、開発者が想定していない入力までテストできることが挙げられます
例えば、ゲームなら壁に向かってダッシュしてジャンプしまくると壁に埋まる、的なバグはよくあるかと思います。
普通のプレイではこんな入力は起こるはずがないですよね(笑)
このようなバグは従来のテスト手法ではテストしきれていない可能性が高いです。
ゲームにファジングを適用することの難しさ
「ゲームって想定しないバグ結構ありそうだし、ファジングでゲームのテストやればいいじゃん」という発想が出てくるのも自然な流れかと思います。
そんな感じで僕の研究室では先輩たちが頑張っているわけですが、なかなかこれが一筋縄ではいかんのです...
ゲームごとに作り替えなきゃいけない
これは当たり前ではありますが、ゲームはジャンルによってどんな操作が必要かが異なります。
レースゲームならコースの通りに進み続けますし、オープンワールドなら何をやってもいいんです。
https://www.youtube.com/watch?v=HljJvwifETg | https://www.youtube.com/watch?v=OMw-xmBEWuE |
必要な入力のタイミング、継続時間等もゲームによってそれぞれ特殊になっているので、「ゲームのファジングができるツール」として開発できないという難点があります。
単純にファジングするだけだと意味を為さない
前述した通り、ファジングはランダムに入力を生成することでソフトウェアの脆弱性を発見します。
特にオープンワールドのゲームにファジングをただ適用しただけだとこうなるのです
そう、その場で動き続けるだけなのです
これは毎フレームごとに入力を生成しているからで、状態の数が膨大になってしまうからだと考えられます。
例えば、簡単のために、上下左右4方向にしか移動できないとしましょう。
1フレームにつき4通りの選択肢があります
つまり、
1秒(=60フレーム)で4^60通り、
1分(=3600フレーム)で4^3600通り
になってしまうわけです
最近のオープンワールドなら斜めにも進めるので、実際のゲームにしてみたらもっと状態数は多くなります....
こんなの、1秒間まっすぐ進むだけでも奇跡なわけです😅
これで、ゲームにファジングを適用することの難しさを理解してもらえたんじゃないかなと思います
ゲームファジングの希望「IJON」
そんな時に現れたのがIJONです!!!!
なんとこいつは初代スーパーマリオをクリアできるファジングツールだと言い出したんです。
IJONの論文内の画像より引用
従来のファジングツールとして有名なAFL(American Fuzzy Lop)と比較しても歴然で、AFLでは一度もクリアできていません
なぜIJONが優れているのか
それはIJON固有の機能であるアノテーションが効いています。
人間がソースコードにアノテーション
といわれる数行のコードを挿入することができます。
こんな感じで
void ErrorMessege::setmsg(const std::string &msg)
{
static unsigned int call_count = 0;
call_count++;
+ IJON_MAX(call_count);
...(中略)...
}
https://kuznetsov.am/fuzzing-cppcheck-ijon より引用
アノテーションを付けることで、ファジングツールが生成する入力列を少々ガイドしてやることができます。
これで効率的にいろんな状態を探索することが可能になるわけですね!
IJONで実装されているアノテーションにはたくさん種類があります。
(気になる人は論文を読んでみてください)
今回は初代マリオをクリアした例で使用されていた IJON_MAX()
について紹介します。
IJON_MAX()
では、引数に設定された変数が最大になるように入力列を生成するようになります。
マリオをクリアさせるなら、右側にゴールがあるのでX座標を最大化するようにすればよさそうですよね。
IJONの論文内の画像より引用
こんな数行を挿入するだけで探索の効率が爆伸びするんです、革命的ですね...
AFLでは12時間かけてもクリアできなかったのに対し、IJONは難なくクリアでき、さらにはグリッチまで2つ発見したと報告しています
IJONの欠点
こんな完璧にも見えるIJONに欠点は存在しているんです。。。
一番の問題は、IJONは更新が4年前で止まっていてろくに保守されていないことなんですけどね
誰でも簡単に使えるというわけではない
アノテーションをソースコードの中に挿入しないといけない関係上、プログラムをよく理解しているエンジニアしか利用できません。
適当にアノテーションを入れ込んだだけでは探索の効率が上がらず、普通にAFLを動かしているのと変わりません。
アノテーション入れるの普通にめんどい
ゲーム性を理解して、どのようにアノテーションを入れればいいか考え、ソースコードを読み、必要な変数を見つけ、アノテーションを挿入して,,,
めちゃめちゃしんどいし、時間かかっちゃうんですよね
少なくとも「個人開発で作成したゲームにIJON適用してバグ探すぞ!」とかめんどくさくて僕は絶対やらないですね
アノテーションを入れてガイドした入力で本当に想定しないバグは見つけられるのか?
前述したとおり、ファジングは「ランダムな入力を生成し、開発者が想定していないバグを見つけることができるツール」です。
- アノテーションなんか入れ込んで人間が恣意的に入力列をガイドしてしまったら、それは本当にランダムに生成された入力なのでしょうか?
- 人間が恣意的に誘導した生成列で、想定しないバグを発見できるのでしょうか?
これは議論の余地があるように感じていますが、僕は下の図のようなグラデーションでつながっていると考えています。
完全にランダムでもないが、想定しないバグを完全に発見できないほど人間が誘導しているわけではないというのが僕の考えです。
というか、ちょっと自由度高くなると前に載せた動画みたいに、テストとして成立しないんですよね...
人間が少しくらいガイドしてやることは、ファジングを適用する上で必須のものだと感じています。
終わりに
いかがでしたでしょうか?
ゲームファジングの希望「IJON」についてお話させていただきました。
入力列をもともと用意しておいて、それに基づいて行われる自動テストであれば、現在でも盛んに行われているのですが、ゲームに対するファジングの適用はまだまだ発展途上です。
少しでも覚えておいてもらえると嬉しいです!
もしIJONを動かしてみたいと思った人がいたら僕の記事を参考にして動かしてみてください!
マニアックすぎて日本語で書かれたIJONの記事は僕の記事のみです!
ここまでお付き合いいただきありがとうございました!
参考
ファジング活用の手引き - IPA (2024/12/6 参照)
Ijon: Exploring Deep State Spaces via Fuzzing - IEEE (2024/12/7 参照)
Фаззинг cppcheck с помощью IJON+AFL - Александр Кузнецов (2024/12/8 参照)