Static Detection of Unsafe DMA Accesses in Device Drivers . USENIX Security 2021静态分析DMA,有很多DMA基础知识

Jia-Ju Bai and Tuo Li, Tsinghua University; Kangjie Lu, University of Minnesota; Shi-Min Hu, Tsinghua University

Abstract

Direct Memory Access (DMA) is a popular mechanism for improving hardware I/O performance, and it has been widely used by many existing device drivers. However, DMA accesses can be unsafe, from two aspects. First, without proper synchronization of DMA buffers with hardware registers and CPU cache, the buffer data stored in CPU cache and hardware registers can be inconsistent, which can cause unexpected hardware behaviors. Second, a malfunctioning or untrusted hardware device can write bad data into system memory, which can trigger security bugs (such as buffer overflow and invalid pointer access), if the driver uses the data without correct validation. To detect unsafe DMA accesses, some key challenges need to be solved. For example, because each DMA access is implemented as a regular variable access in the driver code, identifying DMA accesses is difficult.

直接内存访问(DMA)是一种用于改进硬件I/O性能的流行机制,它已被许多现有设备驱动程序广泛使用。然而,从两个方面来看,DMA访问可能是不安全的。首先,如果DMA缓冲区与硬件寄存器和CPU缓存没有适当的同步,存储在CPU缓存和硬件寄存器中的缓冲区数据可能会不一致,这可能会导致意外的硬件行为。其次,如果驱动程序在没有正确验证的情况下使用数据,故障或不可信的硬件设备可能会将错误数据写入系统内存,从而引发安全漏洞(比如缓冲区溢出和无效的指针访问)。为了检测不安全的DMA访问,需要解决一些关键的挑战。例如,由于每个DMA访问在驱动程序代码中被实现为常规变量访问,因此很难识别DMA访问。

In this paper, we propose a static-analysis approach named SADA, to automatically and accurately detect unsafe DMA accesses in device drivers. SADA consists of three basic steps. First, SADA uses a field-based alias analysis to identify DMA accesses, according to the information of DMA-buffer creation. Second, SADA uses a flow-sensitive and pattern-based analysis to check the safety of each DMA access, to detect possible unsafe DMA accesses. Finally, SADA uses a SMT solver to validate the code-path condition of each possible unsafe DMA access, to drop false positives. We have evaluated SADA on the driver code of Linux 5.6, and found 284 real unsafe DMA accesses. Among them, we highlight that 121 can trigger buffer-overflow bugs and 36 can trigger invalid-pointer accesses causing arbitrary read or write. We have reported these unsafe DMA accesses to Linux driver developers, and 105 of them have been confirmed.

在本文中,我们提出了一种名为SADA的静态分析方法,用于自动、准确地检测设备驱动程序中的不安全DMA访问。SADA由三个基本步骤组成。首先,根据DMA缓冲区创建的信息,SADA使用基于字段的别名分析来识别DMA访问。其次,SADA使用流敏感和基于模式的分析来检查每个DMA访问的安全性,以检测可能的不安全的DMA访问。最后,SADA使用一个SMT求解器来验证每个可能的不安全DMA访问的代码路径条件,以消除误检。我们在Linux 5.6的驱动程序代码上评估了SADA,发现了284个真正不安全的DMA访问。其中,我们强调了121个可能触发缓冲区溢出bug, 36个可能触发无效指针访问,从而导致任意读或写。我们已经向Linux驱动程序开发人员报告了这些不安全的DMA访问,其中有105个已经得到证实。
请添加图片描述

1 介绍

现代操作系统(OS)控制各种外围硬件设备,包括以太网控制器、声卡、存储适配器等。提高操作系统与硬件之间的数据通信性能设备,直接内存访问(DMA)旨在减少CPU对硬件I/O的参与。操作系统通过将硬件寄存器映射到系统内存的一个区域(称为DMA缓冲区)来启用DMA,然后操作系统可以通过访问DMA缓冲区来直接访问硬件寄存器。

