go1.18新体验-模糊测试(fuzzing)

736 阅读3分钟

「这是我参与2022首次更文挑战的第7天,活动详情查看:2022首次更文挑战」。

go1.18beta1支持了泛型(generic)、模糊测试(fuzzing)、工作空间模式(go workspace mode)

对模糊测试比较感兴趣,稍微了解一下,做个小demo。

安装go1.18beta2

复制以下指令

go install golang.org/dl/go1.18beta2@latest
go1.18beta2 download

编译运行时使用go1.18beta2命令即可

go1.18beta2 run xxx.go

模糊测试

go1.18版本将支持原生的模糊测试,现在推出的go1.18beta2是测试版本,抢先体验。

模糊测试(fuzz testing, fuzzing)是一种软件测试技术。 其核心思想是將自动或半自动生成的随机数据输入到一个程序中,并监视程序异常,如崩溃,断言(assertion)失败,以发现可能的程序错误,比如内存泄漏。 ———维基百科

随机数据测试可以测试出单元测试未覆盖到的代码。模糊测试可以检测到一些边界情况,对于寻找漏洞和脆弱代码有一定价值。

实践

*_test 文件里起名为FuzzXxx函数。 该函数必须传入*testing.F 参数。

  • fuzzing可能会消耗大量内存,并可能在运行时影响到机器的性能。
  • go test -fuzz默认使用$GOMAXPROCS个进程并行运行fuzzing
  • fuzzing引擎在运行时将扩展的测试覆盖值写入$GOCACHE/fuzz 目录中,该文件作为缓存目录,目前没有限制可以写入模糊缓存的文件的数量或总字节数,所以它可能会占用大量的存储(通常是几个GB)

例子

下面这个例子是使用模糊测试,测试net/url包的行为。

//go:build go1.18
// +build go1.18

package fuzz

import (
    "net/url"
    "reflect"
    "testing"
)

func FuzzParseQuery(f *testing.F) {
    f.Add("x=1&y=2")
    f.Fuzz(func(t *testing.T, queryStr string) {
        query, err := url.ParseQuery(queryStr)
        if err != nil {
            t.Skip()
        }
        queryStr2 := query.Encode()
        query2, err := url.ParseQuery(queryStr2)
        if err != nil {
            t.Fatalf("ParseQuery failed to decode a valid encoded query %s: %v", queryStr2, err)
        }
        if !reflect.DeepEqual(query, query2) {
            t.Errorf("ParseQuery gave different query after being encoded\nbefore: %v\nafter: %v", query, query2)
        }
    })
}
  • f.Add增加默认测试输入
  • f.Fuzz函数的函数参数是模糊测试的目标
  • 被测函数必须传入*T参数以及一个或多个随机输入,例子程序中的随机输入参数是queryStr
  • fuzz将使用重复对默认输入(f.Add添加默认seed)进行随机更改而生成的参数来调用
  • fuzzing引擎将导致失败的输入写入包目录中testdata/fuzz/<Name>目录中的一个文件,如下图

image.png

运行

go1.18beta2 test -fuzz=Fuzz .\fuzzing_test.go

结果

fuzz: elapsed: 0s, gathering baseline coverage: 0/1 completed
fuzz: elapsed: 0s, gathering baseline coverage: 1/1 completed, now fuzzing with 16 workers
fuzz: elapsed: 3s, execs: 737527 (245821/sec), new interesting: 156 (total: 156)
fuzz: elapsed: 6s, execs: 1659546 (307344/sec), new interesting: 178 (total: 178)
..................................................................................

总结

模糊测试可以检测到单元测试无法覆盖到的边界情况,Fuzz.Add(inputCase)函数中添加默认情况(Seed input),模糊测试时会更改默认输入数据,良好的Seed可以帮助模糊测试更好的检测到bug。模糊测试会使用到测试机器的内存和硬盘,会对机器性能有一定影响,使用时需要注意这一点。

参考翻译

Tutorial: Getting started with fuzzing - The Go Programming Language

Fuzzing is Beta Ready - The Go Programming Language

testing package - testing - pkg.go.dev