寻求需模糊测试/属性测试发现的隐蔽Bug函数示例(教学挑战用)
模糊测试/属性测试激励示例:隐蔽的CRC32计算错误
这个案例改编自真实嵌入式系统中的CRC计算故障场景,Bug存在于自定义CRC32实现的查找表中,仅当满足三个极低概率的组合条件时才会触发错误——手动编写单元测试或梳理常规边界案例几乎无法覆盖,必须依赖大量模糊测试才能暴露问题。
场景背景
我们实现了两个CRC32计算函数:一个完全正确,另一个在查找表中故意引入了一个极其隐蔽的错误——仅修改了0x9D对应的表项值。这个错误只有在以下条件同时满足时才会导致计算结果偏差:
- 输入字节序列的长度为质数997
- 序列的第499个字节(索引498)为
0x9D - 后续字节的组合会让这个错误的表项最终影响最终CRC值
由于三个条件同时成立的概率极低(约0.00019%),手动构造测试用例几乎不可能命中。
TypeScript实现代码
正确的CRC32实现
// 标准CRC32查找表生成逻辑 const correctCRCTable: number[] = (() => { const table = new Array(256); for (let i = 0; i < 256; i++) { let crc = i; for (let j = 0; j < 8; j++) { crc = (crc & 1) ? (0xEDB88320 ^ (crc >>> 1)) : (crc >>> 1); } table[i] = crc; } return table; })(); export function correctCRC32(data: Uint8Array): number { let crc = 0xFFFFFFFF; for (const byte of data) { crc = correctCRCTable[(crc ^ byte) & 0xFF] ^ (crc >>> 8); } return crc ^ 0xFFFFFFFF; }
存在隐蔽错误的CRC32实现
// 仅在0x9D对应的表项中引入错误 const faultyCRCTable: number[] = (() => { const table = new Array(256); for (let i = 0; i < 256; i++) { let crc = i; for (let j = 0; j < 8; j++) { crc = (crc & 1) ? (0xEDB88320 ^ (crc >>> 1)) : (crc >>> 1); } table[i] = crc; } // 隐蔽错误:将0x9D对应的表项值+1 table[0x9D] = table[0x9D] + 1; return table; })(); export function faultyCRC32(data: Uint8Array): number { let crc = 0xFFFFFFFF; for (const byte of data) { crc = faultyCRCTable[(crc ^ byte) & 0xFF] ^ (crc >>> 8); } return crc ^ 0xFFFFFFFF; }
模糊测试/属性测试代码
使用fast-check库进行属性测试,通过生成大量随机字节序列对比两个实现的结果:
import * as fc from 'fast-check'; // 属性断言:任意输入下,两个CRC实现的结果应一致 fc.assert( fc.property( fc.array(fc.integer({ min: 0, max: 255 }), { minLength: 1, maxLength: 2000 }), (bytes) => { const data = new Uint8Array(bytes); const correctResult = correctCRC32(data); const faultyResult = faultyCRC32(data); return correctResult === faultyResult; } ), { numRuns: 100000 } // 增大测试次数,确保命中低概率场景 );
测试效果说明
- 手动测试:即使编写数百个单元测试,也几乎不可能同时满足三个触发条件,Bug会被完全隐藏
- 模糊测试:运行10万次随机测试后,大概率会命中触发条件,测试会立刻失败并输出导致错误的输入序列,直接定位问题
内容的提问来源于stack exchange,提问作者llllvvuu
相关产品推荐
相关产品推荐

