概念定义
人写的用例探索人想象得到的路径,模糊器枚举其余:从覆盖反馈中育种输入,保留发现新分支的变异体、丢弃冗余者。
工作机制
插桩(编译期或二进制翻译)把每个输入映射到覆盖签名;引擎变异种子——字节翻转、拼接、字典令牌——保留签名新颖的输入,并记录崩溃、挂起、OOM。结构化目标用文法感知变异器或协议状态模型生成“足够有效”以深入代码的输入。
使用场景
一切解析不可信字节的东西:文件格式、网络协议、JWT 与 Cookie 解析、图像解码器、模板引擎。语料库提交进仓库持续运行,每次都继承过去的发现。
常见误区
模糊是补充不是替代:它找没人想到的用例,测试钉住要紧的用例。“纯随机只能挠表面”早已过时——反馈驱动的变异能把覆盖推得很深。对无符号无覆盖的黑盒二进制盲变异是烧 CPU;挂起与 OOM 不是噪声而是可用性 bug;只喂空与平凡输入会饿死语料库的结构多样性。崩溃要先最小化才可执行,不可复现的二进制让崩溃无解。
代码示例
export interface PropertyCase { readonly numbers: number[]; }
export function check(numbers: number[]): boolean {
return numbers.every((n, i) => i === 0 || (numbers[i - 1] as number) <= n);
}
export function shrink(failing: number[], holds: (input: number[]) => boolean): number[] {
let current = failing;
let changed = true;
while (changed) {
changed = false;
for (let i = 0; i < current.length; i++) {
const candidate = current.filter((_, index) => index !== i);
if (!holds(candidate)) {
current = candidate;
changed = true;
break;
}
}
}
return current;
}
export function findCounterexample(seed: number[], holds: (input: number[]) => boolean): number[] | null {
if (!holds(seed)) return shrink(seed, holds);
return null;
}