发布时间:2024 年 12 月 24 日
代码编写
Investigating Large Language Models for Code Vulnerability Detection: An Experimental Study
代码漏洞检测(CVD)对于解决和防范系统安全问题极为重要,在保障软件安全方面发挥着关键作用。以往基于学习的漏洞检测方法,要么依靠微调中等规模的序列模型,要么从头训练较小的神经网络。大型预训练语言模型(LLMs)的最新进展在包括代码理解和生成等各类代码智能任务中展现出了非凡能力。然而,LLMs 在检测代码漏洞方面的有效性在很大程度上还未被充分挖掘。本研究旨在通过为 CVD 任务微调 LLMs 来探究这一差距,涉及四个广泛使用的开源 LLMs。同时,我们还实现了其他五个先前的基于图或中等规模的序列模型以作对比。实验在五个常用的 CVD 数据集上开展,涵盖了短样本和长样本的部分。另外,我们进行了定量实验,以研究类别不平衡问题以及模型在不同长度样本上的表现,这些在以往的工作中鲜少被研究。为更好地服务社区,我们在 https://github.com/SakiRinn/LLM4CVD 和 https://huggingface.co/datasets/xuefen/VulResource 开源了本研究的所有代码和资源。
如遇无法添加,请+ vx: iamxxn886
- 现状:代码漏洞检测与LLM
在源代码中检测漏洞(Code vulnerability detection,CVD)对于保护软件应用免受潜在安全风险而言,极为重要。如今,软件中的漏洞数量持续攀升,自动化检测流程对于组织迅速响应并降低潜在风险愈发关键。
1.1 传统方法
传统方法主要借助动态执行代码程序并观察其输出 ,在模糊测试和符号执行技术的辅助下,分析代码漏洞是否存在。
1.2 基于深度学习的方法
近年来,基于深度学习的静态代码漏洞检测方法成为安全相关领域的突出研究方向。
通常仅分析代码内容,无需执行代码,从而降低了识别代码是否存在漏洞的成本。早期尝试包括训练基于图的模型或基于序列的模型。
- • 基于图的模型:以Devign为代表的基于图的模型,试图将源代码转化为代码图,把代码元素提取为图节点,并通过图表示学习来分析漏洞。
- • 基于序列的模型:以CodeBERT为代表的基于序列的模型,旨在将源代码视作令牌序列,并利用基于RNN或更先进的基于Transformer的预训练语言模型来捕获代码中的漏洞模式。
基于图的模型擅长捕捉代码的结构信息,但在捕获节点间的长距离关联方面存在困难,尤其是当代码内容增多时。但易受攻击的代码模式往往存在于长代码上下文中。因此,基于序列的模型来检测代码漏洞更加值得研究,特别是预训练语言模型。
1.3 基于大语言模型的方法
大型预训练语言模型(LLMs)作为更强大的预训练语言模型,在诸如机器翻译或代码生成等众多通用下游任务中取得显著成功。
然而,很少有研究探讨LLMs能否识别代码漏洞,特别是在CVD数据集上微调的LLMs。对于代码漏洞检测(CVD)任务,相关的代表性工作旨在固定模型权重并设计恰当的提示,以评估像ChatGPT这样的闭源LLMs和像Llama系列这样的开源LLMs的性能。最近的一项工作VulLLM首次尝试微调开源LLMs,但未对较长代码样本(> 512个tokens)进行评估,而中提到易受攻击的代码模式往往存在于这些较长的代码样本中。
- 训练方法
2.1 指令微调
指令微调的目的在于优化大型语言模型对特定指令的响应,以确保符合特定给定任务的要求。运用指令微调对大型语言模型进行调整,用于代码漏洞检测任务。通过将该指令与输入代码相结合,微调后的大型语言模型能够产生特定的输出。接着,大型语言模型会衡量生成的输出与预期目标之间的差距,并利用这一偏差来微调其权重。
作者采用了 Alpaca 所提供的模板。
微调方法采用了当前最为流行的轻量级方法-LoRA,对四个被评估的开源大型语言模型(即 Llama-2、CodeLlama、Llama-3 和 Llama-3.1)进行微调。
LoRA 的核心思路是冻结预训练模型的权重,并引入可训练的低秩矩阵作为额外的模型旁路分支。这些矩阵用于捕捉特定任务的适应性。有效减少了可训练参数的数量,加快了训练速度,同时让模型有效地适应特定领域的新任务。
2.2 训练数据集
2.2.1 CVD 数据集
在代码漏洞检测领域,已有一些先前的工作整理出了同时包含正负代码样本的数据集,选用了常用的 C/C++函数级数据集来开展实验。
- • ReVeal 借助 Chromium 安全问题和 Debian 安全跟踪器中已知的安全问题补丁进行标注。把安全补丁(提交)之前更改的函数视为易受攻击,补丁之后的视为不易受攻击,所有未更改的函数视为不易受攻击。
- • Devign 数据集涵盖了从两个大型且热门的 C 编程语言开源项目(即 QEMU 和 FFmpeg)的与安全相关的 Github 提交中提取的 27,318 个手动标注的易受攻击或不易受攻击的函数。Devign 属于高质量的标注,由三位安全专家标注,但手动标注成本极高,约耗费 600 个人工时。
- • Draper 数据集从三个静态分析器(Clang、Cppcheck 和 Flawfinder)中选取警报类别来生成标签。包含了从 SATE IV Juliet 测试套件、Debian Linux 和 GitHub 仓库中收集的数百万个 C/C++函数级示例,以及一些合成样本。所有样本均使用自定义的 C/C++词法分析器进行标准化处理,去除了诸如代码注释之类的冗余信息,并进行了去重以保障数据质量。
- • BigVul 从 348 个项目的常见漏洞和暴露(CVE)条目中收集漏洞修复提交,涵盖 91 种漏洞类型中的 3,754 个代码漏洞。BigVul 先是通过使用自动化工具对 GitHub 上的 C/C++项目进行初步搜索,检测可能与漏洞相关的提交。随后,这些提交通过错误报告进行交叉核对,并与 CVE 条目匹配。
- • DiverseVul 因其多样性而备受瞩目。从 797 个项目中收集了 7,514 个提交,涵盖多达 150 种 CWE 漏洞类型。其收集方法与 ReVeal 数据集类似,将函数的提交前版本标注为易受攻击,其余标注为良性,并使用函数的 MD5 哈希进行去重。最后,所有易受攻击的函数都手动映射到相应的 CVE 和 CWE 条目。
除了 Devign ,其他数据集均存在明显的类别不平衡现象。对 Draper、BigVul 和 diverseVul 中的部分样本进行了二次抽样。
2.2.2 数据集预处理
数据集是微调 LLM 的基石,不同的数据集会带来迥异的结果,是微调环节中最为关键的部分。
为了保证数据质量、数据正负样本配比比例,作者采用了如下预处理步骤:
- • 过滤(Filtering) :代码数据集的质量参差不齐,在数据预处理期间,在部分样本中发现了异常:
- • DiverseVul 数据集中,基于“项目”和“提交 ID”属性,无法追踪某些样本。
- • Draper 数据集中,注释不准确的情况颇为常见,特别是在与多个 CWE 类型相关的代码样本中,存在明显的标记错误。
- • 为解决此问题,对这两个数据集进行了初步过滤,以剔除异常样本。
• 格式化(Formatting) :样本的多样表示和存储格式不统一,所以对每个数据集予以格式化。为每个样本分配唯一索引,并运用“代码”和“标签”属性分别代表每个样本的代码和标签。保留了每个数据集特有的其他属性,如 CWE 类型和提交 ID,这有助于未来开展漏洞行提取和漏洞分类等工作。
• 按序列长度划分(Division by Sequence Length) :部分代码预训练模型,如 CodeBERT 和 UniXcoder ,包含可学习的位置编码,将输入序列长度限制在 512 个标记。若位置编码扩展超出此限制,就需要重新初始化扩展的编码,导致无法充分利用预训练参数。可能引发不可预测的性能下降。为应对此情况,将数据集划分为短样本和长样本,以 512 的序列长度为界。使用 Llama-3 分词器对所有数据集样本进行序列化,并计算序列长度,将每个数据集分为长样本和短样本子集,遵循 VulLLM 的做法。由此,每个数据集均有两个子集,其中一个子集包含长度小于 512 的短样本,另一个子集包含长度介于 512 至 1024 之间的长样本。长度超过 1024 个标记的样本因其长度变化过大(有些甚至超过 10K 个标记)而被排除。训练和推理这些超长样本的资源成本将难以承受。
• 子采样(Subsampling) :某些数据集的样本数量差异显著。Draper 中的样本数量是 ReVeal 的 50 多倍。过大的数据集会大幅增加训练成本,并造成不平衡,给模型引入隐性偏差。对数据集进行了子采样。对 Draper 、BigVul 和 DiverseVul 中的部分样本进行了子采样。由于每个数据集都被分为长样本和短样本子集,且短样本子集通常包含的样本数量远多于长样本子集,对短样本数据集进行了子采样,将样本的最大数量限定为 25,000。而后,对长样本数据集采用与短样本数据集相同的子采样比例。
所有数据预处理代码见:https://github.com/SakiRinn/LLM4CVD
- 效果评估
3.1 主要实验结果
发现一:所有LLM性能受数据集类别不平衡的影响 。所有模型在Devign数据集上的表现显著优于其他数据集,Devign也是正负样本数量近乎相等的最平衡数据集。相比之下,所有模型在DiverseVul数据集上的表现通常较差,该数据集的正样本数量最少。这两个数据集在召回率上的差异最为显著。
发现二:中等规模的序列模型在短样本数据集上表现卓越,总体优于LLM :在ReVeal、Draper和DiverseVul数据集上,两个中等规模的序列模型分别斩获最高和次高的F1分数。LLM与中等规模的序列模型之间存在明显的性能差异。尽管它们的精度大致相仿,但LLM的召回率显著低于中等规模的序列模型,致使F1分数较低。随着数据集愈发平衡,这种差距缩小,Devign数据集的差距最小。相较于LLM,中等规模的序列模型受正样本比例低的影响较小。
发现三:LLM在长样本数据集上具备出色表现的潜力 。由于参数规模的限制,多数CVD模型难以有效处理长样本。除LLM外,仅有2个所选模型能在长样本数据集上评估,且表现均远逊于LLM。LLM的大参数规模和长上下文窗口确保了其处理长样本的卓越能力。此外,对于所用的所有数据集,更多的漏洞样本为长样本,这能解释为何LLM在同一数据集中的长样本上通常比短样本表现更优。大量的漏洞样本有助于LLM学习漏洞特征。
发现四:LLM的FPR较低,使其比其他模型更可靠 。FPR(false positive rate)直接决定了漏洞检测工具的可靠性,过多的误报(FP)会阻碍开发人员在实际中使用该模型 。除ReVeal和DiverseVul数据集的长样本部分外,LLM的FPR远低于其他模型,且不影响整体性能。这种优势在短样本数据集上尤为突出。
3.2 不同正样本比率数据集的分析
CVD 数据集中的长尾分布可能给基于 LLMs 的漏洞检测解决方案带来挑战。所以,重新采样实验创建更均衡的数据集。
对 Draper 和 DiverseVul 数据集进行了二次采样,让正样本(即易受攻击的样本)在整个训练数据集中占比更高,比率逐步设为 10%、20%、30%、40%和 50%。和主要实验一样,每个采样数据集的规模控制在 25,000。选 CodeBERT 作为研究的中型序列模型,选 LLama-3.1 作为研究的 LLM。
当数据集中的正样本比率不低于 30%时,F1-Score 指标和召回指标的性能有显著提升。
较高的召回率意味着被预测为良性的易受攻击样本更少。反映出像 CodeBERT 这样的中型序列模型和像 Llama-3.1 这样的 LLMs 对类别不平衡较为敏感。因此,建议在更平衡的数据集上开展实验,这有助于这些模型取得更满意的性能。
3.3 不同样本长度数据集的分析
LLM 在同一数据集的长样本部分和短样本部分表现迥异。由于主实验中长样本部分和短样本部分的正样本比率不同,难以确切判定究竟是样本长度还是正样本比率为影响模型性能的关键因素 。
把样本长度划分为8个区间,范围为0至1024,步长是128,保证每个长度区间的样本数量相等。鉴于长样本的缺乏,无法从单个数据集中为每个长度区间进行抽样。于是,将全部5个数据集加以混合。混合之后,在混合数据集上抽样,为每个长度区间创建8个子集,每个子集包含10,000个样本且正样本比率为20%。
总体而言,样本长度对微调LLM存在一定影响。发现随着样本长度的增加,微调后的LLM的F1分数会降低,不过这种趋势不如正样本比率的影响显著。由此可以得出结论,正样本比率对微调LLM的影响远大于样本长度 。
- • 论文原文: https://arxiv.org/abs/2412.18260
- • 获取更多最新 Arxiv 论文更新: https://github.com/HuggingAGI/HuggingArxiv!
- • 加入社群,+v: iamxxn886
- • 点击公众号菜单加入讨论