Unveiling Compiler Faults via Attribute-Guided Compilation Space Exploration (ATC 2025)

一句话总结:观察到 C/C++ __attribute__ 能对单个函数/变量/语句做 option 无法做到的细粒度编译控制,ATLAS 在种子程序上随机插入 attribute 并配对激活 option,比无 attribute baseline 平均多发现 109.1% bug,在 GCC/LLVM trunk 报 73 个 unique bug(58 已确认或修复)。

问题与动机

Compiler-Testing 长期依赖 CsmithYARPGen、EMI 等生成/变异测试程序,再叠加不同 compilation option(-O1-O3、sanitizer、独立优化 pass)做 stress / Differential-Testing,已在成熟 GCC / LLVM 上挖出大量 bug。但 compilation option 默认作用于整个程序,无法让「函数 A 强制 inline + 函数 B 关闭 ASan」这类 per-element 组合同时出现。

C/C++ 的 attribute(always_inlineno_sanitize_addressoptimize(...)vector_sizepackednoreturnpure 等)在 frontend 解析后会影响 semantic analysis、optimization、codegen 各阶段,粒度比全局 flag 更细。Figure 1 的 GCC bug 43234 表明:给 foo() 单独加 optimize("-ftree-loop-distribution") 会 crash,而全程序开同一 option 不会——说明 attribute 能触达 option-only 探索不到的编译状态。然而此前几乎没有工作系统化把 attribute 当作 fuzz 维度。

作者 claim 的边界是:在已有合法 C 种子程序上,通过 attribute-guided mutation 扩大 compilation space exploration (CSE),提高 ICE / crash 发现率;不是新的程序生成器,也不验证生成代码的运行时语义正确性(oracle 是编译器是否 crash)。

关键观察 / 隐含假设

  • 观察 1:attribute 与 compilation option 的交叉组合能触发 option 单独或 attribute 单独无法复现的罕见编译路径(如 always_inlinefoo() 被内联进带 no_sanitize_addressbar(),全局 -finline-functions + -fsanitize=address 仍不 crash)。

    • 依赖假设:测试目标是 C 前端 + middle-end + backend 的 ICE/编译失败;attribute 语义在 GCC/LLVM 文档中有明确定义;种子程序本身可编译。
    • 可能失效场景:Rust / Go 等无等价 attribute 机制的语言;attribute 被宏完全隐藏、parser 忽略的路径;只关心 miscompilation(需 differential execution oracle)时,crash-only oracle 覆盖不足。
  • 观察 2:现有 fuzz 已把「全局 option 组合」挖得较深,per-element 编译策略仍是低密度区域;向函数/变量/语句随机插 attribute 能显著提高 compiler 代码覆盖率(GCC line +30,519、LLVM line +41,806 vs RO)。

    • 依赖假设:LibTooling 解析出的 AST 位置与编译器实际处理位置一致;随机插入的 attribute 在类型检查通过后即可进入后续 pass;coverage 提升与 bug 发现正相关。
    • 可能失效场景:大量 attribute 导致语义非法或编译早期拒绝,使有效测试时间下降(论文报告 ATLAS compilable 比例低于 RO/EMI);coverage 饱和后边际 bug 率下降(RO 在 Csmith 上约 40h 饱和,ATLAS 10 天内仍持续发现)。
  • 观察 3:许多高价值 attribute 在真实软件中常见(optimize/optnone 用于调试与 benchmark,simd 用于 HPC,aligned/vector_size 用于 SIMD 代码),因此 attribute 触发的 bug 并非纯人造边角。

    • 依赖假设:文档列出的 attribute 集合与生产代码使用分布有重叠;trunk 测试发现的 bug 会出现在多版本 release 中(Figure 7:GCC 4 个 bug 跨越 8 年、LLVM 4 个跨越 6 年)。
    • 可能失效场景:论文排除 GPU 专用 attribute;C++ 模板/宏展开后 attribute 附着点与手写代码不同;仅测 trunk daily build,与 LTS 发行版维护分支的 bug 池可能错位。
  • 假设 1:FlipCoin 随机插入 + 类型约束筛选 + attribute→option 映射(Algorithm 3)足以系统探索 CSE,无需语义保持变异(不像 EMI 要求 I/O 等价)。

    • 证据强度中强——73 个 trunk bug、58 确认;但 24/73 仅 attribute 即可触发,49/73 仍依赖 option,说明 option 维度不可缺;且未证明已覆盖全部 attribute×option 空间。
  • 假设 2:10 天 Correcting Commits 对比 GCC-5.1 / LLVM-5.0.0 的 bug 数可代表 ATLAS 相对 RO/AO/EMI 的效率优势。

    • 证据强度——方法被社区广泛使用,但仍是近似计数;选旧版本是因为 sanitizer 支持与 bug 池已部分耗尽,有利于公平对比,却可能低估 trunk-only 长期 fuzz 的绝对发现量。

核心方法

