Go 測試模糊化

начиная с Go 1.18, Go поддерживает фаззинг в своей стандартной цепочке инструментов. OSS-Fuzz поддерживает нативные тесты Go fuzz.

Попробуйте учебник по тестированию нечеткости с Go.

Обзор

Фаззинг - это тип автоматизированного тестирования, который постоянно изменяет входные данные программы для поиска ошибок. Фаззинг Go использует руководство по охвату, чтобы разумно просматривать фаззинг-код, находить и сообщать об отказах пользователю. Поскольку он может достигать крайних случаев, которые люди часто упускают, фаззинг-тестирование может быть особенно ценным для поиска эксплойтов и уязвимостей безопасности.

Ниже приведен пример фаззинг-теста с выделением его основных компонентов.

Example code showing the overall fuzz test, with a fuzz target within
it. Before the fuzz target is a corpus addition with f.Add, and the parameters
of the fuzz target are highlighted as the fuzzing arguments. Example code showing the overall fuzz test, with a fuzz target within
it. Before the fuzz target is a corpus addition with f.Add, and the parameters
of the fuzz target are highlighted as the fuzzing arguments.

Написание тестов нечеткости

Требования

Ниже приведены правила, которым должны следовать фаззинг-тесты.

建議

以下建議能幫助你充分運用模糊測試。

執行模糊測試

有兩種執行模糊測試的方式:作為單元測試(預設的 go test)或搭配模糊測試(go test -fuzz=FuzzTestName)。

預設情況下,模糊測試的執行方式與單元測試非常類似。每個 原始樣本集 項目會針對模糊測試目標進行測試,並在結束前回報任何失敗。

若要啟用模糊測試,請搭配 -fuzz 旗標執行 go test,並提供與單一模糊測試相符的正規表示式。預設情況下,該軟體套件中所有其他測試都會在模糊測試開始前執行。這是為了確保模糊測試不會回報現有測試已能發現的問題。

請注意,由你決定要執行模糊測試多久。如果模糊測試沒有發現任何錯誤,則它極有可能無限期地執行。未來會支援使用 OSS-Fuzz 等工具持續執行這些模糊測試,請參閱 問題 #50192

注意:模糊測試應在支援涵蓋範圍工具的平台上執行(目前為 AMD64 和 ARM64),這樣在執行時,樣本集才能有意義地成長,而且在執行模糊測試時可以涵蓋更多程式碼。

命令列輸出

當 fuzzing 作業正在進行時,fuzzing 引擎 會產生新的輸入並針對所提供的 fuzz target 執行它們。預設情況下,它會持續執行直到找到會失敗的輸入,或直到使用者取消此程序(例如使用 Ctrl^C)。

輸出的外觀會類似於這樣

~ go test -fuzz FuzzFoo
fuzz: elapsed: 0s, gathering baseline coverage: 0/192 completed
fuzz: elapsed: 0s, gathering baseline coverage: 192/192 completed, now fuzzing with 8 workers
fuzz: elapsed: 3s, execs: 325017 (108336/sec), new interesting: 11 (total: 202)
fuzz: elapsed: 6s, execs: 680218 (118402/sec), new interesting: 12 (total: 203)
fuzz: elapsed: 9s, execs: 1039901 (119895/sec), new interesting: 19 (total: 210)
fuzz: elapsed: 12s, execs: 1386684 (115594/sec), new interesting: 21 (total: 212)
PASS
ok      foo 12.692s

第一行表示在開始執行 fuzzing 之前,已收集「基準覆蓋率」。

為了收集基準覆蓋率,fuzzing 引擎會執行種子語料庫產生的語料庫,以確保沒有發生錯誤,並了解現有語料庫已提供的程式碼覆蓋率。

以下行提供對主動 fuzzing 執行的詳細資訊

要讓輸入「有趣」,它必須擴展超出現有產生的語料庫所能達到的程式碼覆蓋率。通常,新的有趣輸入數量在開始時會快速增加,然後最終會減緩,當發現新的分支時會偶爾激增。

當語料庫中的輸入開始涵蓋更多程式碼行時,您應期待看到「new interesting」的數字隨著時間而減少,如果 fuzzing 引擎找到新的程式碼路徑,則會偶爾激增。

錯誤的輸入

在執行 fuzzing 時可能會因許多原因導致失敗

如果發生錯誤,fuzzing 引擎會嘗試將輸入最小化至最小的可能值且最易於人類判讀,這仍會產生錯誤。如需進行設定,請參閱自訂設定檔部分。