许多现有设备驱动程序都使用DMA来提高性能,但是DMA访问可能是不安全的,即使使用IOMMU来保证访问的内存地址是有效的。首先,只有当缓冲区与硬件寄存器和CPU缓存正确同步时,驱动程序才应该访问DMA buffer。否则,存储在硬件寄存器和CPU缓存中的访问数据可能不一致,从而导致硬件设备的意外行为。简而言之,我们把这样的问题称为不一致的DMA访问。

其次,考虑到硬件设备可能出现故障[27,55]或不受信任[28,53,65],它可能会将坏数据写入DMA缓冲区,因此驱动程序应该在使用之前对DMA缓冲区中的数据执行正确的验证。否则,在运行时可能会触发安全漏洞(如缓冲区溢出和无效指针访问)。简而言之,我们将此问题称为未检查的DMA访问。

为了减轻DMA访问带来的安全风险,一些最近的工作[45、51、52]执行驱动程序模糊,并发现了一些由来自DMAbuffer的坏数据导致的安全漏洞。具体来说,他们创建了一个模拟设备来生成和改变硬件输入(包括来自DMABuffer的数据),并测试驱动程序是否能够正确处理这些输入。但它们在检测非安全DMA访问时仍有一些局限性。首先,它们需要相关的模拟设备来实际运行测试的驱动程序,而实现这些模拟设备通常需要大量的手工工作。其次,它们的代码覆盖范围仅限于生成的测试用例,导致许多真正不安全的DMA访问被错过。最后,他们无法检测不一致的DMA访问,因为它们不考虑DMA缓冲器的同步。

静态分析在实现高代码覆盖年龄和减少假阴性方面是有效的。但是,在Linux驱动程序代码中使用静态分析来检测不安全的DMA访问仍然具有挑战性。首先,当每个DMA访问都被实现时,作为驱动程序代码中的常规变量访问,很难静态识别DMA访问。其次,由于Linuxkernel代码库非常庞大和复杂,对其执行静态分析也很困难。第三,由于缺少驱动程序的准确运行时信息,静态分析可能会报告许多误报。据我们所知,目前还没有系统的静态方法来检测不安全的DMA访问。

在本文中,我们提出了一种静态分析方法 SADA(静态分析DMA访问),以自动并准确地检测设备驱动程序中的不安全DMA访问。总体而言,SADA包括三个基本步骤:

首先,考虑到DMA访问和DMA映射创建可能在不同的驱动程序功能中执行,因此,使用基于字段的别名分析,根据DMA映射创建的信息识别DMA访问,因为我们对Linux驱动程序代码的研究发现,大约87%创建的DMA Buffer存储在驱动程序代码的数据结构字段中。

其次,我们使用流敏感和基于模式的分析来检查每个DMA访问的安全性,以检测可能的非安全DMA访问。具体地说,为了检测不一致的DMA访问,SADA CHECK通过分析代码上下文来检查每个DMA访问是否在DMA缓冲区正确同步的情况下执行。为了检测未检查的DMA访问,SAD使用静态污染分析来检查从DMA缓冲区访问的数据是否会对数据流或控制流造成可能的不安全影响。例如,如果将存储在DMA缓冲区中的变量用作数组索引而不进行任何检查,则可能会发生缓冲区溢出错误。

最后,Sadaus使用SMT解算器Z3[66]验证每个可能的不安全DMA访问的代码路径可行性,以丢弃错误的。这样,与分析整个驱动程序代码时验证代码路径条件的传统方法相比,SMT解决方案引入的开销可以减少。

我们已经用LLVM实现了SADA[33]。

总体而言,我们做出了以下技术贡献:

•通过研究设备驱动程序中的DMA,我们从两个方面揭示了DMA访问的安全风险:

1)它们可能导致意外的硬件行为;2)它们可能会触发安全漏洞(如缓冲区溢出和无效指针访问),这些漏洞是由功能不正常或不受信任的硬件设备的坏数据引起的。

