概念定位

Fuzz test 把沒想過的輸入轉成可重播、可修補的失敗案例,補齊人工列舉無法觸及的邊界盲區。

這一頁處理的是輸入空間的盲區。當 API、parser、codec 或 schema 的邊界不清楚時,fuzz 比人工列案例更能覆蓋非預期路徑。

核心判讀

判讀 fuzz 的品質先看 target 選擇是否對準高風險輸入邊界,再看 corpus 是否持續收斂,最後看 crash 是否能轉成可回歸的修復。

重點判斷:

  • fuzz target 是否足夠小,能對準單一責任
  • corpus 是否持續收斂,coverage delta 是否仍為正
  • crash reproduction 是否可重播到同一條路徑
  • 修補後是否回寫成 regression test

Fuzz target 設計

Fuzz target 是 fuzz campaign 的最小驗證單位,責任是把外部輸入導入一個可觀測邊界的函式。

好的 target 對準單一 parser、codec、serializer 或 validation function,函式簽章接受原始位元組(如 func([]byte) 或等效形式)。target 選擇的判準有三個:這個函式是否直接處理外部輸入、邊界行為是否不清楚、crash 是否有業務影響。

target 粒度影響 fuzz 的效率與判讀價值。target 太大(整個 HTTP handler 含 auth / routing / DB 存取)會讓 crash 難以定位到具體邊界,因為 fuzz engine 需要同時探索太多分支,coverage 增長慢且 crash 歸因模糊。target 太小(單一 if 分支)會讓 coverage 增長無意義,因為分支行為已經被 unit test 覆蓋。

常見的高價值 target 類型:

Target 類型典型邊界風險範例
Protocol parser畸形封包、長度溢位、巢狀深度HTTP header parser、gRPC frame decoder
Schema deserializer型別不匹配、缺欄位、巢狀物件遞迴JSON/Protobuf/MessagePack deserializer
Image / media codecbuffer overflow、memory allocationPNG decoder、PDF parser
Validation function邊界值、正則回溯、encoding 混淆email validator、URL parser、SQL escaper
Config parser非預期組合、環境變數注入YAML/TOML config loader

Corpus 管理

Corpus 累積有效的輸入種子,讓 fuzz engine 能從已知邊界往外探索。corpus 品質直接決定 fuzz campaign 的探索效率。

初始 corpus 從三個來源收集:unit test 的既有 fixture(已知的合法與邊界輸入)、production sample 脫敏後的真實請求(反映實際流量的輸入結構)、schema 範例與文件中的合法樣本。初始 corpus 的重點是涵蓋主要合法路徑,讓 fuzz engine 從合法輸入開始 mutation,更容易觸達邊界。

持續擴充靠 coverage-guided mutation。fuzz engine 每次產生的 mutated input 若觸發了新的 code path(新分支、新呼叫),這個 input 會自動加入 corpus。隨著 campaign 進行,corpus 會累積越來越多能觸達深層分支的種子。

corpus 品質的判讀指標是 coverage delta trend — 每個時段新增的 code path 數量。coverage delta 持續為正代表 corpus 仍在有效探索;coverage delta 趨近零代表當前 target 的探索接近飽和,應考慮三個方向:切換到新 target、調整 mutation dictionary(加入 domain-specific token)、或擴充初始 corpus 的多樣性。

corpus 需要持久化管理。corpus 檔案應納入版本控制或 artifact storage,跨 CI job 保留。每次 fuzz campaign 結束時,新發現的有效種子合併回 corpus;crash input 在修復後轉成 regression fixture,從 fuzz corpus 移到 test fixture。

Crash reproduction 與 minimization

Fuzz 找到 crash 後的處理流程是 reproduce → minimize → fix → 回灌 regression test。

Reproduce:用 fuzz engine 產出的 crash input 在相同環境重跑,確認 crash 可穩定觸發。不可穩定觸發的 crash 通常來自 race condition 或環境差異,需要額外的 concurrency 或環境控制才能定位。

Minimize:minimization 把觸發 crash 的輸入縮到最小等效形式,讓 root cause 更容易定位。自動化 minimizer(如 Go 內建的 fuzz minimizer、libFuzzer 的 -minimize_crash=1)會反覆刪減 input 中的位元組,保留能觸發同一 crash 的最小子集。minimized input 通常比原始 input 短一到兩個數量級,讓開發者能直接看出觸發條件。

