sweep(差分テスト / 生成ベースのバグ発見)

「sweep」は俗称で、世の中では 差分テスト(differential testing)+ ランダム/系統的なプログラム生成(コンパイラ fuzzing) として確立した手法。多数のプログラムを生成し、同値であるべき2つの実装の出力を突き合わせて発散(divergence)を炙り出す。McKeeman (1998) が「差分テスト」として定式化したのが起点。

中心アイデア: オラクル問題の回避

テストの難所は「正解(オラクル)を別途どう用意するか」。差分テストはこれを回避する — 正解を決めずとも、同値なはずの2実装が食い違えば、どちらかが必ずバグ。生成器が入力を量産し、照合が穴を炙り出す。

世の中の確立した手法・ツール

名前対象オラクルの作り方
Csmith(Yang+ PLDI’11)C コンパイラランダム C 生成 → 複数コンパイラ(GCC/Clang…)の出力を差分。3年で 325+ バグ
EMI / Equivalence Modulo Inputs(Le+ PLDI’14)最適化コンパイラ実行されない分岐を刈って同値変種を作る(メタモルフィック)。GCC/LLVM で 147 バグ
YARPGen(OOPSLA’20)C/C++スコープ/UB を制御したランダム生成
QuickCheck(Claessen & Hughes 2000)任意の関数性質(プロパティ)を生成入力で反証。Hypothesis(Py)/fast-check(JS)
SQLancer(M. Rigger)DBMS の論理バグ同値変換オラクル PQS / NoREC / TLP(メタモルフィック+差分)
jsfunfuzz / AFL / libFuzzer / OSS-FuzzJS エンジン・任意プログラムカバレッジ誘導 fuzzing

→ 「sweep」が指すのは、この系統の クロス実装/クロスターゲットの差分テスト。

オラクルの3類型

正解を用意できない所で「食い違い」を判定する作り方は、ほぼ3つ:

  1. クロス実装/ターゲット — 同一仕様の別実装を互いの基準に(Csmith: 複数コンパイラ、native vs WASM の照合)
  2. メタモルフィック — 入力/プログラムを意味を変えない変換にかけ、出力が変わったらバグ(EMI、SQLancer の TLP/NoREC、決定性前提)
  3. 参照実装 vs 最適化版 — 素朴で正しい実装 vs 速い実装

なぜ spec テストより穴を見つけるか

人手の spec テストは限られたケース + 部分観測(例: 長さや勝者だけ)で、想定外の組み合わせ・境界を踏まない。生成ベースは広く薄く当てて穴を突くので「やればやるだけ出てくる」。モデル検査の全探索とは対照的に、確率的・系統的に網羅の隙間を埋める。

発見した後: 縮める

生成された発散プログラムは巨大で読めないことが多い。test-case reduction(C-Reduce / delta debugging)で、発散を保ったまま最小の再現例へ機械的に縮めるのが定石。

身近な実例: Almide の closure cross-target sweep

クロスターゲット差分テストの一例。closure プログラムを native(Rust 経由)== WASM で照合し、カテゴリ別(スカラ/フラット配列/ネスト/variant payload/HOF)に振る。初回で 15 発散 → 修正、再走で 2 バグ(Unit-param closure variant の trap、group_by Int-key の invalid module)。→ vault では [almide-differential-gate|差分ゲート]と対。

押さえどころ(カード化候補)

関連

Graph