官方教程:Go fuzzing模糊測試

coding進階發表於2022-02-27

前言

Go 1.18在go工具鏈裡引入了fuzzing模糊測試,可以幫助我們發現Go程式碼裡的漏洞或者可能導致程式崩潰的輸入。Go官方團隊也在官網釋出了fuzzing入門教程,幫助大家快速上手。


本人對Go官方教程在翻譯的基礎上做了一些表述上的優化,以饗讀者。

注意:fuzzing模糊測試和Go已有的單元測試以及效能測試框架是互為補充的,並不是替代關係。

教程內容

這篇教程會介紹Go fuzzing的入門基礎知識。fuzzing可以構造隨機資料來找出程式碼裡的漏洞或者可能導致程式崩潰的輸入。通過fuzzing可以找出的漏洞包括SQL隱碼攻擊、緩衝區溢位、拒絕服務(Denial of Service)攻擊和XSS(cross-site scripting)攻擊等。

在這個教程裡,你會給一個函式寫一段fuzz test(模糊測試)程式,然後執行go命令來發現程式碼裡的問題,最後通過除錯來修復問題。

本文裡涉及的專業術語,可以參考 Go Fuzzing glossary

接下來會按照如下章節介紹:

  1. 為你的程式碼建立一個目錄
  2. 實現一個函式
  3. 增加單元測試
  4. 增加模糊測試
  5. 修復2個bug
  6. 總結

準備工作

  • 安裝Go 1.18 Beta 1或者更新的版本。安裝指引可以參考下面的介紹
  • 有一個程式碼編輯工具。任何文字編輯器都可以。
  • 有一個命令列終端。Go可以執行在Linux,Mac上的任何命令列終端,也可以執行在Windows的PowerShell或者cmd之上。
  • 有一個支援fuzzing的環境。目前Go fuzzing只支援AMD64和ARM64架構。

安裝和使用beta版本

這個教程需要使用Go 1.18 Beta 1或以上版本的泛型功能。使用如下步驟,安裝beta版本

  1. 使用下面的命令安裝beta版本

    $ go install golang.org/dl/go1.18beta1@latest
  2. 執行如下命令來下載更新

    $ go1.18beta1 download

    注意:如果在MAC或者Linux上執行go1.18beta1提示command not found,需要設定bash或者zsh對應的profile環境變數檔案。bash設定在~/.bash_profile檔案裡,內容為:

    export GOROOT=/usr/local/opt/go/libexec
    export GOPATH=$HOME/go
    export PATH=$PATH:$GOROOT/bin:$GOPATH/bin

    GOROOTGOPATH的值可以通過go env命令檢視,設定完後執行source ~/.bash_profile讓設定生效,再執行go1.18beta1就不報錯了。

  3. 使用beta版本的go命令,不要去使用release版本的go命令

    你可以通過直接使用go1.18beta1命令或者給go1.18beta1起一個簡單的別名

    • 直接使用go1.18beta1命令

      $ go1.18beta1 version
    • go1.18beta1命令起一個別名

      $ alias go=go1.18beta1
      $ go version

    下面的教程都假設你已經把go1.18beta1命令設定了別名go

為你的程式碼建立一個目錄

首先建立一個目錄用於存放你寫的程式碼。

  1. 開啟一個命令列終端,切換到你的home目錄

    • 在Linux或者Mac上執行如下命令(Linux或者Mac上只需要執行cd就可以進入到home目錄)

      cd
    • 在Windows上執行如下命令

      C:\> cd %HOMEPATH%
  2. 在命令列終端,建立一個名為fuzz的目錄,並進入該目錄

    $ mkdir fuzz
    $ cd fuzz
  3. 建立一個go module

    執行go mod init命令,來給你的專案設定module路徑

    $ go mod init example/fuzz

    注意:對於生產程式碼,你可以根據專案實際情況來指定module路徑,如果想了解更多,可以參考Go Module依賴管理