•我们提出了一种实用的静态分析方法,名为DSAD,为了在设备驱动程序中有效检测不安全的DMA访问,SADAI结合了多种技术以确保检测的准确性和有效性。
据我们所知,SADAI是第一种检测不安全DMA访问的系统静态方法。

•我们评估了Linux 5.6上的ADA,发现了284个真正不安全的DMA访问。其中,我们强调121个可以触发缓冲区溢出错误,36个可以触发无效指针访问,导致任意读写。我们已经向Linuxdriver开发人员报告了这些不安全的DMA访问,其中105个已经得到确认。

本文的其余部分组织如下。第2节介绍了DMA的背景和我们的研究。第3节分析了静态检测不安全DMA访问的挑战。第4节介绍了我们的解决方案技术。第5节呈现详细信息。第6节显示了我们的评估。第7节讨论了DSA和unsafeDMA访问。第8节介绍了相关工作,第9节总结了本文。

2 DMA的背景和研究

在本节中,我们介绍DMA及其现有研究中存在的问题,然后揭示DMA访问的安全风险,最后研究Linux设备驱动程序中的DMA。

2.1 DMA体系结构直接内存访问(DMA)

是一种流行的机制,允许外围硬件设备在不涉及CPU的情况下与系统内存进行数据通信。在没有DMA的情况下,当数据在硬件设备和系统内存之间传输时,CPU通常会在整个数据传输期间被完全占用,因此该CPU无法执行其他任务。在DMA中,CPU初始化数据传输,然后将实际数据传输交给DMA控制器(DMAC),这样CPU就可以专注于其他任务。一旦数据传输完成,CPU将从DMA控制器接收中断,以结束数据传输。通过这种方式,CPU只执行最小最小作业,即数据传输的初始化和终结,从而提高硬件I/O性能。

请添加图片描述
图1显示了现代计算机系统中DMA的体系结构。为了启用DMA,DMA缓冲区被分配并映射到系统内存和硬件寄存器。当CPU想要读取硬件寄存器时,它直接读取系统内存中的DMA缓冲区,并将数据同步到CPU缓存中。同样,当CPU想要写入硬件寄存器时,它会直接写入系统内存中的DMA缓冲区,并将数据同步到硬件寄存器中。为了降低编程复杂性,每个DMA访问在驱动程序代码中实现为一个常规变量访问,例如data=DMA_buf->data(读取DMA缓冲区)和DMA_buf->data=data(写入DMA缓冲区)。

DMA type

根据硬件寄存器和CPU缓存的同步方式,设备驱动程序中使用了两种类型的DMA缓冲区:

Coherent DMA buffer。相干DMA缓冲区同时对CPU和硬件设备可用,并且通常在驱动程序的生命周期内存在(在驱动程序初始化时分配,在驱动程序删除时释放)。要使存储在硬件寄存器和CPU缓存中的数据始终保持一致,此DMA缓冲区必须位于缓存一致性存储器中,这通常是昂贵的设置和使用。这样,驱动程序就不需要显式同步硬件寄存器和CPU缓存之间的数据。

Streaming DMA缓冲区。流式DMA缓冲区是同步的CPU和硬件设备都可用,并且在驱动程序运行时,它通常会动态映射和取消映射到特定的内存区域。因为存储在硬件寄存器和CPU缓存中的数据可能不一致,所以驱动程序需要在适当的时间显式同步它们之间的数据。但是,因为StreamingDMA buffer不会生活在高速缓存相干内存中,它比Coherent DMA缓冲区更易于设置和使用。

请添加图片描述

DMA接口

Linux内核为执行DMA操作的驱动程序提供特定的内核接口。表1列出了用于相干和流化DMA缓冲区的一些常用接口。
请注意,一致性DMA缓冲区位于高速缓存一致性内存中,因此不需要硬件寄存器和CPU高速缓存之间的同步接口。

2.2现有研究中的DMA问题

尽管DMA可以提高硬件I/O性能,但也会带来安全风险。在过去的几年中,DMA的许多安全问题已经被发现并得到了广泛的解决,我们列举了以下具有代表性的问题:

