想象一下,你有一位非常严格的高端厨师(即 Rust 编译器),他因制作极其安全且快速的菜肴而闻名。这位厨师有一套非常具体的规则:如果你试图按错误顺序放入食材、使用被禁止的香料,或混合不协调的质地,他不仅会说“不行”。有时,你奇怪的请求会让他困惑到打翻整个托盘、尖叫,或完全僵住。这些就是论文中提到的“漏洞”。
问题在于,要 trick 这位厨师非常困难。如果你只是随机向他投掷食材(就像标准的“模糊测试”或自动化测试工具),他通常会无视你,或礼貌地说“顺序无效”。他很少会困惑到崩溃。
这时,ClozeMaster 登场了,这是研究人员提出的新方法。其工作原理如下,使用一个简单的类比:
“疯狂填词”策略
研究人员没有让厨师从零开始猜测你想要什么,而是决定查看过去的错误。他们进入厨师的投诉日志(即“漏洞报告”),找到了那些曾导致厨师崩溃或僵住的食谱。
他们意识到这些“崩溃食谱”具有特殊的结构。它们就像一场疯狂填词游戏(一种填空的文字游戏)。
掩码(空白处):研究人员选取了一份曾导致厨师崩溃的食谱。他们查看括号、方括号和花括号内的部分(如 ( )、{ }、[ ]),并用“掩码”(空白)覆盖这些特定部分,同时保持食谱其余部分完整。
- 类比:想象一份食谱写着:“将面粉与**______**混合后烘烤。”加粗部分即为空白。句子的其余部分(结构)是安全且语法正确的。
AI 填充者(大语言模型):他们使用一个智能 AI(大语言模型)来填充这些空白。但他们并非只是让 AI 随意写一句话。他们首先用所有那些旧的、有问题的食谱“训练”AI,使 AI 学会那种能让 Rust 厨师困惑的代码的特定“风味”。
- 类比:AI 就像一个研究过厨师每次发怒情况的学生。当被要求填空时,AI 不会只写“糖”;它可能会写出一些奇怪的内容,比如“一只活章鱼”,因为它知道厨师会对奇怪的组合感到困惑。
结果:由于食谱的结构是完美的(基于真实且可运行的食谱),厨师接受了它。但由于填充部分奇怪且复杂,厨师感到困惑并崩溃。这就揭示了一个新漏洞。
为何需要此方法
研究人员发现,Rust 就像一种拥有非常复杂语法规则的语言。
- 旧方法就像蒙着眼睛向靶盘投掷飞镖。由于规则过于严格,它们很少能击中靶心(即漏洞)。
- 直接 AI 生成就像让学生在不参考任何示例的情况下从零开始写食谱。学生(AI)对 Rust 的严格规则了解不足,写出的内容厨师会立即拒绝。
- ClozeMaster 则像是给学生一份部分写好的复杂食谱,只要求他们完成一个棘手的句子。学生了解上下文,因此写出的内容既符合规则,又足够奇怪,足以让厨师崩溃。
他们的发现
团队构建了一个名为 ClozeMaster 的原型工具,并在两个版本的 Rust 厨师(官方版本和社区自制版本)上进行了测试。
- 他们发现了 37 个新漏洞(崩溃或僵住),此前无人见过。
- 开发人员确认了其中 27 个,并修复了 10 个。
- 与之前的最佳工具相比,他们的工具在发现这些漏洞和探索厨师的“厨房”(代码覆盖率)方面表现更优。
核心结论
该论文声称,通过旧的、有问题的代码,隐藏其特定部分,并利用 AI 填充这些部分,他们可以创建新的、棘手的测试,从而暴露 Rust 编译器中隐藏的弱点。这是一种利用历史来预测未来错误可能藏身之处的方法。
以下是论文《ClozeMaster:利用大语言模型填充掩码真实程序以模糊测试 Rust 编译器》的详细技术总结。
1. 问题陈述
Rust 编程语言因其内存安全和线程安全保证,正日益被关键系统所采用。然而,Rust 编译器本身非常复杂,其中的缺陷可能导致生产系统中的未定义行为或崩溃。
- 挑战 1:语法复杂性。 Rust 学习曲线陡峭且拥有严格的语法规则(所有权、借用、生命周期),使得使用传统的随机生成或基于变异的模糊测试方法难以生成有效的测试程序。
- 挑战 2:大语言模型(LLM)的局限性。 虽然大语言模型在软件测试中展现出潜力,但直接提示它们生成 Rust 测试用例往往会导致无效代码,或无法触发编译器缺陷。这是由于“长尾”问题:Rust 在 LLM 训练数据中占比很小,现有模型难以应对 Rust 编译器特定且不断演变的边缘情况。
- 挑战 3:缺乏上下文。 现有的基于 LLM 的模糊测试工具通常依赖零样本生成或微调,而未利用历史触发缺陷代码的具体结构特征。
2. 方法论:ClozeMaster
作者提出了 ClozeMaster,这是一个模糊测试框架,它结合历史缺陷数据与大语言模型能力,采用了一种新颖的 ClozeMask 策略。工作流程包含四个主要步骤:
A. 数据集构建与增强
- 来源: 系统从历史 Rust 编译器缺陷报告(特别是标记为"C-bug"和"T-compiler"的
rustc 问题)、官方 Rust 测试套件以及 Glacier 仓库(包含内部编译器错误触发器)中收集代码片段。
- 增强: 为了增加用于微调的数据集规模,作者应用了数据增强技术:
- 随机删除(RD): 以概率 p=0.2 随机移除代码令牌。
- 随机交换(RS): 交换程序中两个语句的位置。
- 这将数据集从约 2.5 万条扩展到了 10 万条片段。
B. 大语言模型微调
- 模型: 选择开源的 Incoder 模型,因其具备处理代码填充任务的能力。
- 训练: 模型在精心策划的历史触发缺陷代码数据集上进行微调。这教会了大语言模型与 Rust 编译器漏洞相关的特定语法和语义模式。
C. ClozeMask 策略(核心创新)
ClozeMaster 不从头生成代码,而是采用“填空”方法:
- 掩码: 从数据集中选择一个代码片段。深度优先搜索(DFS)识别括号结构(
()、{}、[]、<>)。将这些括号 内部 的内容替换为 [mask] 令牌。
- 理由: 括号定义了 Rust 的语法和语义。保留外部结构可确保生成的代码在语法上有效,而掩码的内部内容则允许大语言模型探索边缘情况。
- 填充: 提示微调后的大语言模型填充掩码部分。
- 对“特性”块(例如
#![feature(...)])应用特殊处理,以鼓励生成已知不稳定的实验性特性。
- 如果填充后的代码与原始代码相同,则将其丢弃以确保新颖性。
D. 预言机与过滤
- 预言机: 系统使用
rustc 和 mrustc 编译生成的代码。
- ICE(内部编译器错误): 通过崩溃或“内部编译器错误”消息检测。
- 挂起: 如果编译超过 180 秒超时则检测为挂起。
- 去重: 存储堆栈跟踪和时间传递(time-passes)信息以过滤重复缺陷。
3. 主要贡献
- ClozeMask 策略: 一种新颖的方法,掩码历史缺陷报告中括号包围的代码段,并利用大语言模型进行填充。这种方法在保留 Rust 所需结构完整性的同时,利用大语言模型生成新颖且可能包含缺陷的变体。
- CLOZEMASTER 框架: 首个专为 Rust 编译器设计的模糊测试工具,集成了历史缺陷数据、数据增强和大语言模型微调。
- 实证验证: 在两个编译器(
rustc 和 mrustc)上进行了广泛评估,证明了其优于现有基线的性能。
4. 实验结果
作者将 ClozeMaster 与三个基线进行了评估:RustSmith(生成式)、Rustlantis(生成式)和 SPE(基于变异)。
- 缺陷检测:
- 报告总数: 37 个缺陷。
- 确认数: 27 个缺陷(其中 10 个已由开发者修复)。
- 对比: ClozeMaster 在
rustc v1.73 上发现了 11 个独特缺陷,而基线工具仅发现 0–1 个。
- 新颖性: 许多缺陷潜伏在 v1.56 这样古老的版本中(超过 2 年),未被其他工具检测到。
- 代码覆盖率:
- ClozeMaster 实现了 64.34% 的代码覆盖率。
- 这比 RustSmith(32.84%)高出 95.92%,比 Rustlantis(29.55%)高出 117.73%。
- 它也优于 SPE(62.02%)。
- 组件覆盖率: 缺陷发现于各种编译器组件中,包括解析器、宏、特质、生命周期和常量泛型。值得注意的是,“泛型常量表达式”特性是 ICE 的主要来源。
5. 意义与讨论
- 历史数据的有效性: 研究证实,历史上触发缺陷的代码片段包含有价值的领域特定知识。将其作为掩码/填充的基础,比随机生成或纯大语言模型生成更有效。
- 上下文的重要性: 消融研究表明,移除历史缺陷数据或使用未微调的模型会显著降低性能。此外,在没有“完形填空”上下文的情况下使用大语言模型(从头生成)会导致大部分语法无效。
- 模型选择: 论文强调,通用大语言模型(如 GPT-4o)或在通用代码上训练的模型(如 StarCoder)由于语法错误,在 Rust 填充任务中表现不佳。Incoder 因采用随机掩码策略训练,在此特定任务上表现更优。
- 影响: ClozeMaster 证明了将历史缺陷模式与大语言模型填充相结合,是测试像 Rust 这样复杂且安全关键的编译器的可行且强大的策略,能够揭示传统模糊测试工具遗漏的深层边缘情况。
工件可用性: 实现代码和缺陷列表公开可用,地址为 https://github.com/clozeMasterPro/clozeMaster。
每周获取最佳 computer science 论文。
受到斯坦福、剑桥和法国科学院研究人员的信赖。
请查收邮箱确认订阅。
出了点问题,再试一次?
无垃圾邮件,随时退订。