论文链接:FuzzingBrain-Bench V1: Evaluating Open-Ended Bug Discovery by LLMs 代码仓库:https://github.com/fuzzingbrain/FuzzingBrain-Bench 发表时间:2026年8月 机构:Texas A&M University + University of Novi Sad(塞尔维亚诺维萨德大学)——高校合作(Ze Sheng 与 Aleksandar Kezic 共同一作,后者双隶属) 领域标签:cs.AI / cs.CR / cs.LG / cs.SE,软件安全评测、LLM Agent
一、论文背景
理解本文需要四个概念:LLM 漏洞发现评测的四代演进、fuzzing 与 sanitizer、crash 签名、目标复现式评分的盲区。
背景动因:2025 年 NVD 收录 CVE 达 48,185 条,创历史新高、较 2024 年的 39,962 条增长 20.6%。漏洞体量上升使可扩展的缺陷发现技术愈发重要,LLM 已被用于代码审计、漏洞复现、补丁等环节——系统评测其「漏洞发现能力」因此成为基础设施问题。
四代评测演进:第一代给单个函数做二分类(脆弱/非脆弱),与数据集金标比对——单函数输入过于简化,且分类对了不代表理解缺陷。第二代要求输出结构化报告(CWE 类、根因行、描述),仍是静态匹配——不执行任何代码,匹配记录答案既不保证答案正确、也不证明缺陷真能触发。第三代引入动态执行:模型生成触发完整代码库中漏洞的输入,在带 sanitizer 的 harness 上运行——但现有基准(CyberGym、SEC-bench 等)只按预定义目标漏洞判成功。第四代即本文:开放式发现,不预设目标。
fuzzing 与 sanitizer 是什么? Fuzzing 是向程序灌入大量随机/变形输入、观察其崩溃的自动化测试技术;harness 是把目标函数包装成「喂一个输入、跑一段逻辑」的驱动程序。Sanitizer(AddressSanitizer、UBSan、LeakSanitizer、Jazzer 等)是编译期插桩工具,在越界读写、内存泄漏、未定义行为、JVM 异常发生时输出结构化报告——把「沉默的内存破坏」变成「可读的错误报告」。
目标复现式评分的盲区:第三代基准问「你触发的是不是那个已知漏洞」,答案只有是/否。但模型在探索代码库时经常发现别的缺陷——这些真实、有效的发现在目标匹配下计为零分,系统性低估模型能力,且评分天然偏向「记忆已知 CVE」而非「理解代码」。本文实验直接验证了这一点,并以此重构了评分范式。
二、论文定位和关联工作
相关工作沿四代演进组织,本文的定位针对第三代两大代表。
CyberGym(ICLR 2026):以输入让 pre-patch 版崩溃而 post-patch 版不崩溃计成功——依赖开发者补丁的正确性与完整性,且把评分锚定在单一已知漏洞上。SEC-bench(NeurIPS 2025):要求 PoC 在预期位置触发相同 sanitizer 错误——同样限定目标,且「匹配记录答案」可能出错。两者还有共同局限:目标漏洞全是 C/C++ 内存安全类。
本文与第三代的三个决裂:(1) 移除预定义目标,按「经 harness 触发的不同 crash 签名数」计分——模型发现计划外缺陷同样得分;(2) 不限于 C/C++ 内存安全:纳入内存泄漏、OOM、超时目标与 Java/JVM 漏洞,跨 ASan/UBSan/LSan/libFuzzer/Jazzer 五种工具评级;(3) 只保留脆弱构建、不做 pre/post 补丁对比——使评测结果独立于开发者补丁的质量(一个有缺陷或不完整的补丁会让差分式评分失真)。
| 维度 | 第一代分类 | 第二代报告 | 第三代目标复现 | 第四代开放式(本文) |
|---|---|---|---|---|
| 执行代码 | ✗ | ✗ | ✓ | ✓ |
| 评分对象 | 金标标签 | 记录答案匹配 | 单一目标漏洞 | 任意有效 crash 签名 |
| 缺陷类型 | 不限 | 不限 | C/C++ 内存安全 | 14 类含 JVM/DoS |
| 依赖补丁 | — | — | 是(CyberGym 差分) | 否 |
| 低估计划外发现 | 严重 | 严重 | 是(计零分) | 否(正是设计目标) |
定位结论:这是一篇评测方法论纠偏型基准工作——价值不在提出新模型或新算法,而在指出「目标复现式评分在结构上测不准开放式发现能力」,并用 crash 签名计分制落实纠偏。
三、问题定义
具体问题:如何评测「模型在真实开源项目中发现 bug 的能力」,使其反映真实能力而非对已知漏洞的记忆?
核心洞察:漏洞发现本质是开放式生成任务而非验证任务——正确答案集合不可预先枚举(代码库里到底有多少可达缺陷是未知的),因此评分不能是「与唯一金标比对」,而应是「数出你触发的不同真实故障」。类比:评价一个质检员,不该只问「你找到没找到已知的那条裂缝」,而应数「你独立发现了多少条不同的真实缺陷」。
形式化:给定挑战集(每题=一个自包含 Docker 镜像:脆弱版本源码 + harness 源码 + 配置 + 插桩后的 harness 二进制),模型在轮次预算(100)与时间预算(1800 秒)内提交 PoC 候选;每个候选跑三遍,三遍同签名崩溃才计入。模型总分:
$$\text{score} = \sum_{c=1}^{77} \min(3, \text{sig}_c) \cdot D_c$$其中 $\text{sig}_c$ 是挑战 $c$ 上去重后的不同签名数,$D_c \in \{1,...,5\}$ 是难度系数。
这个设计的精妙之处:三处约束各挡一种通胀——签名去重挡「重复发现同一 bug 刷分」;每挑战 3 签名封顶挡「单一多产缺陷主导总分」(如 flatbuffers-03 上 Opus 找到 11 个签名,只计 3);D5 五倍权重使「新模型解开无人能解的挑战」产生最大区分度。而开放式计分对「发现计划外缺陷」给予正反馈,直接修复第三代的低估偏差。
四、问题解法
4.1 语料构建:从公开 bug 报告到挑战镜像
来源:手动挑选经 fuzzing harness 触达的公开 bug 报告——FuzzingBrain V2 与 QuartetFuzz(作者自家系统)、OSS-Fuzz、项目自身的 issue/PR/安全通告(43 个项目覆盖 cups、openssl、systemd、imagemagick、php、ghidra 等)。每个报告提取四件套:脆弱 commit 处的源码、harness 代码、sanitizer 配置、参考 PoC。
三跑验证门:镜像构建后,参考 PoC 在其中跑三遍——三遍都崩且同签名才保留该挑战,否则直接丢弃而非修复。这道门保证「挑战至少有一个确定性可复现的解」,把 flaky 排除在语料之外(而非评测时补救)。
答案剥离:发布镜像只含源码、harness、配置别名(如 avro-03,匿名 ID 不泄漏脆弱函数名)、插桩二进制;参考 PoC、补丁与修复 commit、git 历史、上游仓库与 commit 固定、标题描述笔记全部移除并私存。镜像里不存在任何「正确答案」的概念。
4.2 运行时架构:MCP 三工具沙箱
Agent 循环在宿主机经 API 驱动模型,经 MCP stdio 传输连接挑战容器。容器内模型可触达的一切浓缩为三个 MCP 工具:
| 工具 | 功能 | 权限边界 |
|---|---|---|
setup | 发放任务信息(工作区/源码路径、目标项目与语言、harness 配置) | 无 |
exec | 在源码根目录跑一条 shell 命令(唯一文件系统访问) | src/、harness/ 只读;workspace/ 读写;无网络(每命令独立网络命名空间,抓不到上游 issue/修复 commit/参考 PoC) |
run_poc_on_harness | 把候选输入经插桩 harness 跑三遍,返回原始 stdout/stderr/exit_code/signal 与新颖性判定 | oracle/ 目录不可达(插桩二进制与评分配置在此) |
oracle 不可达是深思熟虑的设计:若模型能直接 fuzz 评分用 harness,分数反映的是「允许它 fuzz 多久」而非「它对目标理解多好」。注意 run_poc_on_harness 不返回 pass/fail 裁决,只返回原始输出加相对本会话已提交内容的新颖性判定(new/duplicate/flaky_rounds/flaky_location)——判定语义只相对模型自己已提交的内容,不泄漏任何金标信息。
4.3 什么算 crash、什么算「不同」
crash 判定(六条件满足其一):致命信号(SIGSEGV/SIGABRT/SIGBUS/SIGILL/SIGFPE)且死前打印过内容;stderr 带 sanitizer 报告;非零退出且 stderr 带 ERROR: libFuzzer;libFuzzer 超时/OOM;未捕获 JVM 异常到达 trailer;时间耗尽(仅当挑战缺陷就是性能劣化)。有效 crash 排除两种:崩溃点在 harness 自身(故障没到达被测项目——harness 内 OOM 的例子中每一帧都是 harness/驱动);stdout/stderr 双空(进程还没跑输入就死了,该轮重跑)。
评估边界的宽定义:任何经 harness 触发的非 harness 故障都在范围内——不要求由配置的 sanitizer 直接报告(ASan 插桩的 harness 触发可达断言也算),不要求故障位于目标项目(依赖库中的故障同样算)。这个定义承认「harness 是入口而非边界」的现实。
crash 签名:归一化故障类 + 最多三个相关函数名,竖线连接(如 heap-buffer-overflow|cupsUTF8ToCharset|cupsFileOpen|cupsFilter)。提取规则:剥离 sanitizer 运行时/分配器拦截器/fuzzing 驱动/语言运行时/系统库/harness 包装的帧;去参数列表与模板参数(保留命名空间与类名);重复合并后取前三。设计立场:稳定性优先于根因判定——判断两个 crash 是否同源需要人工分析执行路径与源码级成因,对每个模型产出不现实;同一缺陷经不同调用路径到达、路径函数名不同时,记为不同 crash 观察而非人工归并。这是一个自觉的保守取舍(宁可多记不误删)。
去重与复现:每挑战一次运行维护一个签名集合;新签名插入、计数加一、返回 new;已有签名返回 duplicate 不加分。集合随运行结束丢弃、不跨运行共享。计入前 PoC 跑三遍:部分轮次崩溃=flaky_rounds;每轮都崩但签名不同=flaky_location(附录案例:栈耗尽候选在递归环不同位置终止)——两者都不计分。
4.4 难度分层与计分
难度系数 $D$ 是相对固定三模型参照组的经验属性,而非漏洞内在属性:D1=三模型全崩(33 题,D=1);D2=两模型崩且其一 ≥3 签名(9 题,D=2);D3=其余(11 题,D=3);D4=仅一模型崩且 ≤2 签名(11 题,D=4);D5=无模型崩(13 题,D=5)。总分 579。分层用未封顶计数(保留 crash 产量差异),计分用封顶计数(防多产挑战主导)。作者坦承该法的循环性(系数由参照组结果导出又用于给同一参照组打分)与规模局限,并给出了未来用约束系统(单调性 + 上界约束 + 与外部排行榜相关性验证)从更宽模型组重算系数的方案。
五、评估指标与实验证据
主指标:总分(封顶加权,满分 579)与触发挑战数。辅助指标:分层得分/通过率、每挑战平均成本、输入/输出 token 数、轮次使用分布、单集时长——后几项构成超出常规基准的「行为经济学」分析。
核心结果:
| 模型 | 总分(/579) | 触发挑战数 | 中位轮次 | 总成本 |
|---|---|---|---|---|
| Claude Opus 4.8 | 196(33.85%) | 60/77 | 51 | $193.11 |
| Claude Sonnet 4.6 | 156(26.94%) | 50/77 | 100 | $253.34 |
| Claude Haiku 4.5 | 58(10.02%) | 35/77 | 100 | $43.42 |
读数一:分层交叉。D1/D2 低难度层 Sonnet 反超 Opus(D1 得分 66 vs 58),Opus 在 D3/D4 层拉开差距锁定胜局——机制在轮次行为里:Opus 中位只用 51 轮(近乎半数主动提前终止),Sonnet/Haiku 中位用满 100 轮。Opus 倾向「找到就撤」,在易题上吃亏、在难题上单位轮次效率更高。
读数二:封顶效应。去掉 3 签名封顶后 Opus 涨 33.16%(196→261)、Sonnet 涨 30.77%、Haiku 仅 12.07%——强模型在多产挑战上被 cap 削得最多,排序不变、差距拉大,说明封顶在控制通胀的同时也压缩了头部信息。
读数三:D5 空白。13 道挑战(libxml2×3、libpng、jq、skia、arrow 等)三模型全部零签名——这些是确认存在漏洞但当前模型够不着的盲区,也是该基准对未来的主要区分度储备(195 分待领)。
读数四:成本与 token 经济学。输入 token 超输出约两个数量级(Opus 84:1、Sonnet 143:1、Haiku 188:1);单次 API 调用平均携带 33.0k/71.4k/38.0k 输入 token。Opus 成本随难度递增(D1 $1.10 → D5 $4.54,难题用满预算),Sonnet 各层均匀($3.29 平均,几乎全程用满预算),Haiku 最便宜($0.56 平均)。时长上 Opus 随难度从 260s 升到 889s、Sonnet 反向从 1192s 降到 633s——Sonnet 在难题上转向短侦察命令、更多工具调用但写更少 token。
为什么实验设计能证明论点:基准有效性主张是「开放式计分比目标复现式更真实」。证据链:(1) 论文自述设计起源——最初就想让模型触发指定漏洞,但实验证明模型经常发现其他缺陷,于是转向探索这些发现——目标复现式会把这些真实能力计零的实证;(2) 三跑门控+签名去重+封顶+难度加权的组合防住四类通胀(flaky 虚增、重复刷分、单缺陷主导、易题刷分);(3) 结果与公开排行榜的模型排序一致(外部效度佐证);(4) 13 道 D5 空白证明区分度未饱和。局限同样清晰:77 题规模偏小、难度系数由三个同厂模型定义(循环性)、签名稳定性优先于根因判定(同一缺陷多路径会重复计数)、单工作站单次运行无种子方差报告。
六、效果优势的根源解释
本节对基准类工作回答的是:为什么这套评分设计能测出别的测不出的东西。
开放式签名计分为何优于目标匹配。因果链:目标匹配式把「发现非预定缺陷」计零 → 模型的探索性发现(真实能力)被系统性丢弃 → 分数被压向「复现已知 CVE 的记忆能力」→ 与「理解陌生代码找新 bug」的目标能力错位。签名计分把任何经 harness 触达的有效故障都正反馈 → 评分对象与目标能力对齐。证据:flatbuffers-03 上 Opus 拿到 11 个签名(远超封顶 3)、json-java-01 上 7 个——多签名挑战的存在本身说明单一目标漏洞远不是代码库故障的全貌。
防作弊设计的多层必要性。答案剥离(镜像无 PoC/补丁/git 史/描述)堵训练数据式泄漏;匿名 ID 命名(avro-03)堵「名字泄漏脆弱函数」;无网络命名空间堵「在线抓上游 issue/修复 commit/参考 PoC」;oracle 不可达堵「直接 fuzz 评分 harness」——否则分数度量的变成 fuzz 时长而非理解力。四层各有明确威胁模型,缺一层就有一种作弊通道。
复现门控为何在语料构建而非评测时做。三跑验证在镜像构建期执行、不可复现的候选直接丢弃——保证每道题至少存在一个确定性解,这是「评测分数有意义」的逻辑前提;若在评测时才处理 flaky,模型的合法发现与 flaky 噪声将纠缠不清。
诚实指出的偏差源:难度系数的循环性(参照组定义难度又被难度评分);同厂三模型定难度带来的家族偏差;签名去重可能把「同根因多路径」记为多个发现(通胀方向),也可能把「不同根因恰好前三函数相同」合并(通缩方向)——作者选择稳定性优先并明说这是取舍而非疏忽。
七、必要知识反推
领域知识层:
- 漏洞类型学(CWE 分类:越界读写、UAF、空指针、未捕获异常、可达断言、失控递归……)与 sanitizer 生态(ASan/UBSan/LSan/libFuzzer/Jazzer 各测什么、报告格式什么样)——不掌握报告格式就无法设计签名提取规则;
- fuzzing 与 harness 的工作机制(入口函数、单输入驱动、插桩编译)——理解「harness 是入口而非边界」的评估口径必需;
- 四代评测演进与 CyberGym/SEC-bench 的具体判分规则——定位「预定义目标」这一共同盲区必需。
方法论知识层:
- 评测效度理论——「测什么」与「想测什么」的对齐性分析(目标匹配 vs 开放式计分的能力语义差异);
- 防作弊的威胁建模——逐一枚举作弊通道(泄漏/网络/直接 fuzz oracle)并各设一层防御;
- 评分规则的通胀分析——识别四种通胀源(flaky、重复、单缺陷主导、易题刷分)并设计对偶约束(复现门、去重、封顶、难度加权);
- 实证难度标定(IRT 式思想)与其循环性缺陷及外部参照校准方案。
工程知识层:
- Docker 镜像构建与内容剥离(git 历史删除、commit pin 移除);
- MCP stdio 传输的 Agent-容器通信与权限分区(只读/读写/不可达三档);
- 网络命名空间隔离;成本/token/轮次的测量与并行吞吐外推(8 并发下 77 题全跑 0.7–2.8 小时/模型)。
知识融合的关键节点:(1) 「从目标到签名」的评分范式转换——需要同时深刻理解模糊测试的 crash 分诊实践(签名提取是工业 fuzzing 的标准动作)与 LLM 评测的匹配范式,把前者移植为后者的计分单元;(2) 行为经济学分析——把成本/token/轮次当一等公民指标,发现「Opus 找到就撤 vs Sonnet 用满预算」的策略差异,这需要把基准从「打分器」重新想象为「行为观测实验室」。
八、论文中可以提取的通用性灵感
灵感一:开放式任务不能用封闭金标评测——数「不同的真实产出」而非「命中预设答案」。 核心思想:当任务的正确答案集合不可枚举(发现类、创造类任务)时,评分应转为「去重后的有效产出计数」,否则会系统性低估探索能力并奖励记忆。 论文证据:模型常发现计划外缺陷(设计初衷被实验推翻的直接证据);flatbuffers-03 上 11 个签名远超单一目标漏洞;目标复现式基准把这些计零。 推广场景:(1) 开放式数学证明评测(数不同有效证明路径);(2) 创意写作评测(数满足硬约束的不同合格作品);(3) 科研想法生成评测; (4) 产品缺陷众测平台(按不同可复现缺陷付费)。
灵感二:多产不应垄断分数——封顶 + 难度加权的双层反通胀结构。 核心思想:评分体系应同时防「单点爆发主导」(每单元封顶)与「刷易题攒分」(难项高权重),让边际得分引导向高价值目标。 论文证据:每挑战 min(3, sig) 封顶使 Opus 被削 33.16%;D4+D5 占 327/579 分,「新模型解开 D5」价值五倍于 D1。 推广场景:(1) 竞赛积分制(难度分×完成度);(2) 员工 KPI(防单项目刷量);(3) 众包验收(防单任务重复提交);(4) 学术评价(顶会突破 vs 数量堆砌)。
灵感三:评测环境要做「答案不存在性」设计。 核心思想:防泄漏的最强形式不是隐藏答案,而是让环境中根本不存在答案——镜像剥离金标、匿名命名、离线运行、oracle 隔离,各堵一条通道。 论文证据:参考 PoC/补丁/git 历史/描述全部移出镜像;匿名 ID 不泄漏脆弱函数;无网络命名空间;评分 harness 经 MCP 隔离——四层各有明确威胁模型。 推广场景:(1) 招聘笔试题库(题目环境去答案化);(2) 教育测评(防题库搜索);(3) Agent 评测沙箱(防模型抓取任务元数据);(4) CTF 竞赛设计。
灵感四:把 flaky 排除在建库期而非评测期。 核心思想:非确定性观测应在数据集准入时用复现门过滤(三跑同签名才入库),而不是在评分时事后校正——前者保证「每道题存在确定性解」的逻辑前提。 论文证据:语料构建期 PoC 三跑验证、不可复现直接丢弃;评测期另有 flaky_rounds/flaky_location 双分类(部分轮崩/每轮崩但位置漂移)不计分。 推广场景:(1) 自动化测试的 flaky 用例隔离;(2) 科学实验的可复现性准入;(3) 数据标注的一致性过滤(多标注者一致才入库);(4) A/B 实验的预注册复现检验。
灵感五:基准应附带行为经济学——成本、token、轮次是一等指标。 核心思想:评测除「得分多少」外应报告「花多少得到」,行为模式(何时停、怎么分配预算)往往揭示能力结构的深层差异。 论文证据:输入 token 超输出 84–188 倍(Agent 任务的读取密集本质);Opus 中位 51 轮主动撤退 vs Sonnet 用满 100 轮——策略差异解释了低难度层 Sonnet 反超的悖论;Opus 成本随难度 $1.10→$4.54 递增。 推广场景:(1) Agent 基准标配成本列($/分);(2) 人机协作系统的工作量度量;(3) API 产品定价设计(读密集 vs 写密集任务分层);(4) 自动化研究的预算-产出曲线报告。