尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

funfuzz 语法化模糊测试揭秘

funfuzz 语法化模糊测试揭秘 funfuzz 语法化模糊测试揭秘【免费下载链接】WeChatMsg提取微信聊天记录将其导出成HTML、Word、CSV文档永久保存对聊天记录进行分析生成年度聊天报告项目地址: https://gitcode.com/GitHub_Trending/we/WeChatMsg一段随机生成的 JS 代码把 SpiderMonkey 的 JIT 编译器当场炸了。奇怪的是没人手写过它——这段代码是自动长出来的结构看着像人写的却处处埋着坑。幕后推手是 funfuzz一个专攻 JavaScript 引擎的模糊测试工具集。它的核心思路是语法化模糊测试像编译器反着解析那样按文法逐层拼出暗藏陷阱的代码去轰炸引擎最脆弱的边角。funfuzz 在轰炸什么定位与思路分野funfuzz 是一套面向 SpiderMonkey 的模糊测试工具集其中子项目 jsfunfuzz 负责自动产出 JavaScript持续施压给引擎的编译器与执行器。它不靠人肉写用例而是让机器把合法又刁钻的代码源源不断地生成出来。思路上的分野其实是两种世界观的对撞。传统变异式模糊测试拿着一段已有代码随机改字节、删字符、翻符号赌的是改坏了正好踩中 bug而语法化模糊测试反过来把 JavaScript 抽象成语句 → 表达式 → 左值 → 字面量的文法层级像编译器解析代码那样逐层随机拼装天然就能产出结构上合法、甚至刻意不合法的程序。前者是往代码里扔石头后者是按图纸盖房子再把承重墙抽掉一根。README 里对src/funfuzz/js/jsfunfuzz/下的gen-grammar.js有一句很直白的定性它是整个 jsfunfuzz 最大的模块几乎就是一个上下文无关文法模糊测试器。文法工厂代码靠递归生长长出来打开gen-grammar.js每种语法成分都对应一个makeXxx(d, b)函数可以把它理解成一条文法生产规则makeScript→ 生成一整个脚本makeStatement(d, b)→ 生成一条语句makeExpr(d, b)→ 生成一个表达式makeNewId(d, b)→ 生成一个不与已绑定变量冲突的新名字两个参数d和b是整棵生成树的命脉参数全称在树里扮演的角色ddepth递归预算还能往下展开几层。每深入一层就扣 1~2扣到 0 必须收手保证代码有限长、一定写得完bbound已绑定变量表记着当前作用域已声明的变量名新标识符会绕开它们避免重名、保证引用合法真正让代码活起来的是Random.weighted的加权选择。makeStatement内部维护着一本加权文法手册statementMakers从第 150 行起列了上百条规则每条形如{ w: 权重, v: 生成函数 }抽到哪条就执行哪个生产规则。几个有代表性的权重权重 15把两条语句顺序拼在一起最常见的主干走的是常规路径权重 8生成类型不稳定的循环专戳 JIT 追踪式编译器的失效场景权重 7生成 try/catch 异常语句考察异常路径的处理权重 1故意剥掉分号试探引擎的分号自动插入逻辑。⚙️权重越高这种结构越常见权重 1 的破坏性规则像撒在地板上的暗雷平时不显山露水偶尔踩中一下正好砸到编译器最薄弱的角落。0.1% 的叛逆totallyRandom 怎么撕开裂口先抛个问题如果永远乖乖照文法生成输入空间就被文法锁死了——引擎没被文法覆盖到的角落永远够不着。funfuzz 的答案是给文法留一扇后门。mess-grammar.js第 10 行定义了一个常量var TOTALLY_RANDOM 1000; if (rnd(TOTALLY_RANDOM) 2) return totallyRandom(d, b);rnd(1000) 2的意思是每展开一个文法节点都有约 1/1000 的概率跳过既定文法改走彻底随机分支。这个totallyRandom只做三件事深度漂移d d (rnd(5) - 2)递归预算可能减少、不变甚至增加——文法规则绝对禁止的动作却可能生出出乎意料的深层嵌套盲抽生成器从allMakers集合里随机挑一个make*函数执行。这个集合启动时由getListOfMakers自动收集了全局所有make开头的函数等于把任何非终结符都变成合法选择字符串强制校验抽到的生成器若没返回字符串立刻抛错——fuzzer 自己也得保证产出可执行。同一思路还有个姊妹武器cat随机挑两个生成器把它们的输出直接拼起来在gen-grammar.js里随处可见。妙处在于绝大多数代码仍由文法托底结构大体合理但那 0.1% 的逃逸让输入空间涌进大量文法从没描述过的组合。恰恰是这些规则之外的拼贴最容易命中 JIT 编译器与优化器翻车的位置——因为正常程序根本不会这么写。种子的力量让崩溃可以重放rnd(n)背后的随机源在src/funfuzz/js/shared/random.js它封装了 Mersenne Twister梅森旋转器实现见src/funfuzz/js/shared/mersenne-twister.js并提供number、range、index、weighted、chance等加权随机工具。驱动入口src/funfuzz/js/jsfunfuzz/driver.js的start()会先掷出一枚种子并打印出来var fuzzSeed Math.floor(Math.random() * Math.pow(2, 28)); Random.init(fuzzSeed);之后每轮testOne()都固定以深度 14 调用makeScript(depth)生成代码。关键在于这条链路种子被打印 → 崩溃发生后用同一枚种子重新Random.init→ 整条随机序列被完整重放。没有这枚种子一次崩溃就是一次过眼云烟有了它每次失败都能被精确复现这正是模糊测试工具可维护性的命根子。一段代码的完整旅程从生成到崩溃生成只是起点。按 jsfunfuzz 的 README产出的函数体要挨过一套酷刑拦腰劈开两半分别编译——主要抓编译器错误处理的 bug整体编译一遍执行若执行结果是个生成器generator再逐步迭代它。外层由 Python 循环器src/funfuzz/js/loop.py接管--random-flags随机挑选引擎启动参数配置在shell_flags.py--compare-jit在不同优化级别间对比输出差异实现见compare_jit.py并对超时如 20 秒的实例强制杀掉。发现新 bug 时还会顺手调用 Lithium 做用例最小化把冗长的复现代码裁到最短方便开发者一眼看明白。5 分钟跑起来拿到代码后准备一个编译好的 SpiderMonkey 命令行./js两条命令即可开跑git clone https://gitcode.com/GitHub_Trending/we/WeChatMsg python -m funfuzz.js.loop --random-flags --compare-jit 20 mozilla-central ./js参数逐个看--random-flags开启随机启动参数让每轮用不同的引擎配置去撞--compare-jit开启跨优化级别对比专抓同一份代码、不同优化程度却跑出不同结果这类隐藏 bug20是单个实例的最长运行秒数超时即杀。想上规模并行压测可以用src/funfuzz/bot.py自动构建并调度多个 loop 实例。结尾funfuzz 的设计哲学能浓缩成三句话文法是结构骨架用加权生产规则递归长出结构合理的 JS概率是破坏力totallyRandom以 1/1000 撕开裂口靠深度漂移与盲抽生成器造出规则之外的输入种子是复现保障梅森旋转器配上打印种子让每次崩溃都能重放。想深入就从gen-grammar.js的文法主体和mess-grammar.js的totallyRandom实现读起。【免费下载链接】WeChatMsg提取微信聊天记录将其导出成HTML、Word、CSV文档永久保存对聊天记录进行分析生成年度聊天报告项目地址: https://gitcode.com/GitHub_Trending/we/WeChatMsg创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表