模糊化测试的实例指南

89 阅读2分钟

我们很高兴地宣布,原生模糊测试已经准备好在其开发分支dev.fuzz中进行测试了!

Fuzzing是一种自动化测试,它不断地操纵程序的输入,以发现诸如恐慌或错误等问题。这些半随机的数据突变可以发现现有单元测试可能遗漏的新的代码覆盖范围,并发现否则会被忽略的边缘案例错误。由于模糊测试可以接触到这些边缘案例,所以模糊测试对于发现安全漏洞和漏洞特别有价值。

开始使用

要开始工作,你可以运行以下程序:

$ go get golang.org/dl/gotip
$ gotip download dev.fuzz

这是从dev.fuzz开发分支构建Go工具链,一旦将来代码被合并到主分支,就不需要了。运行这个后,gotip 可以作为go 命令的落地替换。你现在可以运行如下命令:

$ gotip test -fuzz=FuzzFoo

dev.fuzz分支中会有持续的开发和错误修正,所以你应该定期运行gotip download dev.fuzz ,以使用最新的代码。

为了与Go的发布版本兼容,在提交包含fuzz目标的源文件到你的版本库时,请使用gofuzzbeta build标签。这个标签在构建时默认在dev.fuzz分支中启用。如果你对如何使用build 标签有疑问,请参考go 命令的文档

// +build gofuzzbeta

编写一个模糊目标

一个模糊目标必须作为一个函数出现在*_test.go文件中,其形式为FuzzXxx 。这个函数必须被传递一个 *testing.F 参数,就像一个*testing.T参数被传递给一个TestXxx 函数。

下面是一个测试net/url包行为的fuzz目标的例子:

// +build gofuzzbeta

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)
        }
    })
}

你可以通过 go doc 阅读更多关于模糊测试 API 的信息:

gotip doc testing
gotip doc testing.F
gotip doc testing.F.Add
gotip doc testing.F.Fuzz

期待

这是一个开发分支的测试版,所以你应该期待一些错误和不完整的功能集。请查看问题追踪器中标有 "fuzz "的问题,以了解现有错误和缺失功能的最新情况。

请注意,模糊处理会消耗大量内存,在运行时可能会影响你的机器性能。go test -fuzz ,默认在$GOMAXPROCS 进程中并行运行模糊处理。你可以通过使用go test 明确设置-parallel 标志来降低模糊处理时使用的进程数。如果你想了解更多信息,可以通过运行gotip help testflag 阅读go test 命令的文档。

还要注意的是,模糊引擎在运行时,会将扩大测试范围的数值写入$GOCACHE/fuzz 内的模糊缓存目录。目前对写入模糊缓存的文件数量或总字节数没有限制,所以它可能会占用大量的存储空间(即几个GB)。你可以通过运行gotip clean -fuzzcache 来清除 fuzz 缓存。

下一步是什么?

这个功能在即将发布的Go版本(1.17)中不会出现,但有计划在未来的Go版本中登陆。我们希望这个工作原型可以让Go开发者开始编写模糊目标,并提供关于设计的有用反馈,为合并到主版本做准备。