最小化完成後,錯誤訊息會被記錄下來,產出內容會以類似這樣的字串結尾

    Failing input written to testdata/fuzz/FuzzFoo/a878c3134fe0404d44eb1e662e5d8d4a24beb05c3d68354903670ff65513ff49
    To re-run:
    go test -run=FuzzFoo/a878c3134fe0404d44eb1e662e5d8d4a24beb05c3d68354903670ff65513ff49
FAIL
exit status 1
FAIL    foo 0.839s

模糊測試引擎將這個 失敗輸入 寫入模糊測試的種子語料庫,現在它會使用 go test 預設執行,當 bug 修復後,此輸入會用作回歸測試。

接下來步驟是診斷問題,修復 bug,透過重新執行 go test 驗證修復狀況,然後提交包含新測試資料檔案、並充當回歸測試的修補程式。

自訂設定

對大多數模糊測試的使用案例來說,預設的 go 指令設定應該都能順利運作。因此,通常在指令列上執行模糊測試的寫法如下

$ go test -fuzz={FuzzTestName}

不過,執行模糊測試時,go 指令確實提供了一些設定。這些設定在 cmd/go 套件文件 中有說明。

重點列出幾個

語料庫檔案格式

語料庫檔案是以特殊格式編碼。這是 種子語料庫產生的語料庫 共用的格式。

以下是語料庫檔案範例

go test fuzz v1
[]byte("hello\\xbd\\xb2=\\xbc ⌘")
int64(572293)

第一行用於告知模糊測試引擎檔案的編碼版本。雖然目前尚未計畫未來版本的編碼格式,但設計必須支援這種可能性。

其後每一行都是構成語料庫項目的值,如果需要,可以直接複製到 Go 程式碼中。

在上述範例中,我們有一個 []byte 後面接著一個 int64。這些類型必須完全與模糊測試參數相符,並按順序排列。針對這些類型的模糊目標會如下所示

f.Fuzz(func(*testing.T, []byte, int64) {})

指定您自己的種子語料庫值最簡單的方法是使用 (*testing.F).Add 方法。在上述範例中,會如下所示

f.Add([]byte("hello\\xbd\\xb2=\\xbc ⌘"), int64(572293))

不過,您可能有大型二進位檔案,您可能不希望將它們作為程式碼複製到測試中,而是希望將它們保留為測試資料/模糊/{FuzzTestName} 目錄中的個別種子語料庫項目。可以使用 golang.org/x/tools/cmd/file2fuzz 上的 file2fuzz 工具將這些二進位檔案轉換為以 []byte 編碼的語料庫檔案。

要使用此工具

$ go install golang.org/x/tools/cmd/file2fuzz@latest
$ file2fuzz -h

資源

詞彙表

語料庫項目:模糊測試時可以使用語料庫中的輸入。這可以是特別格式化的檔案,或是呼叫 (*testing.F).Add

覆蓋率指導:模糊測試方法,使用程式碼覆蓋率的擴充,來判定哪些語料庫項目值得保留供未來使用。

失敗輸入:失敗輸入是執行於 模糊測試目標 時,會造成錯誤或錯誤的語料庫項目。

模糊測試目標:模糊測試中執行的測試函式的功能,以輸入語料庫和產生的值。透過將函式傳遞給 (*testing.F).Fuzz 來提供給模糊測試。

模糊測試:測試檔案中 func FuzzXxx(*testing.F) 格式函式,可用于模糊測試。

模糊測試:一種持續處理輸入至程式,找出問題,例如錯誤或容易受到程式碼影響的 漏洞,的自動化測試類型。

模糊測試參數:傳遞給模糊測試目標的類型,並由 轉換工具 轉換。

模糊測試引擎:一種管理模糊測試的工具,包括維護語料庫、呼叫轉換工具、辨識新覆蓋率及報告失敗。

產生的語料庫:一種由模糊測試引擎,在模糊測試時維護,來追蹤進度的語料庫。儲存在 $GOCACHE/fuzz 中。這些項目僅在模糊測試時使用。

轉換工具:模糊測試時使用的工具,在傳遞給模糊測試目標之前,隨機修改語料庫項目。

套件:同一目錄中同時編譯的原始檔案集合。請參閱 Go 語言規格中的 套件區段

種子語料庫:使用者提供的語料庫,可供模糊測試用於導引模糊引擎。它由模糊測試中 f.Add 呼叫提供的語料庫項目及套件中 testdata/fuzz/{FuzzTestName} 目錄中的檔案所組成。無論是否執行模糊測試,這些項目均會預設執行 go test

測試檔案:xxx_test.go 格式的檔案,可能包含測試、基準、範例和模糊測試。

漏洞:程式碼中的安全性弱點,攻擊者可以利用它。

回饋

如果您遇到任何問題或對功能有任何構想,請 提出問題

針對此項功能的討論及一般回饋,您也可以參與 Gophers Slack 上的 #fuzzing 頻道