接下來,我們來使用map寫一些簡單的程式碼來做字串的反轉,然後使用fuzzing來做模糊測試。

實現一個函式

在這個章節,你需要實現一個函式來對字串做反轉。

編寫程式碼

  1. 開啟你的文字編輯器,在fuzz目錄下建立一個main.go原始檔。
  2. main.go裡編寫如下程式碼:

    // maing.go
    package main
    
    import "fmt"
    
    func Reverse(s string) string {
        b := []byte(s)
        for i, j := 0, len(b)-1; i < len(b)/2; i, j = i+1, j-1 {
            b[i], b[j] = b[j], b[i]
        }
        return string(b)
    }
    
    func main() {
        input := "The quick brown fox jumped over the lazy dog"
        rev := Reverse(input)
        doubleRev := Reverse(rev)
        fmt.Printf("original: %q\n", input)
        fmt.Printf("reversed: %q\n", rev)
        fmt.Printf("reversed again: %q\n", doubleRev)
    }

執行程式碼

main.go所在目錄執行命令go run .來執行程式碼,結果如下:

$ go run .
original: "The quick brown fox jumped over the lazy dog"
reversed: "god yzal eht revo depmuj xof nworb kciuq ehT"
reversed again: "The quick brown fox jumped over the lazy dog"

增加單元測試

在這個章節,你會給Reverse函式編寫單元測試程式碼。

編寫單元測試

  1. 在fuzz目錄下建立檔案reverse_test.go
  2. reverse_test.go裡編寫如下程式碼:

    package main
    
    import (
        "testing"
    )
    
    func TestReverse(t *testing.T) {
        testcases := []struct {
            in, want string
        }{
            {"Hello, world", "dlrow ,olleH"},
            {" ", " "},
            {"!12345", "54321!"},
        }
        for _, tc := range testcases {
            rev := Reverse(tc.in)
            if rev != tc.want {
                    t.Errorf("Reverse: %q, want %q", rev, tc.want)
            }
        }
    }

執行單元測試

使用go test命令來執行單元測試

$ go test
PASS
ok      example/fuzz  0.013s

接下來,我們給Reverse函式增加模糊測試(fuzz test)程式碼。

增加模糊測試

單元測試有侷限性,每個測試輸入必須由開發者指定加到單元測試的測試用例裡。

fuzzing的優點之一是可以基於開發者程式碼裡指定的測試輸入作為基礎資料,進一步自動生成新的隨機測試資料,用來發現指定測試輸入沒有覆蓋到的邊界情況。

在這個章節,我們會把單元測試轉換成模糊測試,這樣可以更輕鬆地生成更多的測試輸入。

注意:你可以把單元測試、效能測試和模糊測試放在同一個*_test.go檔案裡。

編寫模糊測試

在文字編輯器裡把reverse_test.go裡的單元測試程式碼TestReverse替換成如下的模糊測試程式碼FuzzReverse

func FuzzReverse(f *testing.F) {
    testcases := []string{"Hello, world", " ", "!12345"}
    for _, tc := range testcases {
        f.Add(tc)  // Use f.Add to provide a seed corpus
    }
    f.Fuzz(func(t *testing.T, orig string) {
        rev := Reverse(orig)
        doubleRev := Reverse(rev)
        if orig != doubleRev {
            t.Errorf("Before: %q, after: %q", orig, doubleRev)
        }
        if utf8.ValidString(orig) && !utf8.ValidString(rev) {
            t.Errorf("Reverse produced invalid UTF-8 string %q", rev)
        }
    })
}

Fuzzing也有一定的侷限性。

在單元測試裡,因為測試輸入是固定的,你可以知道呼叫Reverse函式後每個輸入字串得到的反轉字串應該是什麼,然後在單元測試的程式碼裡判斷Reverse的執行結果是否和預期相符。例如,對於測試用例Reverse("Hello, world"),單元測試預期的結果是 "dlrow ,olleH"

