AI辅助Linux内核漏洞审计:从候选发现到人工验证的工程实践

AI辅助Linux内核漏洞审计:从候选发现到人工验证的工程实践 大概两周前安全群里不少人转一条标题“AI发现53% Linux内核漏洞人类100%全遗漏了。”老实讲我看到这个标题的第一反应不是“AI真强”而是“这个数字是怎么算出来的”。因为只要真正接触过Linux内核安全的人都会知道内核漏洞的发现从来不是单一环节的事有人用fuzzing跑出崩溃有人靠代码审计看出毛病有人根据补丁回溯找回归还有人是在真实攻击事件里被迫定位。要把“人类全遗漏”说死需要一套边界极其明确的前提。标题里这个“53%”大概率来自某个特定评估给定一组历史漏洞样本模型在代码中定位到了其中约一半而在相同规则下人类专家没有对这些样本提交过报告于是得到“100%全遗漏”的结论。这种表述不能说错但很容易误导。它把一个“候选发现能力”的统计结果包装成了“人类全面落败”的结论。真正值得讨论的其实是一个更实际的问题AI进入内核漏洞审计流程之后我们原来熟悉的“人找代码”模式变成了怎样的“人机配合”模式以及作为工程师我该从哪一步开始把这种能力真正用起来。我的核心判断是AI不会让内核安全研究员失业它改变的是工作重心的分配。模型擅长在巨大代码库里快速生成“可疑候选”而人擅长的是判断候选是否是真的漏洞、影响面多大、该怎么修。这两者不是替代关系而是上下游关系。搞清楚这一点再看任何“AI又发现XXX漏洞”的标题你都会从容很多。下面我按实际使用中的认知顺序拆开讲。1. “AI发现53%漏洞”这类数据先看分母再看定义1.1 数字的“分母”决定它能不能横向比较任何关于“发现率”“命中率”的统计都先要回答一个问题分母是什么。如果分母来自“近两年新增的真实CVE”测试集AI能定位到一半以上这个水平确实不低如果分母是“模型训练数据里反复出现过的代码模式”那命中率高很多也不能说明泛化能力强如果分母本身是“人类还没有报告的样本”那人类自然无法命中——这是数据选择的必然结果不等于人类完全不行。更关键的是“发现”的定义。模型输出一个函数名算不算发现定位到具体行数算不算给出从入口到可疑点的完整调用链算不算明确给出修复方案算不算这些定义不同数字含义差异极大。所以我对这类新闻的读法很简单不追问“AI是不是真的比人强”而是追问“这个数字衡量的是哪个环节的能力”。1.2 “人类100%全遗漏”里通常藏着三个前提先说第一个没报告不等于没发现。内核安全研究员看到可疑代码后可能决定继续观察、等更多证据也可能已经把分析记在自己的笔记里只是没有对外公开。以公开报告为分母得到的“人类命中率”只能说明“人类没有公开命中”不能说明“人类没有发现”。第二个前提是时间窗口。Linux内核漏洞从引入到被发现、修复、公开CVE中间常常相隔数月甚至数年。如果测试数据集里的样本刚好落在“人类还没报告、模型却已经定位到”的窗口里用这个窗口对比人类和AI本质上是在对比“AI的速度”和“人类的公开进度”而不是“能力上限”。第三个前提是评估依赖人工标签。即使是AI定位实验最终判断“模型是否找对了”也需要人类专家对漏洞位置、根因和影响做标注。也就是说人类并没有在这条链路上完全退场只是换了一个位置。1.3 更稳妥的读法把它看成“候选发现能力”的指标所以我的建议是不要把这类数据当作“AI可以取代人”的证据而是把它当作“AI在候选发现环节有多大帮助”的参考。对做工程的人来说更值得关注的是另外四个指标误报率、定位精度、可解释性、跨版本稳定性。误报率高即使命中率是53%也意味着人工复核成本巨大定位精度不够模型只指出“某个文件可疑”帮助就不够明显可解释性弱就没法说服维护者去修跨版本不稳定换一个内核版本结论就可能失效。建议以后看到任何“AI发现X%漏洞”的新闻先问三个问题——测试集选了哪些样本样本时间跨度和公开状态是什么模型输出到什么粒度算“发现”问完这三个问题你基本就能判断这条新闻有多少信息量。2. AI在Linux内核漏洞审计里真正干的三类活2.1 不是“全知”而是三种相对具体的能力把“AI发现漏洞”这个大词拆开模型在内核审计里主要提供三类相对具体的能力。第一类是模式匹配。大量内核漏洞存在相似特征某个整数运算可能溢出、某个数组索引缺少边界校验、某个错误处理分支忘记释放已经获取的引用。模型可以从历史CVE和补丁中学习这些模式然后在新代码里找长尾变体。和传统规则引擎相比大模型的泛化能力更强能够跨函数、跨结构体、跨子系统识别“看起来眼熟”的片段。第二类是路径推荐。内核代码中一个函数是否危险往往取决于谁调用它、路径上是否经过不可信输入、是否持有锁、是否在中断上下文。模型可以结合函数调用关系给出“哪条路径更可能触发问题”的排序。这是从“找可疑函数”到“理解触发条件”的关键一步。第三类是语义解释。模型的输出不再是“第1024行有风险”这种干巴巴的提示而是可以附带解释为什么觉得这里有问题、需要在哪个前置条件下继续验证、修复时可能影响哪些行为。尽管解释可能出错但它降低了人工复核的启动成本。2.2 一个典型的AI辅助审计流程放到实际工作流里可以抽象成下面这个管道输入内核源码、历史CVE数据、函数调用图、相关文档 候选生成用静态分析和复杂性度量先筛出一个较小的候选集 模型筛选对候选集做切片让模型判断每个切片是否可疑 输出排序按可疑度、定位精度、解释质量排序 人工复核对高可疑候选做触发验证、影响面判断和修复这个流程的关键是“候选生成”和“模型筛选”分开。模型不应该从头开始看全量源码而是先由确定性工具把范围缩小再由模型做语义判断。这样既控制上下文需求也避免无效扫描。2.3 传统工具和模型不是竞争而是上下游很多人会问有了大模型还学Sparse、Smatch、Coccinelle这些工具干什么恰恰相反在一个可落地的审计流程里传统工具是模型的“前置过滤器”。环节工具擅长明显短板候选生成Sparse / Smatch / Clang Static Analyzer确定性规则、明显缺陷模式难以理解跨函数语义语义筛选大模型 / 代码模型长尾模式识别、可解释性幻觉、上下文受限、版本滞后最终判断安全研究员触发验证、影响面判断、修复决策时间有限逐行扫描不可持续传统规则工具擅长一个“是或否”的判断比如这个宏是否使用错误、这个锁是否匹配、这个数组是否越界。它不擅长回答“为什么这里值得怀疑”。大模型恰恰能补上这层语义联想但它不稳定会编造理由。两者放在同一个管道里规则工具降低干扰噪声模型提升语义覆盖最终判断还是交给懂目标子系统的人。3. 从“可疑点”到“真漏洞”中间隔了哪些工程问题3.1 模型找到可疑点不等于完成漏洞发现我见过不少人第一次跑完模型审计看到高可疑候选后兴奋地提交issue结果被维护者打回。问题往往不在“这个位置确实有风险”而在于没有完成“可触发性、影响范围、利用可行性”的证明。一个完整的漏洞发现至少要回答几个问题这条路径是否真的能走到可疑代码进入路径前是否满足前置条件需要什么权限影响是越界读、越界写、崩溃还是逻辑错误修复后会不会破坏现有行为这些都需要在具体内核版本上构建、运行复现和分析。模型给出的“可疑”只是起点。3.2 工程化落地时最容易踩的五个坑第一个坑是上下文窗口限制。内核里一个关键函数可能依赖很多兄弟函数和结构体把完整调用链塞进模型会很快超限。切片策略如果太粗暴模型看到的是“缺上下文的一句话”判断自然不靠谱。我会倾向于先切出函数本身、直接调用者、关键结构体定义再一起交给模型。第二个坑是版本漂移。内核主线每天都在变模型训练数据通常只覆盖到某个时间点。如果拿最新代码去跑模型可能完全不知道某个新增的加密锁、新机制或新API。落地前要先确认代码版本和模型知识之间的跨度必要时把相关commit信息也喂给模型。第三个坑是误报堆积。一个子系统跑下来模型可能给出几百条候选如果每条都要人工复核那原本为了提高效率的流程反而变成新的负担。所以要在提示词里限定输出粒度、要求排序并且设置一个候选上限。第四个坑是训练标注质量。历史漏洞补丁的位置标注并不统一有人标在问题函数有人标在修复函数有人标在struct定义。模型基于这些数据学习定位精度就会受影响。评估时要留出“相邻位置也算命中”的容差值。第五个坑是验证成本被低估。构建一个具体内核版本、配置相关子系统、准备触发环境和日志分析消耗的时间通常远超跑模型本身。很多团队误以为“AI挖到洞”等于“洞已经被确认”导致资源和预期不匹配。如果AI辅助审计效果明显下降我会按这个顺序排查先看输入切片是否漏掉关键调用关系、结构体定义、锁信息上下文够不够再看提示词任务范围是不是太宽是否要求了输出格式再看知识时差代码版本是否远新于模型训练数据再看候选生成层传统工具是否已经把明显问题滤掉模型是不是被大量无效候选淹没最后看验证环境是否因为构建配置、内核选项或模块依赖导致本来可疑的路径无法复现。提醒不要用“模型定位到了可疑函数”去替代完整的漏洞报告。至少要完成触发路径确认、权限边界分析和修复建议才值得提交给维护者或写进安全公告。3.3 哪些场景适合跑哪些不适合做一个相对清晰的边界。适合场景不适合场景维护者在合并补丁前的辅助代码审查在没有代码背景的情况下自动做最终裁决对某个子系统做历史缺陷模式专项扫描一次跑完全部源码的无目标扫描对新引入代码做回归安全检查在模型知识落后于当前代码版本太多时直接信任输出教学场景帮新人理解常见漏洞模式不经过人工验证就拿结论去提交CVE或发布公告4. 一个可以复制的AI辅助内核审计流程4.1 第一步先用传统工具建立基线我建议不要一上来就让大模型看源码。先做三件事确定审计范围比如最近改动密集的某个子系统。跑一遍传统静态分析工具保存基线结果。标记已知问题类型后续只需关注新增问题。这一步的价值是过滤掉大量确定性缺陷让模型的注意力放到更难的语义漏洞上。4.2 第二步把一个窄任务交给模型不要直接问“帮我找这个文件的漏洞”而是给一个具体岗位“这段函数是否可能存在整数溢出并导致数组索引越界”“这个错误处理分支是否可能在持有锁时提前返回”“这个改动的行为是否和旧版本不一致”把问题约束得越窄模型输出越容易收敛解释也越可信。一个提示词示例请分析下面这段内核函数。如果存在可疑的内存安全问题请按这个格式输出 可疑点文件路径:行号 问题类型整数溢出 / 越界读写 / 空指针 / 引用计数 / 竞态 触发条件需要什么权限和前置状态 影响范围最坏影响是什么 建议下一步验证需要什么环境或日志 如果无法确定请明确回答“无法判断”不要编造理由。这个示例是通用的审计提示重点是强制模型输出结构减少空泛回答。4.3 第三步用小样本验证不要直接批量拿近期已修复的几个CVE或补丁做测试集看模型能不能指到相关函数。这里的“相关”可以包括根因函数、修复函数、触发路径上的某个关键节点。如果命中率低于你的预期先优化提示词、切片范围和数据格式不要急着加算力。这一步经常被跳过但它是决定流程是否可信的关键。没有小样本验证你根本不知道模型的“53%”在你自己项目里会变成“30%”还是“70%”。4.4 第四步人工复核并形成闭环对每一条高可疑候选按“输入-路径-影响-可修复性”四步确认输入从哪里来是否外部可控路径能否实际走通中间有没有被检查卡住影响是崩溃、内存破坏、信息泄露还是逻辑异常修复是否影响其他模块是否最小可用确认后把误报案例和反馈记录到下一次审计的提示词里。比如模型经常把某个正常的引用计数模式误判为泄漏下一轮就在提示词里明确排除这种模式。这个闭环比单次命中率更重要。4.5 三层配合是更稳定的框架把整条流程收束成三层第一层确定性规则工具负责候选生成滤掉已知问题第二层模型负责语义筛选、路径推荐和解释第三层人负责验证、决策和修复。每层都有独立价值又依赖上一层减少负担。这个框架不限于Linux内核也适用于很多复杂项目的代码审计。关键在于不要试图让任何一层变成“万能层”。模型再强也不能在没有人工判断的情况下解释漏洞影响人工再强也不可能靠肉眼覆盖几百万行代码。5. 长期看AI改变的不是漏洞数量而是人的工作重心5.1 从“看代码”到“设计找法”过去一个内核安全研究员大部分时间在做什么读代码、跑实验、翻历史补丁、构造触发路径。这里面有大量工作本质上是重复的模式识别恰好是模型擅长的。当AI把这些重复环节接走之后研究员的价值会转移到更上游设计一个合理的审计方案、判断一个候选是不是真漏洞、评估漏洞的影响边界、决定修不修、怎么修。这和软件测试行业经历过的变化很像自动化测试没有让测试工程师消失而是让“测试开发工程师”和“测试架构师”成为更重要的角色。5.2 对普通开发者的启示即使你不做Linux内核安全这套思路也适用。日常代码提交之前可以先用传统工具做check再用模型对重点变更做语义审查。但模型的输出只是“同事建议”而不是“裁判结论”。最终合不合入代码仍然由人来决定。有意思的是越是在大模型时代阅读源码的能力反而越值钱。因为只有真读懂代码你才能判断模型给出的解释里哪些是合理的、哪些是强行自洽的。5.3 真正的效率红利和边界效率红利不在于“AI代替人发现了全部漏洞”而在于“原本只能人工抽样检查的地方现在可以大范围扫描”。覆盖面扩大了人的时间并没有增加于是更多精力可以集中在高价值判断上。边界也一直存在。模型会幻觉会因训练数据滞后而漏掉最新机制攻击者在研究同样代码库时也可能使用相似工具提高攻击效率。防御侧更早、更系统地把AI放进审计流程才是长期竞争点。别把AI当成“一键解决安全”的万能按钮它更像一个覆盖面很广但需要人类做终审的初级分析员。回到开头那位朋友的问题“AI会不会让内核安全研究没人做了”我的答案是不会。真正发生的是安全研究员不再需要像过去那样把自己埋在逐行代码里而是站在一个更高的位置去设计“找法”去验证“候选”去决定“修复”。标题里的53%也许确实存在但它衡量的更多是AI在“候选发现”这个环节的贡献。而人类的价值从来不是“有没有找到可疑点”而是“能不能把可疑点变成一次真正可以落地的修复”。