DMA攻击

通过DMA,恶意的支持DMA的硬件设备可以直接访问部分或全部系统内存[21]。通过这种方式,攻击者可以窃取机密数据或控制操作系统。为了防御DMA攻击,许多现有方法[40–42、44、58]使用输入输出内存管理单元(IOMMU)来限制启用DMA的硬件设备可以访问的系统内存区域。
40 要看
[40] MARKETTOS, T., ROTHWELL, C., GUTSTEIN, B. F.,
PEARCE, A., NEUMANN, P. G., MOORE, S., AND WATSON, R. Thunderclap: exploring vulnerabilities in operating system IOMMU protection via DMA from untrustworthy peripherals. In Proceedings of the 26th Network
and Distributed Systems Security Symposium (NDSS)
(2019).
[41] MARKUZE, A., MORRISON, A., AND TSAFRIR, D.
True IOMMU protection from DMA attacks: when copy
is faster than zero copy. In Proceedings of the 21st International Conference on Architectural Support for Programming Languages and Operating Systems (ASPLOS)
(2016), pp. 249–262.
[42] MARKUZE, A., SMOLYAR, I., MORRISON, A., AND
TSAFRIR, D. DAMN: overhead-free IOMMU protection for networking. In Proceedings of the 23rd International Conference on Architectural Support for Programming Languages and Operating Systems (ASPLOS)
(2018), pp. 301–315.
[44] PELEG, O., MORRISON, A., SEREBRIN, B., AND
TSAFRIR, D. Utilizing the IOMMU scalably. In Proceedings of the 2015 USENIX Annual Technical Conference (2015), pp. 549–562.
[58] TIAN, K., ZHANG, Y., KANG, L., ZHAO, Y., AND
DONG, Y. coIOMMU: a virtual IOMMU with cooperative DMA buffer tracking for efficient memory management in direct I/O. In Proceedings of the 2020 USENIX
Annual Technical Conference (2020), pp. 479–492.

映射无效 invalid mapping

DMA缓冲区应映射到连续地址的物理内存区域。**因此,DMA缓冲区无法映射到堆栈内存,因为其物理内存地址可能不连续。**否则,在运行时可能会发生意外的堆栈溢出。最近,Linux驱动程序开发人员强调了此类问题[23],因为一些Linux内核提交(如6C2794A2984F[6]和3840C5B78803[7])已应用于修复此类问题。

缓冲区创建检查不当

一旦驱动程序调用DMA接口创建DMA缓冲区,应在驱动程序代码中正确检查其返回值,因为创建可能失败。否则,可能会发生空指针解引用或无效DMA访问。2013年,Linux驱动程序开发人员使用简单的静态分析[22]来检测Linux内核中的许多此类问题,其中一些问题已被过去的内核提交(如ASCF3C0300B[8]和C9BFBB31AF7C[9])修复。

缓冲区销毁遗漏

驱动程序应在删除之前销毁已创建的DMA缓冲区;否则将发生内存泄漏。现有的几种资源泄漏检测方法(如Hector[48]和PR Miner[32])已经发现了一些这样的问题,并且已经被过去的内核组件(如37C85C3498C5[10]和7CA2CD291FD8[11])修复。

总结

上述大多数DMA问题都与创建和销毁有关,通过调用特定的DMA接口来执行。因此,大多数现有方法检查这些DMA接口的规则以检测DMA问题。事实上,除了调用这些DMA接口外,执行DMA访问也可能存在安全风险,这在现有研究中尚未完全实现。

因此,在本文中,我们将重点放在第2.3.

2.3 不安全DMA访问

DMA访问的安全风险

根据第2.1节中介绍的DMA缓冲区类型,DMA访问可以是流式DMA访问或一致性DMA访问,具有不同的安全风险:

流式DMA访问