但是使用fuzzing時,我們沒辦法預期輸出結果是什麼,因為測試的輸入除了我們程式碼裡指定的用例之外,還有fuzzing隨機生成的。對於隨機生成的測試輸入,我們當然沒辦法提前知道輸出結果是什麼。

雖然如此,本文裡的Reverse函式有幾個特性我們還是可以在模糊測試裡做驗證。

  1. 對一個字串做2次反轉,得到的結果和源字串相同
  2. 反轉後的字串也仍然是一個有效的UTF-8編碼的字串

注意:fuzzing模糊測試和Go已有的單元測試以及效能測試框架是互為補充的,並不是替代關係。

比如我們實現的Reverse函式如果是一個錯誤的版本,直接return返回輸入的字串,是完全可以通過上面的模糊測試的,但是沒法通過我們前面編寫的單元測試。因此單元測試和模糊測試是互為補充的,不是替代關係。

Go模糊測試和單元測試在語法上有如下差異:

  • Go模糊測試函式以FuzzXxx開頭,單元測試函式以TestXxx開頭
  • Go模糊測試函式以 *testing.F作為入參,單元測試函式以*testing.T作為入參
  • Go模糊測試會呼叫f.Add函式和f.Fuzz函式。

    • f.Add函式把指定輸入作為模糊測試的種子語料庫(seed corpus),fuzzing基於種子語料庫生成隨機輸入。
    • f.Fuzz函式接收一個fuzz target函式作為入參。fuzz target函式有多個引數,第一個引數是*testing.T,其它引數是被模糊的型別(注意:被模糊的型別目前只支援部分內建型別, 列在 Go Fuzzing docs,未來會支援更多的內建型別)。

上面的FuzzReverse函式裡用到了utf8這個package,因此要在reverse_test.go開頭import這個package,參考如下程式碼:

package main

import (
    "testing"
    "unicode/utf8"
)

