乐趣区

关于golang:官方教程Go-fuzzing模糊测试

前言

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

  • Fuzzing 教程:https://go.dev/doc/tutorial/fuzz
  • Fuzzing 提案:https://github.com/golang/go/…
  • Fuzzing 介绍:https://go.dev/doc/fuzz/
退出移动版