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
}

安装和使用

  1. 首先安装go-fuzz工具:
go install github.com/dvyukov/go-fuzz/go-fuzz@latest github.com/dvyukov/go-fuzz/go-fuzz-build@latest
  1. 下载测试语料库并构建测试程序:
git clone https://github.com/dvyukov/go-fuzz-corpus.git
cd go-fuzz-corpus
cd png
go-fuzz-build

这将生成png-fuzz.zip存档文件。

  1. 运行模糊测试:
go-fuzz

模块支持

go-fuzz初步支持Go模块。go-fuzz尊重标准的GO111MODULE环境变量,可以设置为onoffauto

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的持续模糊测试:

  1. gitlab.com
  2. fuzzbuzz.io

注意事项

  • 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

1 回复

更多关于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

最佳实践

  1. 从简单种子开始:提供一些有效的输入样本作为起点
  2. 关注错误处理:确保测试函数正确处理错误情况
  3. 并行测试:可以在多台机器上运行相同的测试
  4. 定期更新:随着代码变化,持续运行模糊测试
  5. 结合单元测试:将发现的崩溃案例转化为单元测试

性能调优

# 限制内存使用
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 是一个强大的工具,可以帮助你发现代码中难以通过常规测试发现的边缘情况问题。将它集成到你的开发流程中,可以显著提高代码质量。

回到顶部