執行模糊測試

  1. 執行如下命令來執行模糊測試。

    這個方式只會使用種子語料庫,而不會生成隨機測試資料。通過這種方式可以用來驗證種子語料庫的測試資料是否可以測試通過。(fuzz test without fuzzing)

    $ go test
    PASS
    ok      example/fuzz  0.013s

    如果reverse_test.go檔案裡有其它單元測試函式或者模糊測試函式,但是隻想執行FuzzReverse模糊測試函式,我們可以執行go test -run=FuzzReverse命令。

    注意go test預設會執行所有以TestXxx開頭的單元測試函式和以FuzzXxx開頭的模糊測試函式,預設不執行以BenchmarkXxx開頭的效能測試函式,如果我們想執行 benchmark用例,則需要加上 -bench 引數。

  2. 如果要基於種子語料庫生成隨機測試資料用於模糊測試,需要給go test命令增加 -fuzz引數。(fuzz test with fuzzing)

    $ go test -fuzz=Fuzz
    fuzz: elapsed: 0s, gathering baseline coverage: 0/3 completed
    fuzz: elapsed: 0s, gathering baseline coverage: 3/3 completed, now fuzzing with 8 workers
    fuzz: minimizing 38-byte failing input file...
    --- FAIL: FuzzReverse (0.01s)
        --- FAIL: FuzzReverse (0.00s)
            reverse_test.go:20: Reverse produced invalid UTF-8 string "\x9c\xdd"
    
        Failing input written to testdata/fuzz/FuzzReverse/af69258a12129d6cbba438df5d5f25ba0ec050461c116f777e77ea7c9a0d217a
        To re-run:
        go test -run=FuzzReverse/af69258a12129d6cbba438df5d5f25ba0ec050461c116f777e77ea7c9a0d217a
    FAIL
    exit status 1
    FAIL    example/fuzz  0.030s

    上面的fuzzing測試結果是FAIL,引起FAIL的輸入資料被寫到了一個語料庫檔案裡。下次執行go test命令的時候,即使沒有-fuzz引數,這個語料庫檔案裡的測試資料也會被用到。

    可以用文字編輯器開啟testdata/fuzz/FuzzReverse目錄下的檔案,看看引起Fuzzing測試失敗的測試資料長什麼樣。下面是一個示例檔案,你那邊執行後得到的測試資料可能和這個不一樣,但檔案裡的內容格式會是一樣的。

    go test fuzz v1
    string("泃")

    語料庫檔案裡的第1行標識的是編碼版本(encoding version,說直白點,就是這個種子語料庫檔案裡內容格式的版本),雖然目前只有v1這1個版本,但是Fuzzing設計者考慮到未來可能引入新的編碼版本,於是加了編碼版本的概念。

    從第2行開始,每一行資料對應的是語料庫的每條測試資料(corpus entry)的其中一個引數,按照引數先後順序排列。

    f.Fuzz(func(t *testing.T, orig string) {
            rev := Reverse(orig)
            doubleRev := Reverse(rev)
            if orig != doubleRev {
                t.Errorf("Before: %q, after: %q", orig, doubleRev)
            }
            if utf8.ValidString(orig) && !utf8.ValidString(rev) {
                t.Errorf("Reverse produced invalid UTF-8 string %q %q", orig, rev)
            }
    })

    本文的FuzzReverse裡的fuzz target函式func(t *testing.T, orig string)只有orig這1個引數作為真正的測試輸入,也就是每條測試資料其實就1個輸入,因此在上面示例的testdata/fuzz/FuzzReverse目錄下的檔案裡只有string("泃")這一行。

    如果每條測試資料有N個引數,那fuzzing找出的導致fuzz test失敗的每條測試資料在testdata目錄下的檔案裡會有N行,第i行對應第i個引數。

  3. 再次執行go test命令,這次不帶-fuzz引數。

    我們會發現雖然沒有-fuzz引數,但是模糊測試的時候仍然用到了上面第2步找到的測試資料。

    $ go test
    --- FAIL: FuzzReverse (0.00s)
        --- FAIL: FuzzReverse/af69258a12129d6cbba438df5d5f25ba0ec050461c116f777e77ea7c9a0d217a (0.00s)
            reverse_test.go:20: Reverse produced invalid string
    FAIL
    exit status 1
    FAIL    example/fuzz  0.016s

    既然Go fuzzing測試沒通過,那就需要我們除錯程式碼來找出問題所在了。

修復2個bug

在這個章節,我們會除錯程式,修復Go fuzzing測出來的bug。

你可以自己花一些時間思考下,先嚐試自己解決問題。

定位問題

你可以使用不同的方法來除錯上面發現的bug。

如果你使用的是VS Code,那可以在VS Code裡設定你的Debug偵錯程式來加斷點進行除錯。

本文裡,我們會使用列印日誌的方式進行除錯。

執行模糊測試時的報錯資訊為:reverse_test.go:20: Reverse produced invalid UTF-8 string "\x9c\xdd"

基於這個報錯,我們來看看文件裡對於 utf8.ValidString的描述。

ValidString reports whether s consists entirely of valid UTF-8-encoded runes.

我們實現的Reverse函式是按照位元組(byte)為維度進行字串反轉,這就是問題所在。

比如中文裡的字元 其實是由3個位元組組成的,如果按照位元組反轉,反轉後得到的就是一個無效的字串了。

因此為了保證字串反轉後得到的仍然是一個有效的UTF-8編碼的字串,我們要按照rune進行字串反轉。

為了更好地方便大家理解中文裡的字元 按照rune為維度有多少個rune,以及按照byte反轉後得到的結果長什麼樣,我們對程式碼做一些修改。

編寫程式碼

按照如下方式修改FuzzReverse裡的程式碼。

