登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  Golang >  Go问答

Go fuzz 语料未复现的目录与入口核对

来源:17golang原创

时间:2026-10-28 06:23:50 388浏览 收藏

Go fuzz 语料“没有复现”,通常不是语料内容突然失效,而是目录或入口没有和 fuzz 测试建立匹配关系。文件型种子必须放在执行该 fuzz 测试的 Go 包目录/testdata/fuzz/Fuzz测试名下,例如入口是 FuzzReverse,目录就必须是 testdata/fuzz/FuzzReverse。如果只放在 testdata/fuzz、目录名大小写不一致,或测试函数没有调用 f.Fuzz,Go 就不会按预期读取它。

排查时先确认入口,再核对目录,最后用默认回归命令复现。f.Add 提供的种子和文件目录中的种子都会被 fuzz 测试使用;只有真正启用 -fuzz 时,测试才会在这些种子基础上继续生成输入。

先确认 FuzzXxx 是有效入口

入口文件必须是当前包中的 *_test.go,函数名以 Fuzz 开头,参数为 *testing.F。准备种子后要调用 f.Fuzz 提供目标函数,不能只声明一个普通的 TestXxx 就期待它读取 fuzz 语料。

package parser

import "testing"

func FuzzParse(f *testing.F) {
	// 代码种子用于快速覆盖最小的正常输入。
	f.Add([]byte("name=go"))

	// 只有调用 Fuzz,testing 才知道真正要执行的目标函数。
	f.Fuzz(func(t *testing.T, data []byte) {
		// 解析失败属于当前输入的结果,按业务断言决定是否失败。
		_, _ = Parse(data)
	})
}

这里的有效入口是 FuzzParse,因此文件型语料的目录名也必须使用完整的 FuzzParse,而不是包名、源文件名或自定义简称。

把文件语料放到精确目录

假设包目录是 parser,正确的布局如下。testdata 是 Go 测试约定的测试数据目录,fuzz 下一层再按 fuzz 测试名分目录:

parser/
├── parser.go
├── parser_test.go              # 包含 FuzzParse
└── testdata/
    └── fuzz/
        └── FuzzParse/          # 必须与函数名完全匹配
            ├── seed-name
            └── seed-empty

常见错误有三个:把目录放到了 module 根目录而不是目标包目录;把 FuzzParse 写成 fuzzparse;只创建了 testdata/fuzz,没有继续创建测试名目录。它们看起来都像“语料文件存在”,但不会形成当前 fuzz 入口可读取的种子集合。

Go fuzz 测试入口与 testdata fuzz 测试名目录的静态关系说明图
图1:结构说明图,展示 Go fuzz 入口、目标包与 testdata/fuzz/FuzzParse 目录的匹配关系;这是原创说明图,不是运行截图。

区分 f.Add 和文件语料两条入口

f.Add 适合放少量、可读的基线输入;文件语料适合保存较长或不便直接写进 Go 源码的输入。两者不是互斥配置,默认执行 go test 时都会作为种子运行。

func FuzzDecode(f *testing.F) {
	// 代码种子保持短小,便于阅读和快速启动。
	f.Add([]byte("{}"))

	// 文件种子放在 testdata/fuzz/FuzzDecode,适合保存回归样本。
	f.Fuzz(func(t *testing.T, input []byte) {
		if err := Decode(input); err != nil {
			// 只有不允许的错误才应在这里失败,避免把输入边界误判为目录问题。
			return
		}
	})
}

文件型语料需要遵守 Go fuzz 的语料编码格式,第一行是版本标记,后续行按目标参数类型保存值。只把普通文本随手放进目录,仍可能导致读取失败;目录正确只说明“能找到位置”,不代表内容已经是有效语料。

Go fuzz 中 f.Add 与文件语料共同进入默认回归和随机探索的关系说明图
图2:关系说明图,展示 f.Add 与文件目录两种种子来源如何汇入默认回归和 -fuzz 探索;这是原创说明图,不是运行截图。

用三条命令判断到底有没有读到

先不要直接长时间随机 fuzz。按下面顺序可以把“目录没读到”和“语料读到了但断言没失败”区分开:

# 先运行该包的所有种子,确认文件语料能进入回归路径。
go test ./path/to/parser

# 已知失败样本的文件名或哈希时,只重跑这一条 fuzz 子测试。
go test -run='FuzzParse/样本哈希' ./path/to/parser

# 目录与入口确认后,再限制探索时间,观察是否产生新的失败输入。
go test -fuzz='FuzzParse' -fuzztime=30s ./path/to/parser

第一条命令不需要 -fuzz,也会运行 f.Add 和 testdata/fuzz/FuzzParse 中的种子。第二条命令要求使用输出中的准确子测试名;第三条命令是在已有种子上继续随机变异,不是用来替代目录核对。

排查清单与常见误区

现象先检查判断
文件存在但默认测试没有子测试包目录、大小写、Fuzz 测试名目录没有匹配当前入口
目录匹配但仍报语料解析错误第一行版本和参数类型位置正确,内容格式不正确
只在 -fuzz 时出现问题fuzz 入口和目标函数断言随机输入触发了新路径,不一定是目录问题

还要注意,fuzz 测试函数名是大小写敏感的;如果一个包里有多个 fuzz 入口,每个入口都对应自己的 testdata/fuzz/FuzzXxx 目录。最终目标不是“看到文件”,而是能用默认 go test 重跑该种子,并在需要时用精确的 -run 子测试名定位它。

相关问题

为什么 go test -run 没有匹配到语料? 通常是把目录名当成了测试名,或没有使用输出中的完整 FuzzXxx/哈希 子测试名。

f.Add 和 testdata/fuzz 可以同时使用吗? 可以。它们都是种子来源,默认回归会运行两者,随机 fuzz 会在它们的基础上探索。

语料目录应该放在 module 根目录吗? 不一定。它必须放在包含该 fuzz 测试的 Go 包目录下;多包项目要按实际测试包分别放置。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>