资讯动态

覆盖引导模糊测试性能瓶颈与Intel PT硬件追踪优化

发布时间:2026/10/3 3:38:05 来源:尧图企业网站定制
前些年读Fuzzing相关的论文大部分时间都在纠结三件事覆盖率怎么收集、收集得准不准、能不能再快一点。Full-speed Fuzzing: Reducing Fuzzing Overhead through Coverage-guided TracingNDSS 2021正是把这三件事放到一起回答的代表作。这篇论文的核心主张很直接覆盖引导模糊测试coverage-guided fuzzing的主要开销其实不在“执行大量测试用例”本身而在每一次执行时为了拿到覆盖率而付出的额外代价。论文利用Intel PT这类硬件追踪能力把覆盖率的收集从“每次执行都实时更新”改成“执行时不打扰、事后按需重建”从而把fuzzing的执行速度拉回接近原生程序的速度。无论你是做模糊测试平台、写插桩工具还是单纯想理解覆盖率反馈机制的底层成本这篇文章都值得细读。1. 先说清楚背景覆盖引导模糊测试为什么“慢”1.1 覆盖率反馈是模糊测试器的“发动机”跑过AFL的人对这套闭环肯定不陌生先准备一批种子输入放进目标程序里跑一遍通过插桩手段收集这次执行覆盖了哪些代码区域把覆盖信息保存下来然后从种子池里挑一个输入按一定变异策略生成新输入继续执行比较新输入有没有带来新的覆盖如果有就把这个输入加入种子池作为下一轮变异的种子。整个过程很像一个爬山算法覆盖率反馈就是它的“高度计”。没有这层反馈fuzzing就退化成完全盲目的随机测试效率会大打折扣。这里特别值得强调的是“闭环”两个字。别的程序分析工具可以慢慢扫、慢慢建模fuzzing不行。fuzzing的天然工作模式是高强度重复执行一个目标程序在fuzzing的几天里可能被执行几十万甚至上百万次。任何一次执行里多出来的微小开销放到百万次这个量级上都会被放大成灾难。所以模糊测试工具对“执行路径上的额外计算”极其敏感任何无谓操作都可能拖慢整体节奏。另一个容易忽视的细节是覆盖率不是越简单越好。最原始的覆盖率可以只看“基本块有没有被执行”但基本块粒度太粗无法区分同一基本块内部的不同走向。AFL经典的做法是记录“边缘覆盖”edge coverage也就是从基本块A到基本块B的这条跳转边有没有被执行。边缘覆盖比块覆盖多一层上下文信息变异器能更细致地感知代码行为的变化。这个设计后来几乎成了覆盖引导fuzzer的标配libFuzzer、honggfuzz都采用类似思路。论文里讨论的覆盖率同样是以边缘覆盖为基准。1.2 拿到覆盖率是有代价的插桩开销那么现有工具都是怎么拿到覆盖率的最常见的两个方案一个是AFL式的编译期插桩一个是libFuzzer依赖的SanitizerCoverage。前者在编译目标时把一层很小的“探针”埋进基本块边界后者同样在编译期插入回调。无论哪种方案程序每跑到一个基本块都要执行一段额外代码通常涉及地址计算、位图索引、共享内存更新等操作。听起来只是几条指令积累起来非常可观。我曾经在自己维护的一个测试平台上做过粗略统计对一个比较复杂的解析器做AFL插桩后单次执行时间比原生执行翻了大约一倍有的目标甚至更夸张。这不是AFL或libFuzzer写得不好而是一个结构性问题。覆盖率收集被放在了执行的关键路径上除非完全不做插桩否则这个开销就跑不掉。可完全不插桩又拿不到覆盖率等于没有反馈信号这就形成了一对矛盾。更麻烦的是程序里大量执行路径其实是“无聊”的比如内存分配、库函数调用、系统调用这些代码反复被跑到但真正推动新发现的往往是少数新边界。传统插桩方案并没有区分这两类执行它把所有执行都无差别地加上了探针。论文想解决的本质上就是把“覆盖率即时产出”从执行关键路径上挪开交给硬件和延迟解析去处理。2. 论文的核心思路用硬件追踪拿到“免费”覆盖率2.1 为什么选Intel PT而不是继续优化插桩要继续降低覆盖率收集开销一条很自然的路线是把探针写得更短更优。比如用单条指令更新位图、用共享内存避免锁这些优化已经被各种工具做到了极致再往下榨的空间非常有限。论文选择了另一个方向把覆盖率收集从软件插桩换成硬件追踪。Intel PTIntel Processor Trace是Intel CPU提供的一种硬件追踪特性能够在几乎不干扰程序执行的情况下记录程序的控制流信息。它最初设计出来主要是为了性能调试和故障分析并不是给fuzzing用的。它的优势是开销极低接近零但缺点同样明显输出是压缩的二进制trace流要拿到可读的覆盖信息必须解码而原始trace可能非常大。这就像用行车记录仪代替仪表盘上的小传感器。原来想看到每个路口有没有转弯传统插桩是在每个路口装一根减速带每过一辆车都要颠一下Intel PT则是在车上装一个记录仪车速不受影响但事后你得把录像倒回来一帧一帧看才知道刚才走的哪条路。好处是行车过程不被干扰代价是事后处理成本非常高。论文的核心突破就是解决了“事后处理成本高”这个硬件追踪长期被诟病的问题。具体来说传统方案每执行一次程序就实时解析覆盖率论文把工作分成了两步先低开销地追踪执行轨迹再在需要时按需重建覆盖率。中间用一套精简的覆盖提取算法把Intel PT输出的原始trace转成语义明确的边缘覆盖供fuzzer的变异引擎使用。覆盖率收集方案执行开销反馈实时性边缘精度主要难点AFL编译期插桩中高每次执行都更新位图高执行结束即可读位图中哈希位图有冲突插桩代码本身拖慢执行SanitizerCoverage回调较高依赖回调函数高较高回调调用频繁函数调用开销明显Intel PT硬件追踪低接近原生执行低需要事后解码高可精确定位间接跳转trace数据量巨大解码复杂2.2 从trace到边缘覆盖又一座大山不过硬件追踪输出的是trace不是覆盖率。Intel PT记录的是分支事件和时序信息与AFL位图里那种“从基本块A到基本块B”的边缘信息并不是同一个格式。想从trace中恢复出精确的边缘覆盖至少要做三件事识别已执行的基本块、还原分支走向、把序列转换成上下文相关的边。而且Intel PT的trace是压缩编码的理解它需要解析包头、维护解码状态这一套流程如果直接跑在fuzzing循环里可能比原先的插桩方案还慢。论文对这个问题的处理思路关键词是“按需”。大部分时间内fuzzer并不需要每一轮都立刻得到精确覆盖率它真正需要的是在变异间隙知道新输入有没有带来新覆盖。如果能把trace先存下来批量解码那么解码本身的开销可以被分摊到很多次执行上而且可以只在有变异的节点执行。论文里还对解码过程做了针对性优化例如只处理目标进程内的trace段通过影子内存记录每个已解码边缘的命中状态避免把无用分支也解析出来。这些优化加在一起才最终把“硬件追踪事后解码”的总体开销压到比传统插桩更低的水平。读到这其实就明白了一件事这不是简单的“换一个覆盖率收集后端”而是把整个执行模型从“每次执行同步更新覆盖率”改成“执行时零干预、覆盖率异步重建”。思路说出来不难真正做起来要处理大量trace格式和解码性能的细节。3. 实操视角按Full-speed Fuzzing的工作流程拆解3.1 整体架构与控制流先从整体看这套系统怎么跑起来。论文的实现形态和普通fuzzer类似仍然包含种子输入、变异器、执行器、覆盖率反馈这几个模块。差异点集中在执行器和覆盖率反馈的连接处。常规流程是这样的fuzzer选择一个变异后的输入通过fork或子进程方式启动目标程序目标程序带插桩执行同时把边命中写到共享内存位图中执行结束后fuzzer直接读位图判断是否有新覆盖。Full-speed Fuzzing把最后两步改成了目标程序以接近原生方式运行同时Intel PT在后端记录控制流trace执行结束后先不急着解码而是把trace暂时保存或压缩起来等达到一定批量后再统一重建覆盖率并更新反馈信息。一次典型的批量式覆盖率追踪流程可以拆成这样fuzzer从种子池选中一个种子完成变异得到新输入。启动目标进程开启Intel PT追踪会话。执行变异后的输入。进程退出后根据退出状态先做一个粗筛明显没有探索价值的trace直接丢弃疑似产生新行为或触发崩溃的trace进入解码队列。累积N次执行后统一解码队列里的trace。通过影子内存重建边缘覆盖与已有覆盖集合比较。出现新覆盖的输入保留进种子池其余输入只保留崩溃样本用于去重。清理解码队列进入下一轮调度。这样设计的关键考量是把昂贵的解码工作从fuzzer的“热路径”剥离。可以把fuzzing循环看成两层外层做调度和变异内层做单次执行和覆盖率比较。传统插桩让内层每一步都背着包袱Full-speed Fuzzing则让内层尽可能轻装把包袱挪到外层批量处理。代价是覆盖率反馈的实时性下降但在模糊测试场景里反馈稍微滞后并不会破坏闭环的收敛性只要最终能正确识别新覆盖就行。3.2 coverage重建的关键细节与参数选择要真的把这套方案部署起来有几个细节必须抠清楚。第一个是trace的获取方式。Intel PT提供了多种过滤机制可以通过配置只记录某个地址空间的控制流从而把操作系统内核或无关库的trace排除在外只保留目标程序自身的执行轨迹。论文为了配合fuzzing会在每次执行时启动新的追踪会话并按执行结束时的崩溃标志决定trace是保留还是丢弃。这种“只关心有效执行”的策略能显著减少需要解码的数据量。第二个细节是覆盖率表示的粒度。传统AFL用位图位图里每条边对应一个哈希索引精度不高但胜在速度快。论文为了配合事后重建使用了一种更接近精确边缘覆盖的表示。执行trace经过解码后会生成一个边命中集合再映射到类似AFL位图的结构中。这个环节需要特别处理间接跳转因为间接跳转的目标在静态语境下不明确而Intel PT的trace里恰好记录了实际跳转目标这反而是相对于静态插桩的优势。间接跳转处理得好不好直接影响覆盖率反馈的准确性。第三个细节是执行时序。论文在多次执行之间使用了较快的进程启动和追踪开启流程尽量减少每次fork和trace初始化的耗时。虽然没有完整复现论文里的所有调优参数但自己在用类似思路做实验时体会过Intel PT的会话开启和关闭对单次执行时间的拖累往往比预期大。如果目标程序本身执行得很快开启追踪的开销甚至会占掉大半时间。所以论文在工程上对“什么时候开启追踪、什么时候关闭、什么时候丢弃trace”做了很多精细控制建议阅读源码时重点看这一块。想复现的读者可以从论文公开的full-speed-fuzzing仓库开始先在AFL测试集这种比较简单的二进制上跑通再换到自己的目标。第一次跑不要急着对照速度提升先关注执行是否正常、覆盖率是否有明显差异。4. 实验结果速读到底快了多少4.1 论文公布的性能数据论文的实验设计很典型在多个真实开源项目上对比传统覆盖引导fuzzer和论文实现的性能。根据论文公开的结果在多数目标上执行速度相对传统插桩方案有数倍到数十倍的提升尤其是那些原本插桩开销占比很高的目标提升更明显。读论文时最直观的感受是它把“执行速度”这一项从木桶里的一块短板直接变成了长板短板则转移到了trace解码环节。当然要泼一点冷水速度快不等于漏洞发现能力一定更强因为覆盖率反馈的时效性变化会影响变异策略的收敛曲线。论文同时也报告了崩溃发现数量和到达新覆盖的速度整体上是正向的说明“牺牲一点反馈实时性换取执行速度”的思路是行得通的。对做工程取舍的人来说这组实验数据最大的价值是给出了一个可参考的量化边界什么时候硬件追踪方案会赢什么时候传统插桩仍旧不差。4.2 运行开销的拆解与瓶颈转移看了性能数据之后值得再做一次开销拆解。传统插桩方案中单次执行的开销大致等于“执行本体插桩更新”。Full-speed Fuzzing的单次执行开销则等于“执行本体硬件追踪记录按需解码摊销”。论文把硬件追踪记录这一项控制得很低把解码从执行同步路径里挪出去所以单位时间内可以执行更多轮次。这也解释了为什么它能在加速比上胜出。但瓶颈并没有消失只是转移了。这套方案吃的是CPU解码能力和内存带宽。当trace积累速度大于解码速度时系统会开始排队如果目标程序循环密集、trace膨胀得特别快解码压力也会上来。因此论文在实现中加入了trace截断、压缩、选择性保留等机制这些细节才是真正让方案保持“full-speed”的原因。阅读论文时别只盯着结果图表建议把注意力放在开销模型和trace管理代码上。5. 复现和落地时容易踩的坑我的经验5.1 硬件和系统环境要求第一道门槛是硬件。Intel PT目前只在一部分Intel CPU上支持不同架构的PT实现细节有差异如果机器是AMD或ARM架构很难直接复现论文中的做法需要寻找对应厂商的硬件追踪方案但指令集和格式都不相同。建议先确认CPU是否支持Intel PT可以借助Linux下的perf工具做基础验证否则折腾起来会很痛苦。第二道门槛是操作系统和权限。Intel PT的配置和读取通常需要较高权限在容器或云主机里还涉及虚拟化透传问题。我遇到过在虚拟机里根本无法开启PT的情况最后只能换物理机跑。如果打算大规模部署到云上做持续fuzzing要重点确认云实例是否支持PT透传否则效果会大打折扣。还有一点容易被忽略目标程序的编译选项。为了让硬件追踪更完整目标最好保留调试信息和符号表这样trace解码后能更快映射到源码位置。如果目标被strip过覆盖率依然能采集但后续分析崩溃和定位新覆盖会困难很多。建议在复现时准备一份带符号的构建一份用于fuzzing的release构建两者配合使用。5.2 覆盖信息不完整或解码性能失控实际跑起来最可能遇到两类问题。第一类是新覆盖看起来变少了或者某些已知代码没有被报告覆盖。这往往不是解码算法错了而是trace过滤配置得过窄把一些本该纳入统计的库代码或内联函数路段丢了。把过滤条件放宽一些注意这会增加数据量需要找到平衡点。第二类是解码性能失控运行一段时间后内存占用暴涨或CPU居高不下。这通常是因为trace积压太多或者批量解码的批次过大。需要调整“多久解码一次”的参数以及是否对无价值trace做丢弃。论文的做法是每次执行结束时就对trace做粗筛明显没有带来新信息的直接丢只有可疑或探索性的trace才进入解码队列。自己实现时也可以沿用这个思路。提示如果目标程序非常短小单次执行本身不到毫秒级建议不要盲目上硬件追踪方案。PT会话开启和保存trace的固定开销可能超过插桩方案此时传统位图仍然是更优选择。论文里的收益主要在“执行负担较重的真实程序”上体现。最后再分享一点阅读源码的体会论文附录和开源实现里的trace解析代码并不好读建议先从简单示例入手一步步理解Intel PT的包格式。别急着把整套系统嵌入自己的fuzzer里因为硬件追踪的调试难点和普通软件调试不一样出错时很难靠日志定位。先把最小闭环跑通再谈优化会比较稳妥。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