f.Fuzz(func(t *testing.T, orig string) {
    rev := Reverse(orig)
    doubleRev := Reverse(rev)
    t.Logf("Number of runes: orig=%d, rev=%d, doubleRev=%d", utf8.RuneCountInString(orig), utf8.RuneCountInString(rev), utf8.RuneCountInString(doubleRev))
    if orig != doubleRev {
        t.Errorf("Before: %q, after: %q", orig, doubleRev)
    }
    if utf8.ValidString(orig) && !utf8.ValidString(rev) {
        t.Errorf("Reverse produced invalid UTF-8 string %q", rev)
    }
})

執行程式碼

$ go test
--- FAIL: FuzzReverse (0.00s)
    --- FAIL: FuzzReverse/28f36ef487f23e6c7a81ebdaa9feffe2f2b02b4cddaa6252e87f69863046a5e0 (0.00s)
        reverse_test.go:16: Number of runes: orig=1, rev=3, doubleRev=1
        reverse_test.go:21: Reverse produced invalid UTF-8 string "\x83\xb3\xe6"
FAIL
exit status 1
FAIL    example/fuzz    0.598s

我們的種子語料庫裡每個符號都是單個位元組。但是像 這樣的中文符號由多個位元組組成,如果以位元組為維度進行反轉,就會得到無效的結果。

注意:如果你對於Go如何處理字串感興趣,可以閱讀官方部落格裡的這篇文章 Strings, bytes, runes and characters in Go 來加深理解。

既然我們明確了問題,那我們就可以修復這個bug了。

修復問題

rune為維度進行字串反轉。

編寫程式碼

修改Reverse函式的實現如下:

func Reverse(s string) string {
    r := []rune(s)
    for i, j := 0, len(r)-1; i < len(r)/2; i, j = i+1, j-1 {
        r[i], r[j] = r[j], r[i]
    }
    return string(r)
}

執行程式碼

  1. 執行命令:go test

    $ go test
    PASS
    ok      example/fuzz  0.016s

    測試通過啦!(別高興太早,這個只是通過了種子語料庫和之前)

  2. 再次執行 go test -fuzz,看看我們是否會發現新的bug

    $ go test -fuzz=Fuzz
    fuzz: elapsed: 0s, gathering baseline coverage: 0/37 completed
    fuzz: minimizing 506-byte failing input file...
    fuzz: elapsed: 0s, gathering baseline coverage: 5/37 completed
    --- FAIL: FuzzReverse (0.02s)
        --- FAIL: FuzzReverse (0.00s)
            reverse_test.go:33: Before: "\x91", after: "�"
    
        Failing input written to testdata/fuzz/FuzzReverse/1ffc28f7538e29d79fce69fef20ce5ea72648529a9ca10bea392bcff28cd015c
        To re-run:
        go test -run=FuzzReverse/1ffc28f7538e29d79fce69fef20ce5ea72648529a9ca10bea392bcff28cd015c
    FAIL
    exit status 1
    FAIL    example/fuzz  0.032s

    通過上面的報錯,我們發現對一個字串做了2次反轉後得到的和原字串不一樣。

    這次測試輸入本身是非法的unicode,但是為什麼會2次反轉後得到的字串還不一樣呢?

    我們繼續除錯。

修復2次字串反轉的bug

定位問題

對於這個問題,加斷點除錯會很好定位。為了方便講解,本文使用加日誌的方式進行除錯。

我們可以仔細觀察原字串第一次反轉後得到的結果來定位問題。

編寫程式碼

  1. 修改 Reverse 函式。

    func Reverse(s string) string {
        fmt.Printf("input: %q\n", s)
        r := []rune(s)
        fmt.Printf("runes: %q\n", r)
        for i, j := 0, len(r)-1; i < len(r)/2; i, j = i+1, j-1 {
            r[i], r[j] = r[j], r[i]
        }
        return string(r)
    }

    這可以幫助我們理解把原字串轉成rune切片後發生了什麼。

執行程式碼

這一次,我們只執行讓fuzz test失敗的測試資料,使用 go test -run命令。