ATLAS 四阶段流水线(Figure 3):Collect → Insert Attributes → Select Options → Validate。

Collect(§3.1):用 LLVM LibTooling 解析种子程序 AST,收集函数集 (S_f)、变量集 (S_v)、语句集 (S_s),为每个元素记录类型、名字、位置等属性元组,供后续插入定位。

Insert Attributes(§3.2, Algorithm 1):对每个元素 FlipCoin 决定是否插入;按类型过滤非法组合(如 noreturn 不能用于返回 int 的函数,packed 只用于 struct member)。三类插入:

  • 函数noinline / always_inline / optimize(level|"-pass") / pure / returns_twice / sanitizer 相关等;
  • 变量vector_sizealignedpacked 等;
  • 语句#pragma omp simdlinear(uval(x): y+1) 等带变量列表的 clause(Algorithm 2)。

每轮遍历生成一个变异程序,重复直到达到 (N) 个 variant。

Select Options(§3.3, Algorithm 3):维护 attribute→required option 映射 (\mathcal{M}_o)(来自 GCC/LLVM 文档)。例如 no_sanitize_address-fsanitize=address 才生效;将插入 attribute 对应的 option 合并为程序专属编译选项 (O)。

Validate(§3.4):在 (O) 基础上再随机抽取至多 5 个额外优化 option(沿用既有研究「平均约 5 个 option 触发 bug」的经验),对 GCC/LLVM 编译;编译器 crash 即报 bug,再用 C-Reduce 化简后人工提交。种子来源:GCC/LLVM 回归测试集、CsmithYARPGen

与 RO(无 attribute)、AO(无 compilation option)、EMI(Hermes 实现)的对比设计见 §4.3。实现与 attribute 列表细节见 atc2025-wu-jiangchang

设计取舍

  • 取舍 1:attribute 插入 vs 语义保持变异——不做 EMI 式行为等价约束,换取更大变异空间与实现简单;代价是无法发现「编译成功但错误代码」类 miscompilation,oracle 仅限 ICE/crash。
  • 取舍 2:随机 FlipCoin vs 定向搜索——低实现复杂度、易与任意种子生成器叠加;牺牲对 attribute 组合的结构化探索,可能重复无效组合;论文未报告 mutation 调度策略的收敛性。
  • 取舍 3:类型合法即接受 vs 语义可执行性——插入后 compilable 比例低于 RO/EMI(如 Csmith 上 ATLAS 30,170/46,416 compilable vs RO 53,458/53,481),用覆盖率与 bug 数换吞吐。
  • 取舍 4:GCC/LLVM 双栈 vs 单一 IR——覆盖主流 C 工具链;需维护两套 attribute 集合与 (\mathcal{M}_o),且排除 GPU attribute,不覆盖 CUDA/HIP 等路径。
  • 取舍 5:trunk 连续测试 vs 固定 release——trunk 利于抢先报 bug、减少 duplicate(§4.1);效率对比却用 GCC-5.1/LLVM-5.0.0,两套评估目标不完全一致。
  • 边界条件:最适合已有合法 C 种子 + 需要刺激 frontend/middle-end/optimization 交互的场景;对「纯全局 opt 空间」已饱和的编译器版本,增益主要体现在 attribute-only 触发的 24 个 bug 及 ATLAS 独占 unique bug(Csmith 上 10 个,约 20.4%)。

实验与结果

