文章目录
摘要
编写高质量的模糊驱动程序是非常耗时的,并且需要对函数库有深刻的理解。然而,当前最先进的模糊驱动自动生成技术的性能还有许多需要改进的地方。从用户代码中学习的模糊驱动程序可以到达深层状态,但受限于它们的外部输入。另一方面,解释性模糊测试可以探索大多数API,但需要在广阔的搜索空间中进行大量的尝试。
本文提出了覆盖率引导的模糊器PromptFuzz,用于提示模糊,通过迭代生成模糊驱动来探索未发现的库代码。为了探索模糊驱动在提示模糊过程中的API使用,本文提出了几个关键技术:指导性程序生成、错误程序净化、覆盖率引导的提示变异和约束模糊器融合。本文实现了PromptFuzz,并在14个真实存在的库上评估了其有效性,将其与OSS - Fuzz和最先进的模糊驱动生成解决方案(即Hopper)进行了比较。
实验结果表明,PromptFuzz产生的模糊驱动具有更高的分支覆盖率,是OSS - Fuzz的1.61倍,是Hopper的1.67倍。此外,PromptFuzz生成的模糊驱动程序成功检测出44次崩溃中的33次真正的bug,这些bug以前是未知的,其中27次已经得到了各自社区的确认。
一、介绍
目前,模糊测试在保证软件安全性和稳定性方面发挥着至关重要的作用。其中一个例子是OSS - Fuzz,它为开源软件部署了最先进的模糊测试。截至2023年2月,OSS - Fuzz已经在850个项目中识别和解决了超过8900个漏洞和28000个bug。虽然灰盒模糊测试在识别bug方面也取得了成功,但OSS - Fuzz所取得的令人印象深刻的成果在很大程度上可以归功于各个贡献者在整合新项目方面付出的巨大努力。在集成项目进行模糊测试时,开发人员必须选择合适的模糊器并编写高质量的模糊驱动程序,模糊驱动是必不可少的,因为它们用于解析来自模糊测试的输入并调用被测软件中的代码。然而,编写高质量的模糊驱动程序是一项具有挑战性的任务,既耗时又需要对库有深入的理解。因此,手工编写的模糊驱动程序只能覆盖软件使用的一小部分,无法通过模糊测试对代码进行充分的测试。
模糊驱动程序自动生成技术不需要人工编写模糊驱动程序,而是从已有代码或动态反馈中学习库API的使用方法。FUDGE,FuzzGen和Utility静态地从源代码中提取API使用代码,而APICraft和WINNIE动态地从已有进程的执行轨迹中记录API调用序列。然而,该方法的效率受到消费者代码的限制,无法发掘消费者代码未覆盖的潜在有效用途。Hopper将库模糊测试问题转化为解释器模糊测试问题,从API调用的动态反馈中学习有效的API使用情况。虽然它可以覆盖大部分的API函数,但是可以找到有用的API调用序列。虽然它可以覆盖大多数API函数,但是在庞大的搜索空间中,找到到达深度状态的有用API调用序列需要无数次的尝试。
大语言模型在产生与用户意图一致的代码方面表现出优异的性能。尽管现有的工作已经尝试使用各种大语言模型来生成模糊驱动程序,但它们生成模糊驱动程序的指令仅限于特定的场景。生成的模糊驱动程序仍然存在API使用多样性的问题,往往无法覆盖很少使用的代码或深层状态。
为了解决上述挑战,本文引入了覆盖率引导的模糊测试器PromptFuzz,它通过迭代地生成模糊驱动来探索未发现的库代码。PromptFuzz包括一个基于LLM的程序生成器和一个基于运行时的程序错误预言器。PromptFuzz的核心思想很简单:通过覆盖指导指导LLM生成所需的模糊驱动,并利用程序错误预言保证其有效性。
二、设计
2.1、总览
PromptFuzz侧重于对LLM的提示进行变异,以产生覆盖更广泛的库API使用范围的程序。PromptFuzz的工作流程如图所示。
最初,PromptFuzz从库头中提取函数签名和类型定义。然后,这些提取的细节被用于构造LLM提示,指导LLM生成使用指定API组合的程序。然后执行生成的程序,并根据其运行时行为进行净化。
在这个净化过程中,代码覆盖率也被收集。通过净化的程序存储在种子库中,而触发独特分支的程序被标记为独特种子。种子库中程序的代码覆盖率起到反馈、指导的作用。这种反馈有助于PromptFuzz将提示与更有可能探索新代码路径的API组合在一起。这个迭代过程一直持续到没有新分支被发现或者查询预算耗尽为止。
在最后阶段,PromptFuzz推断种子程序中对库API施加的约束。为了增强这些种子程序的模糊能力,PromptFuzz将唯一种子中具有固定值的参数转换为可以接受模糊测试中随机字节的参数。
2.2、指导程序生成
PromptFuzz选择了ChatGPT和GPT-4等模型,其构造了LLM提示,从而专注于生成库API的特定组合。为编写多样化的程序生成提示,PromptFuzz包含两个关键组件:任务和库上下文。
- 任务组件:指定了LLM应该执行的预定程序的详细信息,它涉及到从库中应该使用哪些API函数,这些API函数应该包含在一个LLVMFuzzerTestOneInput函数中。且采用零样本的思维链来增强LLMs的焦点。这包括API签名、自定义类型定义和库中包含的头信息。考虑到当前LLMs的上下文长度限制和语料库成本,PromptFuzz限制了库上下文中使用的API和自定义类型的数量。当库中API函数数量超过设定的限制(例如, 100)时,PromptFuzz采用随机选择策略,每次选择一个可管理的API函数数量,保证我们保持在限制范围内。对于自定义类型,PromptFuzz只选择所选API组合中API使用的类型,兼顾了相关性和效率。
- 通过整合对库所的上下文理解,LLMs产生的"幻觉"代码的发生可以显著减少。库规范在指导LLM生成符合库所要求的特定模式的代码方面起着至关重要的作用。一些库API可以从文件、文件流或文件描述符中读取输入,这可能会偏离模糊驱动程序的标准例程。通过将相关的库规范融入到LLMs的提示中,我们可以更容易地生成遵守这些规范的代码模式。
通过填充这些组件中的输入来生成这样的提示,PromptFuzz就会使用该提示查询LLM以生成程序。