執行FuzzXxx/testdata目錄下指定的語料庫測試資料,可以給-run引數指定值 {FuzzTestName}/{filename} ,這可以讓我們聚焦在讓fuzz test失敗的測試資料上。

$ go test -run=FuzzReverse/28f36ef487f23e6c7a81ebdaa9feffe2f2b02b4cddaa6252e87f69863046a5e0
input: "\x91"
runes: ['�']
input: "�"
runes: ['�']
--- FAIL: FuzzReverse (0.00s)
    --- FAIL: FuzzReverse/28f36ef487f23e6c7a81ebdaa9feffe2f2b02b4cddaa6252e87f69863046a5e0 (0.00s)
        reverse_test.go:16: Number of runes: orig=1, rev=1, doubleRev=1
        reverse_test.go:18: Before: "\x91", after: "�"
FAIL
exit status 1
FAIL    example/fuzz    0.145s

首先我們要了解:在Go語言裡,字串是隻讀的位元組切片(In Go, a string is a read only slice of bytes),位元組切片裡的每個位元組不一定都是有效的UTF-8編碼的位元組,詳情可以參考a string is a read only slice of bytes

上面的例子裡,輸入的字串是隻有1個byte的位元組切片,這1個byte是\x91

當我們把這個輸入的字串轉成[]rune時,Go會把位元組切片編碼為UTF-8,於是就把\x91替換成了'�','�'飯莊後還是'�',一次就導致原字串\x91反轉後得到的字串是'�'了。

現在問題明確了,是因為輸入的資料是非法的unicode。那接下來我們就可以修正Reverse函式的實現了。

修復問題

修復方式為:在Reverse裡檢查輸入是否為合法的UTF-8編碼字串,如果非法,就返回eror。

編寫程式碼

  1. 修改Reverse 實現如下:

    func Reverse(s string) (string, error) {
        if !utf8.ValidString(s) {
            return s, errors.New("input is not valid UTF-8")
        }
        r := []rune(s)
        for i, j := 0, len(r)-1; i < len(r)/2; i, j = i+1, j-1 {
            r[i], r[j] = r[j], r[i]
        }
        return string(r), nil
    }
  2. 由於Reverse函式現在會返回error,因此要修改main.go裡的對應程式碼,修改如下:

    func main() {
        input := "The quick brown fox jumped over the lazy dog"
        rev, revErr := Reverse(input)
        doubleRev, doubleRevErr := Reverse(rev)
        fmt.Printf("original: %q\n", input)
        fmt.Printf("reversed: %q, err: %v\n", rev, revErr)
        fmt.Printf("reversed again: %q, err: %v\n", doubleRev, doubleRevErr)
    }

    因為main函式裡都是有效的UTF-8編碼字串,所以對Reverse的呼叫會返回一個值為nil的error。

  3. 由於Reverse函式用到了errorsutf8這2個package,因此在main.go的開頭要import這2個package。

    import (
        "errors"
        "fmt"
        "unicode/utf8"
    )
  4. 同樣,我們需要修改reverse_test.go檔案,對於非法的字串輸入,可以直接跳過測試。

    func FuzzReverse(f *testing.F) {
        testcases := []string {"Hello, world", " ", "!12345"}
        for _, tc := range testcases {
            f.Add(tc)  // Use f.Add to provide a seed corpus
        }
        f.Fuzz(func(t *testing.T, orig string) {
            rev, err1 := Reverse(orig)
            if err1 != nil {
                return
            }
            doubleRev, err2 := Reverse(rev)
            if err2 != nil {
                 return
            }
            if orig != doubleRev {
                t.Errorf("Before: %q, after: %q", orig, doubleRev)
            }
            if utf8.ValidString(orig) && !utf8.ValidString(rev) {
                t.Errorf("Reverse produced invalid UTF-8 string %q", rev)
            }
        })
    }

    除了使用return,你還可以呼叫 t.Skip() 來跳過當前的測試輸入,繼續下一輪測試輸入。

