引用计数机制是现代软件中一种常见的内存管理技术. 引用计数错误往往会导致内存泄露、释放后使用(use after free)等严重的安全问题. 现有致力于提高引用计数安全性的工作都依赖于对引用计数的字段进行识别. 然而, 由于类似于Linux等软件系统的代码十分复杂, 在代码中识别出引用计数字段是一项十分困难的工作. 传统的基于代码模式匹配的引用计数字段识别方法一方面存在需要专家经验总结规则, 人工开销大的问题; 另一方面存在总结的模式无法覆盖所有情况, 召回率较低等局限. 针对这些问题, 发现与字段有关的代码行为以及字段的名称可以用来表征这个字段的特征, 帮助识别引用计数字段. 基于这两个层面的特征, 设计了一种基于多模态深度学习的引用计数字段识别方法, 并面向Linux内核实现原型系统. 测试数据表明: 该原型系统的精确率、召回率分别为96.98%和93.54%, 而传统的基于代码模式匹配的方法没有识别出任何引用计数字段. 此外, 在Linux内核上发现61个引用计数字段使用不安全的数据类型, 并对其中21个向Linux内核社区提交数据类型转换补丁以提高引用计数字段的安全性, 其中6个已经被合并到Linux内核代码主分支.
Reference counting (refcount) is a common memory management technique in modern software. Refcount errors can often lead to severe memory errors such as memory leak, use-after-free, etc. Many efforts to harden refcount security rely on known refcount fields as their input. However, due to the complexity of software code, identifying refcount fields in source code is very challenging. Traditional methods of identifying refcount fields are mainly based on code pattern matching and have great limitations such as requiring expert experience to summarize patterns, which is a laborious job. Besides, the manually-summarized patterns do not cover all cases, resulting in a low recall. To address these issues, this studyproposes to characterize a field based on the field name and the code behaviour associated with the field; and designs a multimodal deep learning based approach. The study implements a prototype of the new approach for Linux kernel code. In the evaluation, the precision and recall achieved by the prototype system are 96.98% and 93.54%. In contrast, the traditional code-pattern-based identification method did not report any refcount fields on the testing set. In addition, sixty-one refcount fields are identified which are implemented with insecure data types in the latest Linux kernel. Until now, twenty-one of them are reported to the Linux community, of which six have been confirmed.
引用计数机制(reference count)作为一种常见的内存管理技术, 被广泛应用于现代软件开发中. 一方面, 由于C语言并不支持自动垃圾回收(garbage collection)或智能指针(smart pointer)这样的内存管理机制, 许多由C语言开发的重要程序, 如Linux内核、FreeBSD内核、Mozilla Firefox引入了引用计数机制来帮助程序员管理使用的内存; 另一方面, 如PHP、Python等支持内存自动回收的编程语言自身的内存管理就是基于引用计数机制实现的.
引用计数机制的核心想法是: 用一个整数来记录对当前内存对象的引用数量, 当被计数对象的引用计数为0时, 表明该对象的所有引用已不存在, 则可以将对象销毁. 由于引用计数机制在内存管理中扮演了重要角色, 因此引用计数机制相关的错误会对内存安全造成重大影响[
(1) 使用不安全的数据类型作为引用计数. 引用计数本质是一个有限长度的整数, 对整数进行运算面临着整数溢出的问题. 如果引用计数字段使用的数据类型本身的加减运算是没有进行整数溢出检查的, 那么当一个被计数对象的引用次数过多时, 就有可能造成引用计数的值溢出为0, 使得一个仍在被使用中的对象被释放, 从而造成释放后使用(use after free)等严重安全问题, 比如CVE-2014- 2851、CVE-2016-4558、CVE-2016-0728;
(2) 对引用计数字段进行错误操作. 在C语言中, 需要程序员手动维护引用计数的更新. 错误地对引用计数进行更新, 会使得被计数对象被提前释放或者延后释放, 造成内存泄露、释放后使用等严重的安全问题, 对内存安全造成重大的安全危害[
为了缓解这些安全问题, Reshetova等人[
这些致力于提升引用计数安全性的工作都需要将引用计数字段作为输入. 然而, 从程序中识别出引用计数字段本身就是一个充满挑战的问题. 这主要是由于现代软件开发日益复杂, 参与的开发人员众多, 不同开发人员的开发习惯有所不同. 对于引用计数字段, 在同一个软件中, 虽然其使用约定是固定的, 但对引用计数字段的具体操作和代码风格往往是不同的. 此外, 源代码中也缺少足够的注释来标注结构体中每个字段的具体用途.
目前对引用计数字段识别的方法主要有两种: 人工识别和基于代码模式匹配(code pattern matching)的识别. 部分现有工作[
针对现有方法的这些问题, 本文提出一种新的引用计数字段识别方法. 该方法不依靠专家经验和代码模式, 而是基于字段的关联代码行为和字段名称进行引用计数字段识别. 该方法基于两个重要的观察.
(1) 由于引用计数字段的目的是记录字段所在的内存对象(即被计数对象)的引用情况, 与其他类型字段相比, 对引用计数字段的操作与对字段所在的内存对象的操作行为密切相关. 比如, 对引用计数字段的增操作往往伴随着其所在对象的引用增加; 对引用计数字段的减操作总是伴随着其所在对象的引用减少. 因此, 可以提取对字段的直接操作以及与之相关内存对象的引用操作等代码行为作为识别引用计数字段的重要特征;
(2) 除代码行为外, 开发者对字段的命名有时也表达了字段的用途, 如表示引用计数字段的名称经常是reference count的某种缩写(比如ref、refcount、ref_count等), 表示长度的字段名称经常是length或其缩写(比如len等). 因此, 字段的名称也可以作为引用计数字段识别的一个重要特征.
本文提出的两个特征, 即字段的关联代码行为和字段名称, 相比于依据从固定代码片段总结出的代码模式, 能更灵活、更通用地表征与字段有关的业务逻辑和使用目的, 从而更好地识别引用计数字段.
考虑到Linux内核的重要性及其使用引用计数机制的普遍性, 本文面向Linux内核设计了一个基于多模态深度学习的引用计数字段自动化识别系统. 具体来说, 该系统首先通过静态程序分析, 提取待分析字段的关联行为和字段名, 作为一个字段的特征——本文称之为字段签名(signature), 用于后续判断是否是引用计数字段. 其次, 考虑到一个字段签名是由多个层面的信息(行为和字段名)构成, 本文引入深度学习中的多模态融合技术[
本文在5.9-rc1版本的Linux内核上进行了评估实验. 通过人工标注1 100个字段用于模型的训练和测试, 实验结果表明: 本文提出的系统在测试集上取得的精确率、召回率、
为了说明本工作对提升引用计数安全性研究的意义, 本文选取了一个后续任务——缓解使用不安全数据类型的引用计数字段, 并进行了实验. 基于本文提出的工具, 我们在最新版本的Linux内核(5.14-rc1)上识别出61个引用计数字段使用了不安全的数据类型. 这些字段面临引用计数溢出的风险[
本文的主要工作及贡献如下:
(1) 针对引用计数字段识别问题, 本文首次提出了基于多模态深度学习的引用计数字段识别方法. 该方法以字段的关联代码行为和字段名称为特征, 利用多模态深度学习在训练集上自动学习引用计数字段识别模型. 本文的识别方法一方面能够较好地反映字段的行为特点, 另一方面, 比人工规则匹配/代码模式匹配方法更全面而高效;
(2) 本文面向Linux内核开发引用计数字段识别系统并进行了评估实验. 该系统在测试集上取得的精确率、召回率、
(3) 基于本文设计的引用计数字段识别系统, 我们在最新版本的Linux内核代码中发现61个使用了不安全数据类型的引用计数字段. 为了缓解这些引用计数字段面临的安全风险, 本文对其中21个进行了修补, 截至目前, Linux社区已经接受了我们提交的6个安全补丁.
本文第1节对引用计数机制、命名实体识别、多模态深度学习等背景知识进行介绍. 第2节阐述本文提出的引用计数字段识别方法的设计思路和工作流程. 第3节对本文提出的方案进行实验评估并分析实验结果. 第4节介绍引用计数字段识别和安全性检测的相关工作. 第5节对本文的内容进行总结, 并展望后续的工作方向.
引用计数本质上是一个记录资源对象引用的整数, 当一个结构体对象的引用增加时, 引用计数加1; 当其引用减少时, 引用计数减1; 当对象的引用计数为0时, 表明该对象的所有引用已不存在, 应该销毁这个对象, 释放该对象所占用的内存. 在Linux系统中, 引用计数器被广泛用于各种子系统来管理各种各样的资源, 如动态分配的内存块[
根据Linux内核文档[
除了数据类型, Linux内核还提供了原始的API接口来对引用计数字段进行操作. 根据Linux开发者手册[
kref类型API的使用
命名实体识别(named entity recognition, NER), 是指识别文本中表示命名实体的部分, 是信息提取、句法分析、机器翻译等众多自然语言处理任务的基础工具. 字段识别任务也属于命名实体识别任务的范畴, 使用的方法和其他命名实体识别任务使用的方法具有很强的关联性. 现有的命名实体识别技术主要采取以下3种类型的方法.
(1) 基于规则的方法: 早期的命名实体识别研究主要依赖人工构建有限规则, 之后, 从文本中匹配符合规则的字符串作为命名实体. 之后, 一些研究者尝试利用算法自动地发现和生成规则, 比较著名的有Collins和Singer[
(2) 基于传统机器学习的方法: 随着机器学习在自然语言处理领域的兴起, 许多研究者将机器学习中的常用方法和模型应用于命名实体识别任务中. Szarvas等人[
(3) 基于深度学习的方法: 近年来, 深度学习成为了机器学习的新潮流. 深度学习将词向量作为特征, 来表示词语的含义. 这种表示方法在诸多自然语言处理中取得了显著成效, 并具有很强的普适性.受此启发, 有许多研究将深度学习技术应用到命名实体识别任务中. Collobert等人[
本文受到基于深度学习进行命名实体识别等工作的启发, 设计了基于多模态深度学习的方法来识别引用计数字段, 与传统的基于代码模式匹配的方法相比, 取得了更好的识别效果.
多模态深度学习(multimodal deep learning)指的是将多种模态的信息进行转换和融合, 建立统一的深度学习模型表示, 从而能够处理和关联多种模态的信息. 传统的单模态深度学习利用应用任务(如情绪分析、视频分类任务等)中单一的信息源提取信息的高维表示来完成模型学习, 而多模态深度学习关注应用任务存在多个信息来源的情况, 将多个信息要素进行融合来提升模型的表现. 这种融合技术称之为多模态融合技术. 按照融合方法的演化历程划分, 有以下3类融合方法.
(1) 基于特征的融合方法: 早期融合方法都是基于特征的, 其从每种模态提取了特征表示后立即进行融合. 此外, 由于部分深度学习计数会涉及到从原始数据中学习特征的表示, 有时也需要在未提取特征之前就对原始数据直接进行融合. 在多模态学习场景中, 模态之间是高度相关的, 但这种相关性很难在特征或数据层面被捕获到. Hinton和Salakhutdinov[
(2) 基于决策的融合方法: 为了克服不同数据在低维度关联性弱的问题, 后续的融合技术利用深度学习模型分别处理不同的数据源, 得到各个不同的高维表示之后再进行融合, 也称为晚期融合方法. 晚期融合方法主要采用规则来组合不同模型输出的结果, 即决策融合. Kahou等人[
(3) 混合融合方法: 混合融合方法综合了前两种融合方法的优点, 可以灵活地调整模型的结构来处理复杂的多模态问题, 因此被广泛地应用在多媒体、视觉问答等领域[
在本文的场景中, 要处理的信息主要来自代码行为和字段名称两个维度, 两个维度原始数据的关联性较弱, 不适用于早期融合方法. 因此, 本文使用了晚期融合方法来对这两个维度的信息进行融合.
本节首先介绍我们提出的引用计数字段识别方案的核心思路和设计上的考虑, 其次介绍方案的总体识别流程, 以及识别流程中的4个主要步骤.
引用计数字段识别任务的核心问题在于, 应该提取何种信息作为字段特征用于识别任务. 对于该问题, 本文有以下两个观察.
● 观察1: 字段关联的代码行为对引用计数字段的识别非常重要.
由于引用计数字段用于内存对象的管理, 与引用计数字段相关联的代码包含两部分: 对字段操作的相关代码; 被计数对象的引用相关代码.
引用计数字段行为示例
(1) 对引用计数字段的操作由被计数对象引用的行为(尤其是引用逃逸行为)决定. 如
(2) 对引用计数字段的操作决定引用计数对象的行为. 如
此外, 值得一提的是: 开发者可能会编写引用计数相关的包装函数, 通过包装函数来对引用计数操作来进行操作, 如
● 观察2: 字段的名称也在一定程度上包含这个字段的使用目的, 可以用于引用计数字段的识别.
软件开发者在开发时为了方便其他开发人员理解代码的语义以进行后续的开发, 在命名结构体的字段时, 往往会使用带有实际含义的单词或是单词的缩写来表达字段在程序中的作用, 这也可以帮助我们识别引用计数字段. 如
综合以上两个观察, 本文提出以字段的关联代码行为和字段名称两个维度的信息为特征来表征一个字段, 称为字段签名(signature), 并通过分析字段签名来识别引用计数字段. 除此以外, 本文在方案设计时还有两点重要考虑, 以弥补之前工作的局限性.
(1) 综合考虑字段所有关联代码行为而不是根据单一代码片段作为识别标准: 对于一个待识别字段, 传统的基于代码模式匹配的方法认为只要有一处代码片段符合引用计数字段的代码特征就认为该字段是引用计数字段, 这种孤立的识别方法可能会存在一定的误报; 而本文则是将对一个字段的所有关联代码行为进行分析和合并, 根据合并后的行为特征进行识别, 是一种更为全面的识别方法. 具体来说, 本文以函数为粒度对字段行为进行分析, 对每个操作了该字段的函数进行逐一分析, 将一个函数内的关联代码行为序列作为一条记录, 最终将所有的记录合并构成字段的行为特征;
(2) 基于深度学习来学习规则而不是基于人工经验总结规则: 已有的识别方法都需要基于专家经验来总结识别规则, 这种基于人工的方法不但开销大, 而且无法覆盖所有的使用情况; 为了解决这一问题, 本文引入了深度学习技术, 通过在标注数据上进行训练, 自动学习引用计数字段的签名的特征, 具有较好的普适性. 具体来说, 本文针对上文提出的字段签名, 设计了一种基于多模态深度学习技术的多模态神经网络, 可以将关联代码行为和字段名称两个维度的特征融合起来, 再由分类模型完成最后的识别任务.
基于第2.1节的设计思路, 本文提出了基于多模态深度学习的引用计数字段识别系统, 该系统的流程如
基于多模态深度学习的引用计数字段识别系统
整个系统共包括4个阶段.
(1) 预处理阶段: 分析内核代码, 构建函数调用图(call graph); 遍历内核中所有的结构体, 根据数据类型收集潜在的引用计数字段及字段名称;
(2) 关联行为的识别与提取: 对于每一个潜在的引用计数字段, 定位其在内核中的所有相关代码, 通过程序分析, 定位关联代码行为, 并提取关联代码行为对应的源代码;
(3) 字段签名的生成: 对关联行为对应的源代码进行文本处理(包括分词等), 并将一个字段所有的关联行为代码进行合并, 结合字段名称构造该字段的签名;
(4) 分类模型的训练与预测: 搭建基于多模态深度学习的二分类神经网络, 对字段签名进行向量化作为模型的输入, 在标注数据集上进行训练, 得到最终的引用计数字段识别模型.
在该阶段, 系统对内核代码进行初步的分析, 收集所有潜在的引用计数字段作为后续分析的对象; 同时生成函数调用图用于后续的行为分析和提取. 根据第1.1节, Linux内核中共有5种数据类型(atomic_t, atomic_ long_t, atomic64_t, kref和refcount_t)用于引用计数字段, 因此系统扫描内核代码中所有结构体定义, 将符合这5种数据类型的字段作为潜在的引用计数字段, 并记录这些字段的字段名称. 在构建函数调用图时, 本系统识别出所有的直接调用(direct call), 在调用函数(caller)和被调用函数(callee)之间添加调用关系. 对于间接调用(indirect call), 本文暂时不作处理并将其作为后续工作的改进方向.
基于第2.1节所述, 字段关联代码行为包含了两部分: (1) 对字段进行操作的代码; (2) 被计数对象的引用相关的代码. 第1种代码行为主要包含对字段的增删读写. 对于第2种代码行为, 本文主要考虑第2.1节所述的两种关联情况: 被计数对象的引用行为决定引用计数操作; 引用计数操作决定被计数对象的引用行为. 为了捕获第1种情况, 本文分析并识别被计数对象的引用逃逸行为. 为了捕获第2种关联情况, 本文首先判断是否将对字段的操作的返回值作为if条件; 其次, 如果存在这样的判断, 后续对被计数对象进行了何种操作, 尤其是否将其传入了其他函数做进一步操作. 综上, 本文识别并提取以下4类关联行为.
(1) 对字段的增减、读写操作;
(2) 将字段操作的返回值作为if语句的条件;
(3) 被计数对象的引用被传递到函数外部, 包括通过return语句传递给caller, 或是通过赋值语句传递给外部的全局变量;
(4) 对于行为(2)中的if语句, 当条件为真时, 对被计数对象的操作.
对于预处理阶段收集到的每一个潜在引用计数字段, 系统通过静态分析, 收集所有对该目标字段的操作, 然后对上述4种关联行为进行识别和提取. 如第1.1节所述, Linux内核一般通过相应的API来操作atomic_t、atomic_long_t、atomic64_t、kref和refcount_t这5种数据类型的字段. 根据这一特点, 系统首先识别出代码中所有相关的API函数调用; 然后通过分析API操作的对象, 来收集所有对于该引用计数字段的操作. 除此之外, 由于内核代码的模块化, 有许多模块都基于原始的API进行了包装, 然后在模块内使用包装后的函数进行操作. 本文将这些包装函数也作为是对待分析字段的直接操作.
完成收集后, 对于每一个包含对目标字段操作的函数, 系统进行函数粒度的分析, 提取上述4类关联行为, 将行为序列作为对该函数的分析结果. 具体而言, 对于第(1)类行为, 系统根据对字段的API操作函数名, 可以直接识别出对应的操作类型(如atomic_read为读操作), 并保留出其中的增、减、读、写操作; 对于第(2)类行为, 系统对函数的指令序列进行扫描, 提取出其中所有的if判断, 并检查其判断的条件是否与引用计数字段有关, 从而提取出与引用计数字段有关的if判断.
完成了与引用计数字段相关的行为提取后, 系统进一步提取与被计数对象引用传递情况相关的代码行为. 首先通过静态分析, 系统定位出目标字段所属内存对象(即被计数对象); 然后进行函数内的use-define分析, 定位出所有对被计数内存对象的使用操作; 然后遍历这些使用操作, 识别并提取出第(3)类、第(4)类行为.具体而言, 对于第(3)类行为, 系统检查使用操作是否为return操作, 或是对全局变量的赋值; 对于第(4)类行为, 系统定位所有将被计数对象作为参数传递给其他函数的操作, 定位到具体的函数调用位置, 并检查该函数调用所在的基本块的执行是否是由一个与目标字段相关的if条件决定的(如
根据第2.1节所述, 本文使用字段名称和字段关联代码行为表征字段特征, 称为字段签名, 并用于后续的引用计数字段识别.
字段签名示例
(1) 文本处理: 为了使第2.4节中提取到的代码更适合于作为深度学习模型的输入, 需要进行两步文本处理: 去除特殊符号和分词.
➢ 首先, 与自然语言文本不同, 代码中包含大量的特殊符号, 如“; [{”等. 与带有操作含义的“+−”号等操作符不同, “{[”这些符号并没有特殊的含义, 因此本系统会直接将这些符号删去, 替换为空格符. 而“; ”在代码中起到分隔语句的作用, 表明语句的终止, 因此需要保留;
➢ 其次, 由于代码中大量的函数名和变量名是由下划线连接的多个词组成, 这些词应该被分开理解. 因此, 系统根据下划线对这些函数名和变量名进行划分. 如
多模态神经网络结构
(2) 合并为向量: 第2.4节对每一个操作了待分析字段的函数进行了单独的分析, 分别提取出关联行为代码. 为了综合考虑一个字段的所有关联行为, 需要将这些行为代码合并起来. 如
经过处理后的关联代码行为特征向量和字段名称的二元组, 构成字段的签名.
考虑到本文提出的字段签名实际包含字段名称和字段关联代码行为两个层面的信息: 字段名称更多反映的是自然语言文本上的特征, 而字段关联代码行为反映的是代码的特征. 由于两者表示的信息维度是不同的, 单一的编码和模型难以同时在两种维度的信息上取得较好的效果. 基于这一考虑, 本文采用了多模态融合神经网络技术[
具体来说, 本文设计的神经网络结构如
本节以5.9-rc1版本的Linux内核代码为目标, 通过一系列实验, 评估本文提出方法在识别引用计数字段的有效性. 本节首先介绍了实验数据集的标定以及实验的主要评估指标(第3.1节), 然后围绕以下5个问题开展实验验证, 并与相关方法进行对比(第3.2节−第3.6节).
● RQ1: 模型的识别阈值
● RQ2: 本文提出的方法识别效果如何, 是否优于现有方法?
● RQ3: 本文提出的方法是否存在过拟合问题?
● RQ4: 本文提出的方法在训练集以外的真实程序数据上效果如何? 是否与测试集上的效果一致?
● RQ5: 本文提出的方法如何帮助提升Linux内核的引用计数安全?
系统在预处理阶段(第2.3节)从内核中提取到的待识别的引用计数字段共有3 409条, 其具体的数据类型分布见
各类型的字段在数据集中的分布情况
数据类型 | 数量 |
atomic_t | 2 101 |
atomic64_t | 376 |
atomic_long_t | 164 |
kref | 436 |
refcount_t | 332 |
合计 | 3 409 |
(1) 开发者注释中明确表明该字段为引用计数, 这些包括结构体前的注释、字段旁的注释以及其调用函数前面的注释, 其中明确出现“reference count”“refcount”等字样表明该字段为引用计数;
(2) 对于直接的字段名为“refcount”“ref_count”的字段认定为引用计数;
(3) 通过其调用函数的代码进行分析, 当涉及该字段的操作时, 结构体本身是否发生了变化, 当该字段加1时, 结构体是否建立了一个新的引用; 当该字段减1时, 结构体是否减少了一个引用; 当该字段减为0时, 结构体是否正确地被释放.
在随机抽取的800个字段中, 经过人工标注最终有552个字段为非引用计数字段, 248个字段为引用计数字段. 由于正样本的数量大大少于负样本的数量, 考虑到非平衡的数据集可能导致模型学习效果较差, 本文又通过人工分析字段补充了一些正样本(增加了300个引用计数字段). 最终构建的实验数据集中, 共包括1 100条人工标注的字段. 对于人工标注的数据集, 我们采取随机抽取的方法以7:3的比例构建训练集和测试集, 得到的训练集大小为770, 测试集大小为330.
对引用计数字段的识别相当于一个二分类任务, 因此我们采用准确率、精确率、召回率和
如第2.6节所述, 深度学习模型最终会输出一个字段属于引用计数字段的概率, 如果该概率大于阈值
实验结果见
不同阈值的实验结果对比
阈值 |
FP | FN | 准确率(%) | 精确率(%) | 召回率(%) | |
0.9 | 0.40 | 22.60 | 93.03 | 99.73 | 86.22 | 92.44 |
0.8 | 2.20 | 15.80 | 94.55 | 98.54 | 90.37 | 94.25 |
0.7 | 4.80 | 10.60 | 95.33 | 96.98 | 93.54 | 95.21 |
0.6 | 8.40 | 8.00 | 95.03 | 94.93 | 95.12 | 95.01 |
0.5 | 11.60 | 6.20 | 94.61 | 93.24 | 96.22 | 94.69 |
0.4 | 15.80 | 4.20 | 93.94 | 91.16 | 97.44 | 94.15 |
0.3 | 24.20 | 3.00 | 91.76 | 87.23 | 98.17 | 92.29 |
0.2 | 34.00 | 1.60 | 89.21 | 83.17 | 99.02 | 90.26 |
0.1 | 49.60 | 1.00 | 84.67 | 77.31 | 99.39 | 86.78 |
为了评估本文提出方法的有效性, 本文还与现有的基于代码模式匹配的识别方法[
实验评估结果见
本文提出的方法与代码模式匹配方法的效果对比
类型 | 本文的方法 | 代码模式匹配方法*** | ||||||
准确率(%) | 精确率(%) | 召回率(%) | 准确率(%) | 精确率(%) | 召回率(%) | |||
注释: ***. 基于代码模式匹配的识别方法只支持识别atomic_t类型的引用计数字段; 在计算合计的指标时, 本文认为该方法对于其他类型的字段都认为不是引用计数字段; |
||||||||
atomic_t | 96.98 | 70.21 | 82.50 | 75.86 | 94.24 | N/A**** | 0 | 0 |
atomic64_t***** | 100 | N/A | N/A | N/A | − | − | − | − |
atomic_long_t | 73.58 | 23.08 | 30.00 | 26.09 | − | − | − | − |
kref | 96.94 | 100 | 96.94 | 98.45 | − | − | − | − |
refcount_t | 92.46 | 100 | 92.46 | 96.08 | − | − | − | − |
合计 | 95.33 | 96.98 | 93.54 | 95.21 | 50.30 | N/A | 0 | 0 |
本文使用多模态神经网络结构构建模型, 训练集大小为770个, 由于训练集较小, 可能会存在过拟合现象. 虽然第3.2节、第3.3节的实验结果表明, 模型在测试集上已经取得了不错的效果(
将随机失活、批标准化和正则化这3种缓解过拟合的技术分别应用于我们的基础模型中, 构建了3个新的模型(称为基础模型+Dropout、基础模型+Batch Normalization、基础模型+Regularization). 基于这3个新的模型, 我们使用与第3.2节相同的训练集和测试集进行训练, 并取阈值
不同模型的实验结果对比
模型 | FP | FN | 准确率(%) | 精确率(%) | 召回率(%) | |
基础模型 | 4.8 | 10.6 | 95.33 | 96.98 | 93.54 | 95.21 |
基础模型+Dropout | 3.5 | 15.4 | 94.27 | 97.78 | 90.89 | 94.21 |
基础模型+Batch Normalization | 10.9 | 4.1 | 95.45 | 94.24 | 97.57 | 95.74 |
基础模型+Regularization | 2.0 | 14.0 | 95.15 | 98.72 | 91.71 | 95.09 |
系统在5.9-rc1版本的Linux内核代码中提取到的待识别的引用计数字段共有3 409条, 其中有1 100条被标注的数据被用于训练和测试(第3.1节), 剩余2 309个未标注字段. 本文将最终获得的模型(第3.2节)应用于这2 309个字段进行引用计数识别. 结果见
在Linux 5.9-rc1上识别出的引用计数字段的情况
类型 | 被标注的数据集中引用计数字段的数量 | 无标注的数据集中, 工具识别的引用计数字段的数量 | 内核中该类型字段总数 | 总数据集中正类的比例(列2+列3)/列4(%) |
atomic_t | 28 | 211 | 2 101 | 11.38 |
atomic64_t | 0 | 8 | 376 | 2.13 |
atomic_long_t | 4 | 7 | 164 | 6.71 |
kref | 296 | 137 | 436 | 99.31 |
refcount_t | 220 | 105 | 332 | 97.89 |
合计 | 548 | 468 | 3 409 | 29.80 |
为了验证模型在无标注数据集上的准确性, 对于每一种数据类型识别出的正样本和负样本, 本文各随机挑选了最多30条数据进行人工验证, 共分析101条正样本数据和97条负样本数据. 经过人工分析, 发现存在9条漏报和13条误报, 表明本文的工具在未标记数据集上也取得了良好的效果.
如
为了探讨本工作识别出的引用计数字段如何帮助提升Linux内核中引用计数的安全性, 本文尝试缓解使用不安全数据类型的引用计数字段这一安全问题. 如第1.1节所述, 使用atomic_t类型进行引用计数存在安全风险, 而refcount_t类型因为对运算操作添加了较好的安全检查, 因此被认为不存在加法溢出的问题, 更加适用于引用计数字段类型[
在第3.5节, 本文在5.9-rc1版本的Linux内核代码中一共识别到了239个atomic_t类型的引用计数字段.本文随机分析了其中的94个字段, 其中有4个字段在最新版的内核代码上不存在, 有10个在最新版本的Linux内核上已经被开发者修改为refcount_t类型, 有19个属于工具的误报. 需要注意的是, 本实验的检测对象是atomit_t类型的引用计数字段, 该实验中的精确率79% (75/94)甚至优于测试集上atomic_t类型的精确率(70.21%, 见
本文的相关工作主要有两方面: 引用计数字段的识别和检测引用计数缺陷.
致力于提升引用计数安全性的工作都需要将引用计数字段作为输入, 它们大多依赖人工识别出引用计数字段或是操作引用计数的API[
已有研究主要关注两方面的引用计数缺陷问题.
● 一是缓解使用不安全的数据类型作为引用计数. Reshetova[
● 二是检测错误的引用计数操作. Li和Tan等人提出了Pungi[
以上这些工作都以引用计数字段作为研究对象, 本文提出的引用计数字段识别方法, 为这些工作奠定了基础.
本节将对本文方案设计中的一些重要问题进行讨论.
正如本文最初部分和第4.1节所说, 已有的引用计数字段识别工作主要依赖专家知识构造高度定制化的规则来识别引用计数字段. 这种方法一方面需要人工地对代码进行大量审计, 另一方面总结的规则可能存在遗漏导致漏报. 针对这一问题, 本文提出一种基于机器学习的引用计数字段识别方案, 通过在标注的数据集上进行模型训练, 自动学习引用计数字段的特点. 如第2.4节所述, 本文主要考虑两类引用计数字段特征: (1) 字段的名称; (2) 字段关联的代码行为. 根据引用计数机制的设计, 引用计数字段的这两类特征明显区别于普通内核字段, 可以帮助模型有效识别引用计数字段. 然而, 可能还存在其他类型的特征可以帮助本方案取得更好的引用计数字段识别效果, 包括其他类型的使用这两类特征的方式, 本文将在后续工作进一步探索.
在引用计数字段识别任务中, 很难构造出一个规模较大的数据集用于模型的训练. 通常来说, 在数据量较小时, 往往使用传统的机器学习方法, 如SVM等; 使用深度神经网络可能遇到过拟合问题. 然而, 在本文的设计方案中, 直接使用传统机器学习方法存在两方面的问题: 首先, 如第2.6节所述, 提取出的字段行为和字段名称都需要通过词嵌入将其向量化作为模型输入, 为了更好地得到两者的向量化表示, 在训练过程中需要通过后续网络的反馈来更新使用的词嵌入模型, 使用深度神经网络更合适; 其次, 第2.4节提取出的关联行为特征是不定长的, 不定长的数据通常需要使用长短期记忆网络等深度学习模型进行处理. 基于上述原因, 本文使用了深度神经网络来构建模型. 第3节的一系列实验结果表明: 本文设计的模型取得了不错的效果, 同时并没有出现明显的过拟合问题.
此外, 针对如何更好地表征不定长的文本信息, 除了使用长短期记忆网络模型这一传统思路外, 近期学术界还有许多新进展. 许多研究提出了更为复杂的深度神经网络模型, 如ELMo[
本文在引用计数字段识别模型中, 使用字段名称和字段关联代码行为两种不同维度的特征. 为了将两种特征融合在一个模型中, 本文使用多模态深度学习模型. 如第1.3节所述, 基于决策融合方法的多模态深度学习模型需要对不同层面的特征作进一步处理, 得到其高维特征向量表示后再进行融合. 因此, 对于行为特征, 我们首先使用词嵌入将其向量化, 之后针对其不定长的特点, 使用长短期记忆网络进行处理; 对于字段名称特征, 同样先使用词嵌入将其向量化, 之后为了捕获其字母间的关系, 本文使用了卷积神经网络进行处理; 将获得的高维向量表示合并后, 我们使用全连接神经网络作为分类器, 使神经网络的反馈向前传导, 帮助调整词嵌入网络的权重. 可以看出, 本文的模型设计与选用的特征是高度耦合的. 后续如果考虑其他维度的特征, 或者单独使用一种特征进行引用计数字段识别, 都需要对原有的模型结构进行较大的调整. 同时, 本文提出的将特征空间与模型结构相结合的设计, 帮助我们在内核引用计数字段识别任务上取得了较好的效果.
针对Linux内核中引用计数字段的识别问题, 本文工作提出了基于多模态深度学习的引用计数字段识别方法. 与传统的通过代码模式匹配的方法不同, 本文方法以字段的关联代码行为和字段名称作为引用计数字段的识别特征, 从而显著提升了识别效果. 此外, 本文使用深度学习技术, 从标注样本中自动学习引用计数字段的特征, 而不用基于专家经验总结识别规则, 可以识别更广泛的引用计数字段.
本文在5.9-rc1版本的Linux内核上进行了评估实验. 在测试集上, 本文方法的精确率、召回率、
本文由“系统软件安全”专题特约编辑杨珉教授、张超副教授、宋富副教授、张源副教授推荐.
Reshetova E, Liljestrand H, Paverd A, Asokan N. Toward Linux kernel memory safety. Software: Practice and Experience, 2018, 48(12): 2237-2256.
Feng Z, Nie S, Wang YJ, Xue Z. Use-after-free vulnerabilities detection scheme based on S2E. Computer Applications and Software, 2016, 33(4): 273-276 (in Chinese with English abstract).
冯震, 聂森, 王轶骏, 薛质. 基于S2E的Use-after-free漏洞检测方案. 计算机应用与软件, 2016, 33(4): 273-276.
Zhang J, Huang ZQ, Shen GH, Yu YS, Ai L. Memory leak mechanism analysis and detection of C programs. Computer Engineering and Science, 2020, 42(5): 776-787 (in Chinese with English abstract).
张静, 黄志球, 沈国华, 喻垚慎, 艾磊. C程序中的内存泄漏机制分析与检测方法设计. 计算机工程与科学, 2020, 42(5): 776-787.
Li S, Tan G. Finding reference-counting errors in Python/C programs with affine analysis. In: Proc. of the European Conf. on Object-oriented Programming. 2014. 80-104.
Mao J, Chen Y, Xiao Q, Shi Y. RID: Finding reference count bugs with inconsistent path pair checking. In: Proc. of the 21st Int'l Conf. on Architectural Support for Programming Languages and Operating Systems. 2016. 531-544.
Padioleau Y, Lawall J, Hansen RR, Muller G. Documenting and automating collateral evolutions in Linux device drivers. ACM SIGOPS Operating Systems Review, 2008, 42(4): 247-260.
Ngiam J, Khosla A, Kim M, Nam J, Lee H, Ng AY. Multimodal deep learning. In: Proc. of the ICML. 2011.
Ramachandram D, Taylor GW. Deep multimodal learning: A survey on recent advances and trends. IEEE Signal Processing Magazine, 2017, 34(6): 96-108.
He J, Zhang CQ, Li XZ, Zhang DH. Survey of research on multimodal fusion technology for deep learning. Computer Engineering, 2020, 46(5): 1-11 (in Chinese with English abstract).
何俊, 张彩庆, 李小珍, 张德海. 面向深度学习的多模态融合技术研究综述. 计算机工程, 2020, 46(5): 1-11.
https://www.open-std.org/JTC1/SC22/WG21/docs/papers/2007/n2167.pdf]]>
Bovet DP, Cesati M. Understanding the Linux Kernel. O'Reilly Media, 2005.
https://www.kernel.org/doc/Documentation/atomic_t.txt]]>
https://www.kernel.org/doc/Documentation/kref.txt]]>
https://www.kernel.org/doc/Documentation/core-api/refcount-vs-atomic.rst]]>
https://lwn.net/Articles/668724/]]>
https://www.kernel.org/doc/Documentation/driver-api/basics.rst]]>
Collins M, Singer Y. Unsupervised models for named entity classification. In: Proc. of the Joint SIGDAT Conf. on Empirical Methods in Natural Language Processing and Very Large Corpora. 1999.
Szarvas G, Farkas R, Kocsor A. A multilingual named entity recognition system using boosting and C4.5 decision tree learning algorithms. In: Proc. of the Int'l Conf. on Discovery Science. 2006. 267-278.
McNamee P, Mayfield J. Entity extraction without language-specific resources. In: Proc. of the 6th Conf. on Natural Language Learning. 2002.
Krishnan V, Manning CD. An effective two-stage model for exploiting non-local dependencies in named entity recognition. In: Proc. of the 21st Int'l Conf. on Computational Linguistics and the 44th Annual Meeting of the Association for Computational Linguistics. 2006. 1121-1128.
Collobert R, Weston J, Bottou L, Karlen M, Kavukcuoglu K, Kuksa P. Natural language processing (almost) from scratch. Journal of Machine Learning Research, 2011, 12: 2493-2537.
Huang Z, Xu W, Yu K. Bidirectional LSTM-CRF models for sequence tagging. arXiv preprint arXiv: 1508.01991, 2015.
Hinton GE, Salakhutdinov RR. Reducing the dimensionality of data with neural networks. Science, 2006, 313(5786): 504-507.
Kahou SE, Pal C, Bouthillier X,
Wu D, Pigou L, Kindermans PJ, Le NDH, Shao L, Dambre J, Odobez JM. Deep dynamic neural networks for multimodal gesture segmentation and recognition. IEEE Trans. on Pattern Analysis and Machine Intelligence, 2016, 38(8): 1583-1597.
Hochreiter S, Schmidhuber J. Long short-term memory. Neural Computation, 1997, 9(8): 1735-1780.
Graves A, Schmidhuber J. Framewise phoneme classification with bidirectional LSTM and other neural network architectures. Neural Networks, 2005, 18(5-6): 602-610.
LeCun Y, Boser B, Denker J, Henderson D, Howard R, Hubbard W, Jackel L. Handwritten digit recognition with a back-propagation network. In: Advances in Neural Information Processing Systems. 1989.
LeCun Y, Bottou L, Bengio Y, Haffner P. Gradient-based learning applied to document recognition. Proc. of the IEEE, 1998, 2278-2324.
Peters ME, Neumann M, Iyyer M, Gardner M, Clark C, Lee K, Zettlemoyer L. Deep contextualized word representations. arXiv preprint arXiv: 1802.05365, 2018.
Galassi A, Lippi M, Torroni P. Attention in natural language processing. IEEE Trans. on Neural Networks and Learning Systems, 2021, 32(10): 4291-4308. [doi: 10.1109/TNNLS.2020.3019893]
Vaswani A, Shazeer N, Parmar N, Uszkoreit J, Jones L, Gomez AN, Polosukhin I. Attention is all you need. In: Advances in Neural Information Processing Systems. 2017. 5998-6008.