golang随机化测试系统插件库go-fuzz的使用
Golang随机化测试系统插件库go-fuzz的使用
go-fuzz是一个用于Go包测试的覆盖率引导模糊测试解决方案。模糊测试主要适用于解析复杂输入(文本和二进制)的包,特别适用于强化处理潜在恶意用户输入的系统(例如网络接收的任何输入)。
使用说明
首先,你需要编写一个测试函数,形式如下:
func Fuzz(data []byte) int
data
是由go-fuzz生成的随机输入,注意在大多数情况下它是无效的。函数必须返回:
- 1:如果模糊测试器应在后续模糊测试中增加给定输入的优先级
- -1:如果即使输入提供了新的覆盖率,也不应将其添加到语料库中
- 0:其他情况
示例代码
下面是一个简单的Fuzz函数示例,用于测试image/png包:
package png
import (
"bytes"
"image/png"
)
func Fuzz(data []byte) int {
png.Decode(bytes.NewReader(data))
return 0
}
一个更有用的Fuzz函数可能如下:
func Fuzz(data []byte) int {
img, err := png.Decode(bytes.NewReader(data))
if err != nil {
if img != nil {
panic("img != nil on error")
}
return 0
}
var w bytes.Buffer
err = png.Encode(&w, img)
if err != nil {
panic(err)
}
return 1
}
安装和使用
- 首先安装go-fuzz工具:
go install github.com/dvyukov/go-fuzz/go-fuzz@latest github.com/dvyukov/go-fuzz/go-fuzz-build@latest
- 下载测试语料库并构建测试程序:
git clone https://github.com/dvyukov/go-fuzz-corpus.git
cd go-fuzz-corpus
cd png
go-fuzz-build
这将生成png-fuzz.zip存档文件。
- 运行模糊测试:
go-fuzz
模块支持
go-fuzz初步支持Go模块。go-fuzz尊重标准的GO111MODULE
环境变量,可以设置为on
、off
或auto
。
libFuzzer支持
go-fuzz-build还可以生成与libFuzzer一起使用的存档文件(需要Linux)。
示例用法:
cd $GOPATH/src/github.com/dvyukov/go-fuzz-corpus/fmt
go-fuzz-build -libfuzzer # 生成fmt.a
clang -fsanitize=fuzzer fmt.a -o fmt.libfuzzer
./fmt.libfuzzer
持续模糊测试
目前有2个服务提供基于go-fuzz的持续模糊测试:
注意事项
- go-fuzz-build使用
gofuzz
构建标签构建程序,这允许将Fuzz函数实现直接放入测试包中,但使用// +build gofuzz
指令从正常构建中排除它。 - 如果你的输入包含校验和,可以在Fuzz函数中追加/更新校验和。
- go-fuzz可以利用多台机器。为此,单独启动协调器进程:
go-fuzz -workdir=examples/png -coordinator=127.0.0.1:8745
然后运行一个或多个工作进程:
go-fuzz -bin=./png-fuzz.zip -worker=127.0.0.1:8745 -procs=10
更多关于golang随机化测试系统插件库go-fuzz的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html
更多关于golang随机化测试系统插件库go-fuzz的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
Go-Fuzz 使用指南
Go-Fuzz 是一个强大的随机化测试工具,专门用于 Go 语言的模糊测试(fuzz testing)。它通过生成随机输入数据来测试程序的健壮性,特别适合发现边界条件和异常处理问题。
安装 Go-Fuzz
go get -u github.com/dvyukov/go-fuzz/go-fuzz
go get -u github.com/dvyukov/go-fuzz/go-fuzz-build
基本使用
1. 编写 Fuzz 测试函数
创建一个测试文件,如 fuzz_test.go
:
package mypackage
import (
"bytes"
"encoding/binary"
)
// 这是我们要测试的函数
func ParseData(data []byte) (int32, error) {
if len(data) < 4 {
return 0, errors.New("data too short")
}
var value int32
err := binary.Read(bytes.NewReader(data), binary.LittleEndian, &value)
return value, err
}
// Fuzz 测试函数
func Fuzz(data []byte) int {
_, err := ParseData(data)
if err != nil {
return 0 // 输入导致错误,但这不是我们关心的错误
}
return 1 // 输入被成功解析
}
2. 构建 Fuzz 测试二进制文件
go-fuzz-build -o=fuzz.zip github.com/yourusername/yourpackage
3. 运行 Fuzz 测试
go-fuzz -bin=fuzz.zip -workdir=workdir
高级功能
语料库管理
Go-Fuzz 会保存导致崩溃的输入到 workdir/crashers
目录,你可以使用这些输入来重现问题。
自定义种子语料库
创建一个 corpus
目录,放入有效的输入样本:
mkdir -p corpus
echo -n "test" > corpus/seed1
echo -n "example" > corpus/seed2
然后运行:
go-fuzz -bin=fuzz.zip -workdir=workdir -corpus=corpus
持续运行
Go-Fuzz 会持续运行直到发现崩溃或你手动停止它。发现崩溃后,它会保存导致崩溃的输入。
实际示例:测试 JSON 解析器
package jsonparser
import "encoding/json"
func Fuzz(data []byte) int {
var v interface{}
err := json.Unmarshal(data, &v)
if err != nil {
return 0
}
return 1
}
构建并运行:
go-fuzz-build -o=json-fuzz.zip github.com/yourusername/jsonparser
go-fuzz -bin=json-fuzz.zip -workdir=json-workdir
最佳实践
- 从简单种子开始:提供一些有效的输入样本作为起点
- 关注错误处理:确保测试函数正确处理错误情况
- 并行测试:可以在多台机器上运行相同的测试
- 定期更新:随着代码变化,持续运行模糊测试
- 结合单元测试:将发现的崩溃案例转化为单元测试
性能调优
# 限制内存使用
go-fuzz -bin=fuzz.zip -workdir=workdir -mem=100
# 设置超时
go-fuzz -bin=fuzz.zip -workdir=workdir -timeout=1
# 设置并发数
go-fuzz -bin=fuzz.zip -workdir=workdir -procs=8
Go-Fuzz 是一个强大的工具,可以帮助你发现代码中难以通过常规测试发现的边缘情况问题。将它集成到你的开发流程中,可以显著提高代码质量。