Sitemap

Golang Fuzz Testing

8 min readJul 22, 2023

--

Photo by Sigmund on Unsplash

前言

Fuzz Testing(模糊測試)是一種軟體測試技術,將自動生成的無效資料或是亂數輸入程式,並監控發生的 exceptions,可能是 crash,assertion 錯誤或是 memory leak。

最近研究 golang 的 fuzz testing,這篇文章紀錄使用方式以及該注意的地方。

Fuzz Testing

開始 Fuzz Testing 前,需要先注意電腦環境支援

接下來介紹 go test 支援的 fuzz 參數

  • -fuzz
    fuzz target 正則表達式,程式只會執行符合正則表達式的一個 Fuzz 函數,有多個會發生錯誤
  • -fuzztime
    fuzzing 執行時間,如果設為 5s 表示 5 秒後測試就會結束,預設是會一直執行直到程式 exception 發生
  • -fuzzminimizetime
    Run enough iterations of the fuzz target during each minimization
    attempt to take t, as specified as a time.Duration(-fuzzminimizetime 30s)
    The special syntax Nx means to run the fuzz target N times(-fuzzminimizetime 100x)
    預設是 60s
  • -parallel
    限制 fuzz process 數量,預設是 $GOMAXPROCS
  • -run
    指定執行某個 Fuzz 或測試,如果檔案裡有其他測試的話,預設是會等測試完再 Fuzz,這邊可以透過 run 指定

開始寫 fuzz testing

基礎環境跟 command line option 了解,來開始寫 fuzz testing,文章會用以前實作的開源專案 didyoumean 為例,該套件的功能主要是從 string list 裡找到其中最接近的文字。

寫 fuzz testing 其實跟一般的 testing 很像,需要注意用 *testing.F。

FuzzEditDistance 範例

type EditDistanceTest struct {
A string
B string
Distance int
}

var (
editDistanceTests = []EditDistanceTest{
{
A: "kitten",
B: "sitting",
Distance: 3,
}, {
A: "Saturday",
B: "Sunday",
Distance: 3,
}, {
A: "台灣好棒棒",
B: "台大",
Distance: 1,
},
}
)

// FuzzFindEditDistance
func FuzzFindEditDistance(f *testing.F) {
for _, test := range editDistanceTests {
f.Add(test.A, test.B)
}
f.Fuzz(func(t *testing.T, a string, b string) {
d := findEditDistance(a, b)
})
}

在 FuzzFindEditDistance 函數裡,這邊先用了 f.Add(test.A, test.B) 加入 seed corpus,接著在呼叫 f.Fuzz func(t *testing.T, a string, b string) {} ,這裡注意 Fuzz 這個函數代的參數要跟前面加入的 seed corpus 對應.而 seed corpus 除了會被用來 Fuzz,也會用來產生 generated corpus 並帶入測試。

fuzz testing 執行結果

$ go test -fuzz=Fuzz -fuzztime=10s -test.v ./...
=== RUN TestFindEditDistance
--- PASS: TestFindEditDistance (0.00s)
=== RUN TestFirstMatch
--- PASS: TestFirstMatch (0.00s)
=== RUN TestMatch
--- PASS: TestMatch (0.00s)
=== RUN FuzzFindEditDistance
fuzz: elapsed: 0s, gathering baseline coverage: 0/50 completed
fuzz: elapsed: 0s, gathering baseline coverage: 50/50 completed, now fuzzing with 8 workers
fuzz: elapsed: 3s, execs: 133773 (44576/sec), new interesting: 6 (total: 56)
fuzz: elapsed: 6s, execs: 133773 (0/sec), new interesting: 6 (total: 56)
fuzz: elapsed: 9s, execs: 133773 (0/sec), new interesting: 6 (total: 56)
fuzz: elapsed: 11s, execs: 265630 (65062/sec), new interesting: 7 (total: 57)
--- PASS: FuzzFindEditDistance (11.03s)
=== NAME
PASS
ok github.com/sc0vu/didyoumean 11.156s

一開始 fuzz 會先使用 seed / generated corpus 確保程式不會出錯並測試出 baseline coverage。
其他的 log

  • elapsed 經過多久時間
  • execs 執行 fuzz target 多少次
  • new interesting 執行 fuzz 食被加到 generated corpus 的總數,要增加 code coverage 的 corpus 才會被加入

沒有用 -run 限制只執行哪個 Test,fuzz testing 會在其他測試結束後開始。

corpus 目前支援這幾種型態:

string, []byte
int, int8, int16, int32/rune, int64
uint, uint8/byte, uint16, uint32, uint64
float32, float64
bool

結論

Fuzz testing 會自動產生異常的測資,這讓工程師可以再測試時就可以發現 exception,對於確保程式正常運作蠻有幫助。

Golang testing 目前也支援 fuzz testing,推薦各位同學使用。

參考

--

--

No responses yet