一旦流式DMA缓冲区被映射,它就属于硬件设备而不是CPU。在缓冲区被取消映射之前,驱动程序不应访问流式DMA缓冲区的内容;一个例外是,在与硬件寄存器和CPU缓存同步期间,允许驱动程序访问缓冲区内容[17]。否则,访问流式DMA缓冲区的内容可能会导致硬件寄存器和CPU缓存之间的数据不一致,从而导致意外的硬件行为。短期而言,我们将此问题称为不一致DMA访问。图2显示了Linux 5.6中Tl8192Ce无线设备驱动程序中的实际不一致DMA访问。在函数rtl92ce_tx_fill_cmddesc中,pci_map_single在第531行被调用为tomapskb->datato streaming DMA buffer。然后,本地变量hdrpoints toskb->dataon line 535。之后,在第536行,读取hdr->frame_control并作为签名的tofc,即执行流式DMA访问没有同步,导致DMA访问不一致。驱动程序开发人员承认,这个问题可能导致意外的硬件行为,从而导致驱动程序崩溃。这个问题在Linux 4.4(2016年1月发布)中引入,并在4.5年后(2020年10月)由我们根据我们方法的错误报告修复。在调用PCI\u map\u single之前,我们通过访问DR->frame\u控件解决了这个问题。

一致性DMA访问。

与流式DMA缓存不同,相干DMA缓存不需要与硬件寄存器和CPU缓存进行显式同步。但一方面,由于硬件设备可能出现故障或不可信,它可以将坏数据写入连贯的DMA缓冲区;另一方面,由于硬件设备和驱动程序都可以修改相干DMA缓冲区中的数据,因此驱动程序在读取相同的相干DMA缓冲区时可能会获得不同的数据,从而导致双取情况。出于这两个原因,驾驶员应该在使用数据之前对来自DMABuffer的数据进行正确的验证。否则,可能会触发安全漏洞(如缓冲区溢出和无效指针访问)。简而言之,我们将此问题称为未检查DMA访问。

DMA访问规则

为了更好地理解,我们在图4中用Linux内核的真实DMA接口演示了流式和一致DMA访问的规则。图2和图3所示的代码段显然违反了规则,因此它们具有不安全的DMA访问

请添加图片描述

2.4威胁模型

我们的威胁模型由一个对手组成,该对手利用外部驱动程序中的软件缺陷,通过DMA访问攻击theOS,以实现恶意目标,如拒绝服务和权限提升。实际上,设备驱动程序通常由操作系统内核提供,用于管理多个第三方硬件设备,并在用户级别支持特定的工作负载。例如,Linux内核提供的USB核心驱动器用于控制不同种类的USB设备并支持用户级USB服务。因此,驱动程序总是被认为是良性的,但硬件设备和用户级工作负载可能是不可信的。因此,可以通过两种方式发起攻击。首先,攻击者可以在用户级别执行特定的工作负载,以触发不一致的DMA访问,这可能导致意外的硬件行为,甚至导致操作系统崩溃。其次,攻击者可以使用不受信任的硬件设备通过DMA缓冲区向驱动程序提供恶意数据,这可能导致缓冲区溢出、无效指针访问和其他严重的安全问题。

2.5 DMA访问检查的最新技术

最近,几种驱动程序模糊方法[45、51、52]通过从模拟设备生成和改变硬件输入,发现了一些未经检查的DMA访问。但由于运行时测试的代码覆盖率的限制,他们可能会错过许多真正未经检查的DMA访问。此外,他们没有考虑流式DMA缓冲区的同步,因此无法检测不一致的DMA访问。此外,它们需要相关的模拟设备来运行测试的驱动程序,但是实现这些模拟设备通常需要大量的人工工作。为了解决这些限制,我们的目标是设计一种有效的静态分析方法,以尽可能多地自动准确地检测不安全的DMA访问。

2.6 Linux设备驱动器中DMA的研究

