Go fuzzing 怎样用语料库复现输入
Go 原生 fuzzing 的关键不是把每次随机输入都保存下来,而是把有价值的输入变成可重复运行的语料。固定样本可以通过 f.Add 注册,也可以放进 testdata/fuzz/;当 fuzzing 找到失败输入时,Go 会把它写回同一个语料目录。这样,之后直接运行 go test 就能重新执行这条回归样本,调试时还可以用 -run 精确定位它。
官方文档:https://go.dev/doc/security/fuzz/
testing 包文档:https://pkg.go.dev/testing#F.Add
想复现一次 fuzzing 失败,先看输出中的 FuzzTest 名称和哈希后缀;想让问题长期留在回归测试里,就保留生成的语料文件,并用不带-fuzz的go test重跑。
先把种子语料和失败语料分开看
Go 的 fuzz test 至少有两类输入来源。第一类是开发者主动提供的种子:可以在调用 f.Fuzz 之前用 f.Add 注册,也可以把文件放入 testdata/fuzz/FuzzXxx。第二类是 fuzzing 过程中发现的失败输入,测试引擎会把它保存为新的语料文件。
两者都会在默认的 go test 中执行。区别在于,种子是你用来引导初始覆盖范围的输入,失败语料则是已经暴露出问题、应该被长期保留的回归样本。不要把所有随机输入都手工复制进代码,真正有价值的是能覆盖边界或复现缺陷的少量样本。

用 F.Add 注册一组稳定的初始输入
F.Add 必须写在 F.Fuzz 之前,传入的参数类型要和 fuzz target 的参数一一对应。下面的例子用字符串覆盖空值、普通文本、包含分隔符的文本和多字节字符;这些输入不是为了模拟所有随机情况,而是为了让每次默认测试都有稳定的起点。
func FuzzNormalize(f *testing.F) {
// 种子覆盖空值、分隔符和多字节文本,帮助快速触达边界。
f.Add("")
f.Add(" go-fuzz ")
f.Add("a,b,c")
f.Add("中文输入")
f.Fuzz(func(t *testing.T, input string) {
// fuzz target 要保持快速、确定,并只使用当前输入。
got := normalize(input)
if strings.Contains(got, "\\n") {
// 失败会让当前输入进入可复现的失败语料路径。
t.Fatalf("结果仍含换行符: %q", got)
}
})
}
如果 target 接收的是 []byte、int 或多个参数,f.Add 也必须用完全相同的参数类型和顺序。类型不匹配时,问题应在编写测试时直接修正,不要试图用字符串化方式绕过 fuzz target 的签名。
找到失败输入后看哪个目录
启用 fuzzing 时,如果 target 因某组输入失败,测试输出会给出类似 testdata/fuzz/FuzzNormalize/ 的路径。文件第一行是 go test fuzz v1,后续每一行对应 fuzz target 的一个参数。它是 Go 的语料编码,不是普通日志,也不应该把注释写进文件。
例如一个只接收字符串的语料文件可能是:
go test fuzz v1
string("异常输入")
如果 target 接收 []byte 和 int64,文件中的两条值就必须按同样顺序出现。官方文档还说明,若项目目录不可写,失败输入可能暂时写入构建缓存中的 fuzz cache;在通常可写的项目中,优先以测试输出给出的项目内路径为准。