執行程式碼

  1. 執行測試程式碼

    $ go test
    PASS
    ok      example/fuzz  0.019s
  2. 執行模糊測試 go test -fuzz=Fuzz,執行幾秒後,使用 ctrl-C 結束測試。

    $ go test -fuzz=Fuzz
    fuzz: elapsed: 0s, gathering baseline coverage: 0/38 completed
    fuzz: elapsed: 0s, gathering baseline coverage: 38/38 completed, now fuzzing with 4 workers
    fuzz: elapsed: 3s, execs: 86342 (28778/sec), new interesting: 2 (total: 35)
    fuzz: elapsed: 6s, execs: 193490 (35714/sec), new interesting: 4 (total: 37)
    fuzz: elapsed: 9s, execs: 304390 (36961/sec), new interesting: 4 (total: 37)
    ...
    fuzz: elapsed: 3m45s, execs: 7246222 (32357/sec), new interesting: 8 (total: 41)
    ^Cfuzz: elapsed: 3m48s, execs: 7335316 (31648/sec), new interesting: 8 (total: 41)
    PASS
    ok      example/fuzz  228.000s

    fuzz test如果沒有遇到錯誤,預設會一直執行下去,需要使用 ctrl-C 結束測試。

    也可以傳遞-fuzztime引數來指定測試時間,這樣就不用 ctrl-C 了。

  3. 指定測試時間。 go test -fuzz=Fuzz -fuzztime 30s 如果沒有遇到錯誤會執行30s後自動結束。

    $ go test -fuzz=Fuzz -fuzztime 30s
    fuzz: elapsed: 0s, gathering baseline coverage: 0/5 completed
    fuzz: elapsed: 0s, gathering baseline coverage: 5/5 completed, now fuzzing with 4 workers
    fuzz: elapsed: 3s, execs: 80290 (26763/sec), new interesting: 12 (total: 12)
    fuzz: elapsed: 6s, execs: 210803 (43501/sec), new interesting: 14 (total: 14)
    fuzz: elapsed: 9s, execs: 292882 (27360/sec), new interesting: 14 (total: 14)
    fuzz: elapsed: 12s, execs: 371872 (26329/sec), new interesting: 14 (total: 14)
    fuzz: elapsed: 15s, execs: 517169 (48433/sec), new interesting: 15 (total: 15)
    fuzz: elapsed: 18s, execs: 663276 (48699/sec), new interesting: 15 (total: 15)
    fuzz: elapsed: 21s, execs: 771698 (36143/sec), new interesting: 15 (total: 15)
    fuzz: elapsed: 24s, execs: 924768 (50990/sec), new interesting: 16 (total: 16)
    fuzz: elapsed: 27s, execs: 1082025 (52427/sec), new interesting: 17 (total: 17)
    fuzz: elapsed: 30s, execs: 1172817 (30281/sec), new interesting: 17 (total: 17)
    fuzz: elapsed: 31s, execs: 1172817 (0/sec), new interesting: 17 (total: 17)
    PASS
    ok      example/fuzz  31.025s

    Fuzzing測試通過!

    除了-fuzz引數外,有幾個新的引數也被引入到了go test命令,具體可以參考 documentation

總結

目前你已經學會了Go fuzzing的使用方法。

接下來,你可以在自己寫過的程式碼裡,嘗試使用fuzzing來發現程式碼裡的bug。

如果你真的發現了bug,請考慮把案例提交到了trophy case

如果你發現了Go fuzzing的任何問題或者想提feature,可以在這裡反饋file an issue

檢視文件 go.dev/doc/fuzz瞭解更多Go Fuzzing的知識。

本文的完整程式碼參考Go Fuzzing示例程式碼

開源地址

文章和示例程式碼開源在GitHub: Go語言初級、中級和高階教程

公眾號:coding進階。關注公眾號可以獲取最新Go面試題和技術棧。

個人網站:Jincheng's Blog

知乎:無忌

References

相關文章