AI 把代码改完以后,之前的PoC无法复现。

这个漏洞算修好了吗?

马里兰大学的研究者给出的答案是:未必

他们构建的 PatchBench 基准在 213 道 C/C++ 漏洞修复任务中发现,只有 59.2% 通过了全部检查。

有些补丁这一次不再崩溃,却留下了其他触发路径;

有些补丁干脆删掉了出问题的功能,用阉割功能换来了测试通过。

崩溃消失≠漏洞修复

研究者用了 SEC-bench 的一个案例,说明单一检查会漏掉什么问题。

CVE-2022-1276这个漏洞出现在轻量级Ruby语言实现 mruby 中。

这个程序先由编译器打包参数,再由虚拟机读取参数。

编译器使用了错误的打包条件,导致虚拟机把未正确打包的数据当作数组读取,最终发生越界。

开发者修改了编译器里的打包条件。

Agent则在虚拟机崩溃的位置增加类型检查,拦住了原始样例引发的错误读取。

程序不再因这个样例崩溃,但编译器仍会错误地打包参数。其他参数读取路径依然可能越界,某些输入还会被错误处理。

Agent阻止了这一次错误执行,却没有消除造成错误的原因。

在 PatchBench 的实验中,AI还有更粗暴的做法。

研究者让安全修复系统 Buttercup 修复网络流量识别库 nDPI 中的一处漏洞。

Buttercup删除了用于提取TLS连接元数据的整块代码。

溢出随之消失,原始样例通过了,但对应的信息提取功能也被移除了。

补丁通过了崩溃检查,却以删除功能为代价。

测试一加码,半数补丁过不了关

为了识别这些补丁,研究者在原始样例之外增加了两类验证。

第一类检查安全性。他们通过模糊测试——自动生成和变异输入、寻找程序错误——收集同一漏洞的更多触发方式,再检查补丁能否阻止这些输入引发错误。

第二类检查功能。他们向程序输入正常数据,检查是否出现新错误、输出是否与参考修复版本一致,并运行项目单元测试。

这里使用参考修复版本,是因为有漏洞的程序也可能在不崩溃时产生错误结果。如果一味要求保留原程序的行为,就可能把错误输出当成标准。

研究者共测试了11种Agent配置。

Agent配置

原始PoC通过率

完整验证通过率

Codex + GPT-5.6 Sol

97.2%

59.2%

OpenHands + GPT-5.6 Sol

98.1%

58.2%

Claude Code + Claude Opus 4.8

97.7%

56.8%

通过率下降,是因为更严格的检查发现了原始样例没有暴露的问题。

输出检查的作用尤其明确:研究者仅移除输出状态检查,各配置的平均通过率就上升了。

也就是说,一部分补丁通过了其他检查,却仍然改变了正常输入的处理结果。

为什么这些漏洞,AI 修了却像没修?

构建PatchBench时,研究者刻意选择了开发者修复位置位于崩溃调用栈之外的漏洞。调用栈记录程序崩溃时正在执行的函数及其调用关系,能帮助定位报错现场,但不一定包含错误最初产生的位置。

这类任务要求Agent从崩溃位置向前追查:错误数据在哪里产生?哪个条件出了问题?应该修改哪一段逻辑?

只在报错位置加边界检查、类型检查或提前返回,可能挡住眼前的崩溃,却留下其他错误路径。

研究者还将历史漏洞移植到同一项目较新版本,并调整相关代码结构,以降低Agent直接复用历史补丁的可能性。

这一设计来自论文中的另一项调查。在SEC-bench上,三组Agent配置生成的补丁中,约25%与历史开发者补丁的相似性超过作者设定的阈值。

毕竟,这些漏洞大多来自 OSS-Fuzz / CVE,漏洞报告和开发者补丁本身就在互联网上,很可能已经进了模型的训练数据。

真正的修复,经得起不止一次测试

回到开头的问题:原始 PoC 跑通了,漏洞算修好吗?

PatchBench 给出的答案是,这只是第一步。

同一个漏洞的其他触发方式、被修改代码的原有功能,都需要被逐一验证。

即便是通过了全部检查的补丁,人工复查仍发现部分还是没有根除。

修好漏洞还有很长的路要走。

参考资料:https://arxiv.org/abs/2609.04075v1

声明:本文来自玄月调查小组,版权归作者所有。文章内容仅代表作者独立观点,不代表安全内参立场,转载目的在于传递更多信息。如有侵权,请联系 anquanneican@163.com。