不带 -fuzz 也能把失败输入跑起来
开发者第一次看到失败输出时,容易以为必须重新运行一轮随机 fuzzing。其实,失败语料保存后,默认的 go test 会把它当作种子执行。这个特性让发现问题和回归验证分开:第一次用 -fuzz 找到输入,后续普通测试负责稳定地确认修复没有回退。
# 先只执行已登记的单元测试、种子语料和失败语料。 go test ./... # 需要继续探索时,才指定一个 fuzz test,并限制本次探索时间。 go test -fuzz=FuzzNormalize -fuzztime=30s
如果项目里有多个 fuzz test,可以先用普通的 go test 确认全部语料,再用 -fuzz=FuzzNormalize 选择目标。-fuzztime 只限制 fuzzing 探索阶段的时间,不改变已有语料的含义。
用 -run 精确复现一条语料
当一个 fuzz test 的语料很多,或只有某一条输入触发问题时,可以从失败输出复制完整的子测试标识。Go 官方示例使用 go test -run=FuzzFoo/9ddb952d9814 运行单条 seed corpus 输入。实际使用时,把 FuzzFoo 换成你的 fuzz test 名称,把后面的哈希换成输出中的实际标识。
# 只复现 FuzzNormalize 下的一条失败语料。 go test -run='^FuzzNormalize/9ddb952d9814$' # 只运行该 fuzz test 的全部种子,不开启随机探索。 go test -run='^FuzzNormalize$'
这里的 -run 是筛选已经存在的测试和子测试;它不会生成新的随机输入。若哈希或测试名称复制错误,命令可能匹配不到目标,先回到原始失败日志重新复制完整标识。
语料库维护时记住三个边界
| 问题 | 应该怎么做 | 原因 |
|---|---|---|
| 固定样本放哪里 | 少量可读样本用 f.Add,较大的或长期维护的样本放 testdata/fuzz/FuzzXxx | 两者都会进入默认种子集合,但文件更适合管理较大输入 |
| 失败输入要不要删除 | 修复问题前保留;修复后把它作为回归样本继续运行 | 它记录了曾经触发缺陷的真实边界 |
| 如何缩小调试范围 | 从失败输出复制 FuzzXxx/哈希,传给 go test -run | 只执行目标语料,不重新进行随机探索 |
最后再强调一次:种子语料应该小而有代表性,fuzz target 应该快速、确定且不依赖共享可变状态。语料库的价值是让问题可以重复出现,而不是用文件数量制造覆盖率幻觉。
常见问题
失败语料必须手动转换成 f.Add 吗?
不需要。Go 会按语料文件格式读取它;只有当你确实想把小样本直接写在测试代码里时,才把它改写成 f.Add。
为什么普通 go test 也会失败?
因为失败语料已经成为该 fuzz test 的默认种子。修复代码后,普通测试应当通过;如果仍失败,说明这个输入对应的缺陷还没有处理。
可以只用 -fuzz 不保留语料吗?
可以临时探索,但不适合作为回归流程。要让问题在后续构建和提交中持续可见,应保留失败语料或等价的稳定种子。
Go testing.B.Loop 怎样编写稳定基准循环
- 上一篇
- Go testing.B.Loop 怎样编写稳定基准循环
- 下一篇
- Go go:embed 怎样嵌入目录树
-
- Golang · Go教程 | 31分钟前 | go · 文件系统 · Go os.ReadDir Go File.ReadDir Go DirEntry Go 目录遍历
- Go os.ReadDir 怎样高效读取目录条目
- 269浏览 收藏
-
- Golang · Go教程 | 35分钟前 | go · Go go/types.Config Go Importer Go 类型检查 types.Check
- Go go/types.Config 怎样给类型检查接入导入器
- 222浏览 收藏
-
- Golang · Go教程 | 38分钟前 |
- Go maphash.Hash 怎样用固定种子复现哈希结果
- 341浏览 收藏
-
- Golang · Go教程 | 40分钟前 | Go 字节切片 encoding/binary AppendByteOrder
- Go binary.ByteOrder.Append 怎样把整数追加到现有字节切片
- 373浏览 收藏
-
- Golang · Go教程 | 1小时前 | go · IPv6 Go ipv4 netip.ParsePrefix CIDR
- Go netip.ParsePrefix 怎样解析 CIDR 字符串
- 334浏览 收藏
-
- Golang · Go教程 | 1小时前 | go · Go 版本选择 模块依赖 Go Modules go mod graph
- Go mod graph 怎样查看模块依赖路径
- 148浏览 收藏
-
- Golang · Go教程 | 1小时前 | 工具链 · Go教程 · Go 代码生成 工作目录 go generate go:generate
- Go go:generate 怎样固定代码生成工作目录
- 387浏览 收藏
-
- Golang · Go教程 | 1小时前 |
- Go testing.B.Loop 怎样编写稳定基准循环
- 302浏览 收藏
-
- Golang · Go教程 | 1小时前 |
- Go testing.T.Context 怎样让测试跟随取消
- 160浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 485次学习
-
- PubMedQA
- 深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
- 558次使用
-
- H2O EvalGPT
- H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
- 606次使用
-
- LMArena
- LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
- 633次使用
-
- HELM
- 深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
- 589次使用
-
- MMBench
- MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
- 413次使用
-
- Java 性能优化上线清单:从定位、改造到灰度发布
- 2026-06-11 860浏览
-
- Spring Boot 压测验证:Gatling、JMeter 与性能回归门禁
- 2026-06-11 843浏览
-
- Java NMT 非堆内存排查:Direct Buffer、线程栈与 Metaspace 分析
- 2026-06-11 826浏览
-
- Spring Boot 容器内存优化:JVM 堆、非堆与 MaxRAMPercentage
- 2026-06-11 809浏览
-
- Tomcat 连接与线程参数调优:maxThreads、acceptCount 与 KeepAlive
- 2026-06-11 792浏览

