f.add() 添加的种子未加载是因为go fuzz仅识别硬编码路径fuzz/fuzzparse/corpus/下的合法文件(纯小写十六进制命名、utf-8无bom、无换行符),而f.add()是运行时注入机制,与磁盘种子无关。

为什么 f.Add() 加了种子却没被加载?
这是最常让人懵圈的问题:明明写了 f.Add("abc"),跑 go test -fuzz=FuzzParse 却看不到 loaded 1 seeds 提示,fuzzer 全靠瞎变异——说明种子根本没进引擎。
- 路径对不上:Go 只认
fuzz/FuzzParse/corpus/这个硬编码路径,fuzz/parse/、fuzz/fuzzparse/、甚至大小写错一个字母(如Fuzzparse)都会静默跳过 - 文件名非法:种子文件必须是纯十六进制小写+数字(如
0000000000000001),带.txt、下划线、大写字母,一律忽略 - 内容编码出错:BOM 头、
\r\n换行符、非 UTF-8 字节(比如误存为 GBK 的中文),都会导致加载失败且不报错 -
f.Add()和磁盘种子是两套机制:f.Add()是运行时注入,不生成文件;磁盘种子是启动时加载。两者不冲突,但别以为放了文件就不用f.Add()——它仍是最快验证新 case 的方式
f.Fuzz() 闭包里能用 fuzz.String() 吗?
不能,而且这么写会编译失败。
-
fuzz.String()不是“生成一个字符串”,而是模糊测试框架内部的类型标记辅助函数,只能在f.Fuzz()的参数签名里出现(如f.Fuzz(func(t *testing.T, s string) { ... })),不能在函数体里调用 - 你写
fuzz.String()在闭包里,Go 会报undefined: fuzz或类型不匹配错误,因为fuzz包不在作用域内 - 真正可控的输入来源只有两个:
f.Add(...)显式给的值,以及 fuzz 引擎从 seed + 变异策略中生成的值——后者你无法干预生成逻辑,只能靠种子质量去引导
数值边界怎么喂才有效?别只塞 "123"
模糊测试不是随机乱试,是“有方向地撞墙”。对整数运算类函数(比如加法、解析器里的长度字段),种子得直击临界点。
- 必加极值:
math.MaxInt64、math.MinInt64、math.MaxUint32,不是为了“覆盖类型”,而是触发截断、溢出分支 - 邻域比极值还重要:
math.MaxInt64 - 1、+1、0、-1,这些值更容易让 fuzz 引擎变异出真正 crash 的组合(比如MaxInt64 + 1 → panic) - 别传非法值骗自己:如果函数签名要求
uint32,就别塞负数种子——fuzzer 会把它转成极大正数(比如-1变成0xffffffff),这可能绕过你本想测的逻辑分支
HTTP 解析器 / JSON 反序列化这类场景,种子该怎么选?
结构化数据的 fuzz 效果,90% 取决于种子是否模拟了真实畸形模式,而不是“多”或“长”。
- 合法最小单元必须有:
{}、[]、{"a":1}、[1,2,3]—— 这些是 parser 的“安全起点”,变异才不会一上来就全崩 - 典型截断要手动造:
{"a":(冒号后直接断)、{"a":1,(逗号后断)、{"a":null(缺右括号)——这些是真实网络传输中高频出现的损坏模式 - 别用空字符串或全空格当种子:Go fuzz 会跳过它们,且这类输入通常早被前置校验拦住,根本进不到核心解析逻辑
- 从线上日志扒真实 payload:抽 20–50 条真实 API body(去重、去敏感),存成
fuzz/FuzzParseJSON/corpus/下的合法文件,效果远超手写 10 条
模糊测试不是开个开关就完事,它特别吃种子设计的直觉——这种直觉来自你对被测代码哪条分支最容易崩、哪个字节最容易被误读的理解。写错一个路径、少一个截断 case,可能就漏掉一个内存越界。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











