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

Go fuzz 测试为什么没有复现保存的语料

来源:17golang原创

时间:2026-10-26 00:50:09 487浏览 收藏

Go fuzz 测试没有复现保存的语料,常见原因是把模糊测试缓存当成普通测试会读取的种子、语料目录不属于当前包、目标名称或-run过滤不匹配,或者相同输入在当前代码里已经不再失败。先确认样本确实被运行,再判断它是否还能触发错误。

官方地址:https://go.dev/

保存输入与复现失败是两回事

排查这类问题时,我会先区分三个对象:代码中f.Add提供的种子、包目录下testdata/fuzz/目标名中的文件,以及工具维护的fuzz缓存。它们服务的用途不同。“磁盘里有一个文件”不能证明普通go test会读取它。

对象用途复现建议
f.Add输入显式种子随测试源码保存
testdata/fuzz/FuzzName/文件名文件种子与失败回归保留原始编码和目标名称
fuzz缓存辅助后续覆盖探索不要依赖另一台机器的缓存目录

本文解释现有规则,不代表新版本变更公告。把“再探索一轮能否撞到问题”改为“指定同一输入再次运行”,才能缩小不确定性。

种子语料与探索缓存结构说明图
结构说明图:f.Add、文件种子和探索缓存属于不同输入来源,不是测试运行截图。

把语料放回目标所属的包

假设目标定义在internal/frame/frame_test.go,名称是FuzzFrame,那么文件种子应属于internal/frame/testdata/fuzz/FuzzFrame/。放在仓库根目录的testdata,或使用旧目标名目录,都不能自动绑定这个子包里的目标。重命名Fuzz函数时,也要处理对应目录。

使用失败报告里的原始样本文件,不要把原始业务字节直接粘贴成语料文件。Go语料采用工具定义的带版本头、类型和值的格式,参数数量和类型必须匹配Fuzz回调。若只想加入一个已知字符串,f.Add往往更直接。

用普通测试精确运行样本

# 从模块根目录运行目标包的文件种子,强制重新执行而非复用测试成功缓存。
go test ./internal/frame -run='^FuzzFrame$' -count=1 -v

# sampleID替换为失败报告中的实际子测试名,不是磁盘绝对路径。
go test ./internal/frame -run='^FuzzFrame$/^sampleID$' -count=1 -v

观察-v输出中是否出现目标和相应子测试;如果只看到没有匹配测试,不应据此宣布问题已经修复。失败报告给出的复现命令应优先采用,并确保当前包路径与报告一致。-run是正则表达式,目录名不能直接代替测试名称。

这些命令展示复现方式,本文没有在本机执行。复现已有种子不必启动持续探索;需要寻找新输入时,才使用-fuzz选择单个目标并设置探索时间。例如:

# 探索一个目标;不把是否再次发现某输入作为单样本复现的依据。
go test ./internal/frame -run='^$' -fuzz='^FuzzFrame$' -fuzztime=10s

让输入类型和断言保持一致

下面是刻意保留边界错误的独立教学目标,可放到同一包的_test.go文件。它通过f.Add表达一个两字节种子,帮助区分“输入没运行”与“断言不再失败”:

package frame

import "testing"

func FuzzFrame(f *testing.F) {
    f.Add([]byte{3, 9}) // 声明长度3,实际载荷只有1字节。
    f.Fuzz(func(t *testing.T, data []byte) {
        if len(data) == 0 { return }
        // 故意不检查声明长度,展示缺少边界处理如何被确定输入触发。
        payload := data[1 : 1+int(data[0]) : len(data)]
        _ = payload
    })
}

这个目标预期会因切片边界失败,不应作为生产解析实现。修复应在切片前判断声明长度不超过len(data)-1,再定义截断输入是返回错误还是按协议忽略。不要用无条件Skip或吞掉panic让样本看起来通过。

包路径目标名称输入类型与断言的绑定结构说明图
结构说明图:文件种子绑定目标名称,目标约束参数类型,断言描述业务不变量。

把回调从[]byte改成string或增加参数之后,旧文件可能需要适配。适配应保留导致问题的字节语义;换一种类型表示后,原来的失败条件未必还成立。涉及随机数、环境变量、网络或共享全局状态时,同一输入也可能产生不同结果,需要把触发条件纳入可重复的测试设置。

把失败样本作为回归资产

我会保留能够说明故障的最小样本、对应断言和修复前后的行为说明。失败输入通常适合提交到目标的testdata目录,供普通测试持续回归;缓存仅作为探索辅助。目录有无文件与缺陷有无修复,应分别判断。

样本运行了但没有失败,是工具跳过了吗?

不一定。代码、编译条件、平台或外部状态可能不同,修复也可能已经生效。先确认目标和输入一致,再比较触发条件。

删除所有缓存能解决吗?

不应先删除。-count=1可避免普通测试成功缓存影响观察,但不能修正错误的目录或目标名,也不会把fuzz缓存自动转换成文件种子。

只把语料文件发给同事够吗?

还应给出包路径、目标名、代码版本、必要构建条件以及报告中的复现命令,避免同名目标与参数签名不同。

官方资料:Go Fuzzing、testing的Fuzzing规则、go test参数。

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