Go fuzzing 怎样让输入先通过格式预检查
在 Go fuzz target 的回调入口先检查长度、固定头部或字符范围,不满足本目标前提时用 t.Skip 或直接返回,再对通过的输入执行解码和较昂贵的性质断言。预检查不会让 fuzz 引擎只生成合法输入,而是让不适用的输入尽早结束;它也不应该把本来要测试的解析错误挡在被测函数之外。
官方说明:https://go.dev/doc/security/fuzz/。testing 的跳过规则见 https://go.dev/src/testing/testing.go。下面比较三种策略,示例只限定输入筛选与单次测试成本,不把执行量当成覆盖率或性能结论。
先限定昂贵断言的输入域
例如,目标性质是“合法十六进制文本解码、规范化之后,内容摘要保持一致”。奇数长度或包含非十六进制字符的字符串,并不满足该性质的前提。让它们继续做摘要或业务对象构造没有意义,宜先筛掉。
但如果目标是“解码器面对任意文本应正确拒绝非法格式且不崩溃”,非法输入就是测试主体,不能过滤。先把这两个目标分开命名,比在同一个回调中到处遇错 return 更清楚。
比较直接返回、Skip与构造输入
return 适合结束当前回调,不需要额外说明;t.Skip 明确表达该输入不属于当前目标的有效域,适合排查种子为何未进入断言。二者都不是修复数据,也不能代替发现错误时的 t.Fatal。不要先标记失败,再期待 Skip 将失败抹掉。
第三种策略是 fuzz 原始字节,再通过 hex.EncodeToString 构造合法文本。它适合专测合法数据后的业务性质,能避免反复生成坏格式;代价是只探索构造器可表达的文本形式,例如统一小写,不能覆盖各种大小写、空白或畸形文本。本文选择字符级筛选,保留输入文本本身的变化。
用轻量格式检查约束目标
在现有 Go 模块中新建 hexcheck_test.go,写入下面的完整示例。长度上限 4096 是本例成本预算,不代表标准库只允许这么长。空串符合偶数长度和字符条件,必须保留,不能顺手把空输入当作非法输入跳过。
package hexcheck
import (
"crypto/sha256"
"encoding/hex"
"strings"
"testing"
)
// 小样本上限属于本测试的成本预算,不是十六进制格式的通用限制。
const maxHexLen = 4096
func isHexByte(c byte) bool {
return c >= '0' && c = 'a' && c = 'A' && c maxHexLen || len(s)%2 != 0 { return false }
for i := 0; i
检查器先判断字节长度,再检查 ASCII 字符,避免为了筛选而提前完整解码、分配大型对象。进入有效域后,如果 DecodeString 返回错误,就应失败,而不是再次 Skip;否则预检查与解码器之间的分歧会被藏起来。摘要只是一个可复制的性质示例,真实业务可替换为确定性的业务断言。

把预检查成本和有效样本分开看
该筛选最多扫描预算内的字符串;只有格式通过后才分配解码结果。它仍有扫描开销,如果后续计算本来就很便宜,重复实现格式规则未必划算。对于 JSON、复杂协议或压缩格式,另写一个接近完整解析器的预检查器尤其容易成本倒挂,可优先用已有解析结果决定后续性质是否适用。
大量合法种子不代表变异输入都会合法,f.Add 只是起点。判断筛选是否值得时,应分别观察单次筛选成本、后续断言成本,以及进入断言的样本是否足够多。fuzz 的 execs 不是“执行了昂贵断言的次数”。本文没有执行压测,不提供虚构的加速倍数或跳过比例。
保留不应被筛掉的坏输入
下面增加独立的拒绝路径目标。它只设置长度预算,不在调用解码器之前排除坏格式。十六进制语法简单,因此用字符规则建立接受性期望;复杂格式不宜复制整套生产解析逻辑作为所谓独立判断。
// 与上面的代码放在同一个 _test.go 文件中,复用导入和常量。
func FuzzHexReject(f *testing.F) {
for _, s := range []string{"0", "gg", "0x12", "a b"} { f.Add(s) }
f.Fuzz(func(t *testing.T, s string) {
// 此目标只限制测试预算,不按格式预先跳过。
if len(s) > maxHexLen { t.Skip("超过本目标预算") }
wantValid := len(s)%2 == 0
for i := 0; i
FuzzHexDigest 负责合法输入性质;FuzzHexReject 负责预算内原始输入的接受性。两者共享 maxHexLen 和字符定义,但不共享“坏格式立即跳过”的策略。这个组合依然不测试超过 4096 字节的数据,若大输入属于产品风险,应另设有明确资源预算的测试,而不是声称已经全覆盖。

按目标选择筛选方式
| 目标 | 选择 | 主要代价 |
|---|---|---|
| 合法格式后的昂贵性质 | 轻量检查加 Skip | 无效变异仍会消耗调用成本 |
| 只关心合法载荷业务计算 | 原始字节加编码构造 | 文本形式受到构造器限制 |
| 解析器拒绝行为 | 不按格式筛选 | 需要单独控制长度和计算预算 |
| 预检查几乎等于完整解析 | 复用解析结果 | 需明确解析错误是否属于预期域外 |
以下命令供读者在自己的模块包目录运行。普通 go test 可运行种子,持续 fuzz 使用精确匹配选择一个目标;分别运行两个目标,不把同一次执行当作同时变异两个目标。本文未执行命令。
# 先运行这两个目标已有的种子,不启动持续变异。 go test -run '^FuzzHex(Digest|Reject)$' -count=1 . # 只对合法性质目标做有限时长变异。 go test -run '^$' -fuzz '^FuzzHexDigest$' -fuzztime=20s . # 单独探索原始文本的接受性。 go test -run '^$' -fuzz '^FuzzHexReject$' -fuzztime=20s .
预检查失败应该用 Fatal 吗?
如果它只是说明输入不满足该性质前提,应 Skip 或结束回调;若检查的是被测实现必须履行的承诺,则应报错,不能跳过。
能用 recover 忽略所有 panic 吗?
不应这样做。把真实崩溃转成跳过会掩盖缺陷。筛选条件应来自目标的输入契约,而不是失败现象。
只加合法种子就能取消预检查吗?
不能据此假设后续输入都合法。要么保留适当筛选,要么用构造器明确控制格式域,并另测原始坏输入。
选择规则很简单:先问正在测试“合法输入后的性质”还是“格式接受与拒绝”,再决定是否筛选。省掉无意义的昂贵断言是合理目标,省掉需要发现的失败不是。
Go unsafe.StringData 怎样处理只读字节视图
- 上一篇
- Go unsafe.StringData 怎样处理只读字节视图
- 下一篇
- Go HTTP/2 怎样限制并发流
-
- Golang · Go教程 | 1小时前 |
- Go goroutine 怎样用 semaphore 限制并发
- 402浏览 收藏
-
- Golang · Go教程 | 1小时前 |
- Go database/sql ExecContext 怎样判断影响行数为零
- 449浏览 收藏
-
- Golang · Go教程 | 1小时前 |
- Go HTTP/3 怎样选择 quic-go 适配层
- 199浏览 收藏
-
- Golang · Go教程 | 1小时前 |
- Go HTTP/2 怎样限制并发流
- 130浏览 收藏
-
- Golang · Go教程 | 1小时前 |
- Go map 删除期间迭代怎样设计清理逻辑
- 230浏览 收藏
-
- Golang · Go教程 | 1小时前 |
- Go sort.Interface 怎样稳定排序重复键
- 319浏览 收藏
-
- Golang · Go教程 | 1小时前 |
- Go fmt 处理自定义 Formatter 怎样避免递归
- 314浏览 收藏
-
- Golang · Go教程 | 1小时前 | 标准库 · go · Go iter.Seq bytes.Lines 按行读取
- Go bytes.Lines 保留换行符的遍历方式
- 297浏览 收藏
-
- Golang · Go教程 | 1小时前 | 内存 · 缓存 · go · GC · 运行时 · 缓存 Go GOMEMLIMIT runtime/debug SetMemoryLimit
- Go runtime.SetMemoryLimit 怎样配合缓存上限
- 381浏览 收藏
-
- Golang · Go教程 | 1小时前 |
- Go trace 怎样关联任务阶段
- 402浏览 收藏
-
- Golang · Go教程 | 1小时前 | go · pprof · Go pprof CPU profile StartCPUProfile StopCPUProfile
- Go pprof 怎样给 CPU 采样设置窗口
- 226浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 485次学习
-
- PubMedQA
- 深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
- 529次使用
-
- H2O EvalGPT
- H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
- 587次使用
-
- LMArena
- LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
- 613次使用
-
- HELM
- 深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
- 565次使用
-
- MMBench
- MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
- 388次使用
-
- 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浏览