Fix 與 regression test:修復 crash 後,用 minimized input 作為 fixture 寫成 regression test。這個 test 確保同類 bug 不再出現,也讓未來的 refactor 不會重新打開已修復的邊界。regression test 歸入 CI pipeline 的 fast path,每次 push 都跑。

CI 整合

Fuzz 在 CI 的執行模式跟 unit test 不同。unit test 有明確的 pass/fail 結束條件,fuzz campaign 是開放式探索,執行時間越長覆蓋越廣。

CI 整合分兩種模式,對齊 6.1 CI pipeline 的分層策略:

Fast path regression(30 秒至 5 分鐘):用既有 corpus 跑 fuzz,確認已知邊界沒退化。這個模式的目標是 regression 檢查,每次 push 觸發。corpus 裡的種子已經覆蓋了過去發現的邊界,短時間跑完可以確保修復沒被破壞、新變更沒引入已知類型的 crash。

Scheduled exploration(小時級):定期(每日或每週)跑長時間 fuzz,讓 engine 有足夠時間做深層 mutation 與路徑探索。新發現的種子合併回 corpus,crash input 產生 issue 或 alert。這個模式的 coverage delta 是判讀 campaign 價值的主要指標。

CI 整合的關鍵是 corpus 持久化。corpus 必須跨 job 保存(cache、artifact storage 或版本控制),每次 job 從上一次的 corpus 繼續探索。若 corpus 每次從零開始,fuzz engine 會重複探索已知路徑,浪費運算資源。

Coverage 門檻與收斂判讀

Fuzz coverage 跟 unit test coverage 的意義不同。unit test coverage 衡量的是「多少行被執行過」,fuzz coverage 衡量的是「多少邊界被探索過」。同一個函式的 fuzz coverage 可以隨 corpus 擴充持續增長,因為 mutation 會觸發不同的分支組合。

判讀 fuzz campaign 是否仍有價值靠兩個指標:coverage delta trend(每小時新增多少 code path)與 corpus size growth(每小時新增多少有效種子)。兩者同時趨近零代表當前 target 的探索飽和。

飽和訊號指引兩個決策。第一,是否切換 target — 當前 target 的邊界已被充分探索,把 fuzz 資源移到另一個高風險 target 的邊際價值更高。第二,是否調整 mutation dictionary — 加入 domain-specific token(如 SQL keyword、JSON structure token、protocol magic bytes)可以讓 engine 更有效地觸達 domain-aware 的邊界。

案例對照

  • Google:OSS-Fuzz 對大量基礎元件(parser、codec、serializer)做持續 fuzz,corpus 跨版本累積,crash 自動提 issue 並追蹤修復。這個規模的 fuzz campaign 說明 corpus 持久化與自動化 crash 處理是可擴展的前提。
  • Stripe:API 與 serialization 邊界的 fuzz 需要 domain-specific dictionary(支付欄位、currency code、idempotency key 格式),通用 mutation 難以觸達業務語意上的邊界 crash。
  • GitHub:webhook payload 與 schema 邊界的 fuzz 適合用 schema-aware fuzzer,從 OpenAPI / JSON Schema 產生結構化 mutation,覆蓋嵌套物件與型別邊界。

判讀訊號

訊號判讀條件行動建議
fuzz corpus 從未更新、覆蓋率停滯campaign 已失去探索價值 — 檢查是否需要換 target 或調整 mutation strategy換 target 或加 mutation dictionary
crash 復現靠人工 minimizationminimization 應自動化 — 手動 minimization 耗時且不可重複啟用 fuzzer 內建 minimizer 或接 CI 自動化
fuzz 找到 bug 沒回灌成 regression test修復後邊界可能被再次打開 — regression fixture 應歸入 CI fast path把 minimized input 加入 CI regression 套件
input boundary 無 spec、fuzz 範圍模糊target 選擇需要對齊 — 先定義哪些函式直接處理外部輸入盤點外部輸入函式、建立 target 清單
production 出 crash 但 fuzz 沒抓到fuzz target 未覆蓋該輸入路徑 — 把 production crash input 加入 corpus補 target + 把 crash input 加入 seed
coverage delta 持續為零但仍在跑長時間 fuzz資源浪費 — 飽和後應切換 target 或調整 dictionary停止當前 campaign、轉移資源到新 target

交接路由