为了理解检测不安全DMA访问的重要性,我们需要知道有多少现有设备驱动器具有DMA操作。为了找到答案,我们手动研究Linux内核中的驱动程序源代码,以计算具有DMA操作的设备驱动程序的比例。由于Linux设备驱动程序数量众多且时间有限,我们选择Linux 5.6中8个常见类的所有驱动程序并手动读取其源代码,以识别调用Linux内核中定义的DMA接口的驱动程序。考虑到一个驱动程序可能由多个源文件生成,我们可以通过手动检查Linux内核中的RMAKE文件来确定驱动程序的数量。表2显示了我们的研究结果。大约46%的ied驱动程序显式调用DMA接口,这表明DMA操作在现有设备驱动程序中很常见。因此,在设备驱动程序中检查DMA访问的安全性非常重要。

5 SADA Approach

基于第4节中的三个关键技术,我们提出了一种静态分析方法DSADA,自动调用并准确检测设备驱动程序中的不安全DMA访问。我们已经实现了SADAusing Clang-9.0[15]并对驱动程序LLVM字节码文件进行按格式静态分析。给定设备驱动程序的源文件,SADAW无需手动即可自动工作。图11显示了SADA的总体架构。
请添加图片描述
基于此架构,SADA包括四个阶段:P1:代码编译。语言编译器将每个源文件编译成LLVM字节码文件。由于在不同设备驱动程序中定义的多个函数可能共享相同的函数名,因此过程间分析可能会识别不正确的函数进行分析。为了解决这个问题,在链接过程中,SADARE记录生成同一驱动程序的源文件集。根据此信息,SADAcan在执行过程间分析时可以准确选择正确的函数。P2:DMA访问识别。信息采集器从每个LLVM字节码文件中收集有关每个DMA缓冲区创建的信息,然后访问检测器使用此信息并执行基于字段的别名分析以识别DMA访问。P3:DMA访问检查。访问检测器使用流敏感和基于模式的分析来检查每个已识别DMA访问的安全性,并检测可能未安全的DMA访问。P4:不安全DMA访问验证。路径Validator使用我们的代码路径验证方法删除错误的unsafeDMA访问。此外,我们的流敏感分析可能会发现,当DMA缓冲区创建和DMA访问相同但仅在代码路径上不同时,任何重复的不安全DMA访问都是相同的。为了解决这个问题,路径验证器还根据DMA缓冲区创建和DMA访问的位置来重复结果。最后,SADAgeneratesreadable报告最终的不安全DMA访问。

9 Conclusion

DMA is a frequently-used mechanism to improve hardware I/O performance, but DMA accesses can be unsafe and cause security problems. In this paper, we propose a static approach named SADA, to automatically and accurately detect unsafe DMA accesses in device drivers. SADA integrates three key techniques, including a field-based alias analysis to identify DMA accesses, a flow-sensitive and pattern-based analysis to check the safety of each DMA access, and a code-path validation method to drop false positives. In the Linux driver code, SADA finds 284 real unsafe DMA accesses, which can cause unexpected hardware behaviors or trigger security bugs (such as buffer overflow and invalid-pointer access).

DMA是一种用于提高硬件I/O性能的常用机制,但是DMA访问可能不安全并会导致安全问题。在本文中,我们提出了一种名为SADA的静态方法,用于自动、准确地检测设备驱动程序中的不安全DMA访问。SADA集成了三个关键技术,包括基于字段的别名分析来识别DMA访问,基于流敏感和模式的分析来检查每个DMA访问的安全性,以及消除误报的代码路径验证方法。在Linux驱动程序代码中,SADA发现284个真正不安全的DMA访问,这些访问可能导致意外的硬件行为或触发安全bug(例如缓冲区溢出和无效指针访问)。

In the future, we plan to apply SADA to other OSes (such as FreeBSD and NetBSD) to check their driver code. We also plan to add more patterns in checking the safety of DMA accesses, to find more unsafe DMA accesses that can trigger other kinds of security problems.

在未来,我们计划将SADA应用于其他操作系统(如FreeBSD和NetBSD),以检查它们的驱动程序代码。我们还计划添加更多的模式来检查DMA访问的安全性,以发现更多可能引发其他类型安全问题的不安全的DMA访问。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值