RQ1 — Bug finding(trunk 实战)

  • 共报告 73 个 unique bug(GCC 44,LLVM 29),58 已确认或修复,17 已修复
  • 49 个需 compilation option 才触发,24 个仅靠 attribute 插入即可触发
  • GCC 侧 1 个 P1(#114687)、14 个 P2;部分 bug 影响自 GCC-5 / LLVM-5 起长达 6–8 年的版本(Figure 7)
  • 组件分布(Table 2):Front-End 23(31.5%)、Middle-End 21、Optimization 16、Back-End 13
  • 高频触发 attribute:returns_twicevector_sizesanitizesimdoptimizealways_inline/noinline 等(Table 3)

RQ2 — 效率(10 天,GCC-5.1 + LLVM-5.0.0,Correcting Commits)

生成器方法GCCLLVM合计
CsmithRO / AO / EMI / ATLAS13/19/17/325/11/11/1718/30/28/49
YARPGenRO / AO / EMI / ATLAS8/12/10/256/10/9/1314/22/19/38
  • ATLAS 相对无 attribute baseline 平均多 109.1% bug;覆盖 AO 发现的全部 bug,并有 Csmith 10、YARPGen 11 个独占 bug
  • EMI 仍有少量独占 bug(更多变异 + 更高 compilable 吞吐);ATLAS 在相同周期内 compilable 数更低
  • 时间趋势(Figure 9):RO 约 40h 饱和,ATLAS/AO 在 10 天内持续发现

RQ3 — 代码覆盖率(10k Csmith 种子,GCC-15 / LLVM-19,编译限时 10s)(Table 5,括号内为相对 RO 增量):

  • GCC:36.3% line(+30,519)、37.6% function(+1,152)、23.2% branch(+24,958)
  • LLVM:36.8% line(+41,806)、27.6% function(+1,953)、21.7% branch(+12,954)
  • 四项方法百分比接近,但 ATLAS 在绝对覆盖行/分支数上最高

环境:48-core Intel 2.30GHz、120GiB RAM、Ubuntu 22.04;artifact 提供 Docker 镜像。

Critical Analysis

论证链条

动机案例(Figure 1/2)直接支撑「attribute × option 可触发新 bug」;流水线把 observation 映射到 Collect→Insert→Option→Crash oracle,逻辑闭合。trunk 73 bug + 效率实验 109.1% 提升 + 覆盖率增量形成三角验证。

薄弱跳步在于:将「attribute 提升 CSE」外推为「应成为 compiler testing 标准层」(§5.2 集成主张)——论文未量化集成到 EMI/Csmith 后的边际 bug 率,也未证明 109.1% 在最新 release(非 GCC-5.1)上仍成立。Abstract 的 109.1% 来自 controlled 10 天实验,与 trunk 73 bug 的绝对数量是不同实验设定,读者需分开解读。

假设压力测试

  • 论文已证明:在 C 种子 + GCC/LLVM 上,attribute 插入能提高 bug 发现、覆盖率,且不少 bug 对应真实常用 attribute。
  • 可能失效(推断):
    • 语言扩展:仅 C,未测 C++20 [[attribute]]、Rust #![feature] 等;模板实例化可能改变 attribute 附着语义。
    • Miscompilation:crash-only oracle 对 silent wrong code 无感;与 EMI 或 differential execution 结合才有语义保证。
    • Workload 漂移:若编译器减少 per-function attribute 支持或改为 IR metadata 统一表达,当前 AST 插入策略需重做。
    • 规模:单程序插入密度、(N) 个 variant 与 10s 编译限时如何外推到 CI 级小时长 fuzz,论文未建模。

实验可信度

  • Benchmark:种子含官方回归集 + Csmith + YARPGen,代表性强;效率实验刻意选旧版以利用 Correcting Commits,与 trunk 实战互补。
  • Baseline:RO/AO 隔离 attribute 贡献合理;EMI 选 Hermes(OOPSLA’16 live mutation)是强对手,但 EMI 更高 compilable 数引入混淆因子,论文诚实报告 EMI 独占 bug。
  • Ablation:AO vs ATLAS 证明 option 必要;RO vs ATLAS 证明 attribute 必要;缺少对 FlipCoin 概率、(N)、option 个数上限 5 的 sensitivity。
  • Metrics:聚焦 bug 数与覆盖率,未报告 fuzz 吞吐(programs/hour)、去重后 bug 率、误报率;Correcting Commits 精度依赖后续 fix commit 可识别。

系统性缺陷

  • 人工环节:crash 后需人工分析 + C-Reduce + 提 issue,难以无人值守闭环;LLVM 开发者响应慢,确认率低于 GCC(论文自述)。
  • 可观测性:论文未讨论 fuzz campaign 的统计面板、重复 crash 聚类、attribute 命中率——论文未讨论
  • 资源与尾延迟:未分析高 sanitizer + 高 opt 组合下的单测编译尾延迟分布;48-core 工作站结果难直接映射 modest CI 机器。
  • 隔离与恢复:并行 continuous testing 时缓存、编译器临时文件、竞态崩溃的隔离策略——论文未讨论
  • 兼容性:attribute 与 LTO、PGO、交叉编译的组合未被系统评测。

局限与 Future Work

  • 局限 1:只覆盖 C、排除 GPU attribute;双编译器文档驱动,attribute 集合随版本漂移需持续维护。
  • 局限 2:Oracle 仅限编译期 crash,不覆盖 miscompilation、错误诊断、debug info 损坏等其它 compiler reliability 维度。
  • 局限 3:变异不保证语义合法,compilable 率低于 RO/EMI,整体 fuzz 吞吐受限;vector_size 等 attribute 易与程序约束冲突。
  • 局限 4:bug 计数依赖 Correcting Commits 近似,且效率实验与 trunk 实战使用不同编译器版本。
  • Future work 1:与 EMI、Creal 等语义保持生成器级联——先 EMI 扩程序结构,再 ATLAS 插 attribute,量化边际 unique bug(§5.2 已提出方向,缺实验)。
  • Future work 2:把 attribute fuzz 与 miscompilation oracle(differential execution、translation validation)结合,覆盖 ICE 之外的错误。
  • Future work 3:对高产出 attribute(sanitize×inline、vector_sizesimd)做加权或演化搜索,替代纯 FlipCoin,在相同 compilable 预算下提高 unique bug 率。
  • Future work 4:扩展至 C++ / Rust 属性系统,或 IR 级 metadata fuzz,观察 frontend 插入 vs IR pass 插入的覆盖差异。

相关