Go 30:fuzz 能发现哪些输入假设
上篇用两份固定输入分别验收函数、HTTP 和真实数据库。固定输入清楚,但没遍历异常 JSON 或状态操作的交错次序。本篇沿用同一个任务服务写两个 Go fuzz 目标,检验有限的输入不变量;累计工程包、SHA-256和独立复跑步骤附原始日志。
HTTP 接收之后必须有一个可解释的结果
FuzzHTTPTaskSubmission 的种子包括合法的 {"text":"Go"}、UTF-8 文本、空白、text 类型错误、未知字段、两个相邻 JSON 对象和超过 64 KiB 的 JSON。每一轮拿 httptest.ResponseRecorder 调原有 POST /tasks handler,而非复制一套解析器。服务关闭要等待自己的 worker;若响应为 202,测试要求分配一个任务 ID,并在退出后读到 SUCCEEDED 或 FAILED。若返回 400/413,必须没有分配 ID 或留下记录。这使“报错时仍有后台任务”成为会导致 fuzz 失败的反例。
种子的坏输入也有明确预期:空白文本和类型不符不会当成已接受,拼接两个 JSON 文档不能只读取第一段后放行,大体积请求受 MaxBytesReader 限制。为避免 fuzz 自身无限吃内存,本目标对超过 maxInputBytes+64 字节的 payload 不再执行 handler;因此不能声称覆盖任意尺寸。这个目标用内存存储和响应记录器,不建真实 TCP 连接,更不进入 PostgreSQL。前几篇的 loopback HTTP、数据库和进程退出测试仍是不同的验收层。
字节序列探索状态转移
FuzzTaskStateSequence 将输入字节模 4 映射为 Start、Complete、Fail、Cancel。创建一个 QUEUED 的 task[0] 后,每执行一步就核对三个不变量:已终态的记录不再变动,同一 ID 的终态计数至多增加一次,非 SUCCEEDED 状态不携带部分计数。种子包括“排队时取消”“运行中申请取消后完成”“成功后再取消”和失败后再次领取。长度上限为 64 个操作,探索的只是顺序状态机;有竞态的并行读写仍要靠先前的闸门测试和实际 race 路径。这样把可检验承诺绑定在服务当前 TaskStore 上,而不是另写一套玩具状态机。
本版 testing.F.Add 提供启动种子,F.Fuzz 在目标报错时会保存失败输入;正常 go test 也会运行已保存的种子。本次初轮两个目标各设置 5s、一个 worker:HTTP 执行 1,482 次,状态序列执行 25,581 次,均退出 0;随后带先前 Go fuzz cache 的 3s 复跑分别执行 3,803 和 12,717 次。new interesting 是覆盖引擎感兴趣的新输入数,不是失败数。这两组计数受测试目标成本和缓存语料影响,不可据此声称状态机比 HTTP 实现快。完整包独立复跑另外用新 GOCACHE,避免把此前缓存语料充当包自带的输入。
练习:① go test -fuzztime=5s 显示 PASS,能否推断所有输入正确?不能,只能说在这次预算、种子、资源与实际运行路径上未找到反例。② 模糊引擎报告 new interesting: 20,是否要写一篇“发现 20 个 bug”?不能,它们是覆盖候选,不是导致不变量失败的样本。③ 如果以后出现 testdata/fuzz/FuzzTaskStateSequence/... 的失败文件,先做什么?保留原始文件与命令、确认可在普通 go test 重放、修复服务状态转移,再用固定种子和限时 fuzz 回归;不要删掉失败语料只留下绿色截图。本轮没有产生失败文件,不冒称已完成这一步。
冻结 Go 1.27.1 工具链的 testing.F.Fuzz、F.Add 和 fuzz 命令参数一手 go doc/go help、两轮实际 fuzz 的 stdout/stderr/退出码、真实 PostgreSQL 16.15 下的全包回归、本机 race 与旧 CLI 正反见 writing-plans/go-language/evidence/30/。Go 官方 fuzz 入门网页本轮请求超时,记录了 HTTP_STATUS=000 和 curl 退出码 28,不能将其当作已取得的资料;以冻结版本地文档为准。race 无报告只限运行路径;fuzz 未发现失败不证明不存在 bug。SQL 模糊测试、远程网络、数据库自身崩溃、外部副作用去重、其他平台、全站构建与发布仍 NOT_RUN。下一篇固定负载与业务断言,再谈 benchmark 的时间和分配。
参考资料
- Go 1.27.1
testing.F.Fuzz、testing.F.Add:本地冻结工具链原文及go help testflag输出见writing-plans/go-language/evidence/30/doc-*;本轮 Go 官方页面超时见同目录go-fuzz-doc.*。






