内存安全
记忆体安全(Memory safety)是在存取存储器时,不会出现像是缓冲区溢出或是迷途指针等,和记忆体有关的程序错误或漏洞[1]。像Java语言的执行时期错误检测,会检查阵列存取时的索引范围,以及指针的解引用(dereference),因此是记忆体安全的语言[1]。而C语言和C++的指针可以进行许多的指针运算,存取记忆体时也不会进行边界检查,因此是记忆体不安全的语言[2]。
历史
记忆体安全一开始是在资源管理及分时系统下考量,目的是为了避免像是Fork炸弹之类的问题[3]。最初的研究大部份都是纯理论的,直到后来莫里斯蠕虫出现,此蠕虫在finger协定中造成了缓冲区溢出[4]。此后电脑安全的领域快速发展,之后像是Return-to-libc攻击等大量新的网络攻击手法不断的升级,而防御机制也持续升级,例如非执行堆叠[5]及位址空间配置随机载入(ASLR)。随机化避免了大部份缓冲区溢出攻击,攻击者需要用heap spraying或是其他和应用程式有关的方式才能取得位址,只是其采用的速度还不快[4]。而这类防御机制的应用只限在函式库位置以及堆叠位置的随机化。
影响
微软的安全工程师曾在2019年提出,安全漏洞中有70%是因为记忆体安全的问题所造成[6]。2020年时,有一个Google的团队也提出Google Chromium的“严重安全问题”中,有70%是因为记忆体安全问题造成。许多备受关注的漏洞以及关键软体的漏洞利用也是因为缺乏记忆体安全,这些漏洞包括心脏出血漏洞[7],以及为时已久,在Sudo中权限提升的错误[8]。记忆体安全问题带来的漏洞及漏洞利用如此普遍,而且相当严重,许多软体安全研究者认为在程式中发现记忆体安全问题,是像“瓮中捉鳖”一样简单的事[9]。
作法
大部份高阶程式语言本来就有记忆体安全的特性,不过只检查本身的程式码,不会检查与其互动的系统,因此不是完整的记忆体安全。在预防记忆体安全问题的对策中,最常见的是使用垃圾回收功能的自动记忆体管理功能,此作法可以避免执行时配置资料出现use-after-free的问题[10]。若再结合阵列存取的边界检查,以及不支援原生的指标运算,垃圾回收可以有效确保记忆体安全性(不过若配合像是像是外部函式呼叫等,已视为不安全的低阶运算,其记忆体安全性会比较弱)。其缺点是会影响效能,因此有些要求效能的任务关键应用程式,就无法使用此功能[1]。
使用手动记忆体管理的程式语言,一般无法在执行时确保记忆体安全性。需要透过静态程序分析、自动化定理证明,或是程式开发者在程式执行时的小心管理,才能确保记忆体安全性[10]。像Rust程式语言就用借用检查器(borrow checker)来确保记忆体安全性[11],而C语言或C++语言则不保证记忆体安全性。用C语言和C++语言撰写的软体很多,因此也就有许多外部记忆体分析工具:像Coverity有C语言的静态记忆体分析[12]。
DieHard[13]及Allinea Distributed Debugging Tool有特别的heap分配器,将物件分配在随机的虚拟记忆体页中,不合法的记忆体读写就会停止程式。,此保护是用硬体记忆体保护为基础,额外的运算量不大,不过若大量的使用heap分配,运算量会显著增加[14]。用随机化来避免记忆体错误,仍然有机率会出错,不过此作法很容易在已有的软体实现(例如使用relinking二进位码)。
Valgrind的memcheck工具使用指令集模拟器,在有记忆体检查的虚拟机器中执行编译的程式,可以检测一部份的执行时记忆体问题,不过会让程式执行速度只有原来的1/40[15],而且若有自定的记忆体配置器,需明确告知[16][17]。
若在分析时可以存取原始码,有函式库可以搜集并追踪指标的合法值(metadata),并且将指标存取的位置和metadata比对,看位置是否有效,像贝姆垃圾回收器(Boehm garbage collector)就是一个例子[18]。一般而言,记忆体安全可以用跟踪垃圾回收(tracing garbage collection)以及在每一次存取时插入执行时的检查来确保:此作法会有额外的运算量,但是比Valgrind要好。所有有垃圾自动的语言都使用此一作法[1]。若是C语言或是C++语言,有工具可以在编辑时进行转换,以在执行时进行记忆体安全检查,像是CheckPointer[19]及Code sanitizer,程式执行速度约是原来的一半。 Address Sanitizer(ASAN),可以在程式运行时,对堆积、堆叠、全域变数、动态记忆体分配等错误进行实时检测。[20],这类工具在模糊测试用于检测程式是否进入异常状态。
ARM架构指令集实作了指标验证(Pointer Authentication, PAC)指令,可以对指标进行签章,在使用指标前验证指标是否受到窜改,因此攻击者需要计算出正确的 PAC 才能进行利用。[21][22]
记忆体错误的种类
- 存取错误:记忆体的读取或写入无效
- 缓冲区溢出:写入的资料量超过缓冲区范围,会修改到邻近的物件或是内部资料(像是堆叠中的控制资料或是呼叫堆叠中的返回位置。)
- 缓冲区过读:读取的资料量超过缓冲区范围,可能会读到敏感资料,也可能有助于骇客避开位址空间配置随机载入。
- 竞争危害:二个或多个程式同时读取及写入共享记忆体。
- 无效页缺失:存取到虚拟位址空间中,没有载入实体记忆体中的一个分页。大部份的环境,存取空指标指向的内容常常会造成例外或是程式中止执行,不过若在没有记忆体保护的内核或作业系统,或是该空指标的使用有很多负面的影响,那么无效页缺失会破坏系统。
- 使用已释放记忆体(UseAfter Free):在物件已删除后,依其物件的位置存取资料(指向这种位置的指标称为迷途指针)。
- 未初始化变数:使用了未初始化的变数,其中可能有不想要的资料,有些语言中则会是受损的资料。
- 内存泄漏:记忆体管理不当,无法追踪到特定记忆体,或是追踪到的内容不正确。
- 堆叠溢位:程式将呼叫堆叠用完,原因多半是因为递归控制不当,或是呼叫堆叠配置不足。一般来说堆栈保护页(guard page)会中止程式执行,避免记忆体破坏,不过若函数的stack frame很大,可能会跳过该页面。
- 记忆体用尽:程式想要配置记忆体的大小超过系统可提供的量。在有些程式语言里,需要在每次动态配置记忆体后另外进行检查。
- 双重释放(Double free):对已经释放的记忆体再度释放,若该位置已配置了其他的物件,会错误的释放其他的物件。若该位置尚未被使用,可能会出现其他的问题,特别是使用自由表的分配器(allocators)。
- 无效释放(Invalid free):试图释放无效的位置,可能会破坏Heap记忆体。
- 不匹配的释放(Mismatched free):使用多个分配器,试图用其他分配器的解分配函式(deallocation function)来释放记忆体[25]。
- 非预期的别名:同一个记忆体同时被二个不同用途的函式配置及修改,
参考资料
- ^ 1.0 1.1 1.2 1.3 Dhurjati, Dinakar; Kowshik, Sumant; Adve, Vikram; Lattner, Chris. Memory Safety Without Runtime Checks or Garbage Collection (PDF). Proceedings of the 2003 ACM SIGPLAN Conference on Language, Compiler, and Tool for Embedded Systems (ACM). 1 January 2003: 69–80 [13 March 2017]. ISBN 1581136471. S2CID 1459540. doi:10.1145/780732.780743. (原始内容存档 (PDF)于2023-04-09) (英语).
- ^ Akritidis, Periklis. Practical memory safety for C (PDF). Technical Report - University of Cambridge. Computer Laboratory (University of Cambridge, Computer Laboratory). June 2011 [13 March 2017]. ISSN 1476-2986. UCAM-CL-TR-798. (原始内容存档 (PDF)于2023-05-26).
- ^ Anderson, James P. Computer Security Planning Study (PDF) 2. Electronic Systems Center. [2023-04-09]. ESD-TR-73-51. (原始内容存档 (PDF)于2023-08-22).
- ^ 4.0 4.1 van der Veen, Victor; dutt-Sharma, Nitish; Cavallaro, Lorenzo; Bos, Herbert. Memory Errors: The Past, the Present, and the Future (PDF). Lecture Notes in Computer Science. 2012, 7462 (RAID 2012): 86–106 [13 March 2017]. ISBN 978-3-642-33337-8. doi:10.1007/978-3-642-33338-5_5. (原始内容存档 (PDF)于2016-06-26).
- ^ Wojtczuk, Rafal. Defeating Solar Designer's Non-executable Stack Patch. insecure.org. [13 March 2017]. (原始内容存档于2023-02-16).
- ^ Microsoft: 70 percent of all security bugs are memory safety issues. ZDNET. [21 September 2022]. (原始内容存档于2023-09-29) (英语).
- ^ CVE-2014-0160. Common Vulnerabilities and Exposures. Mitre. [8 February 2018]. (原始内容存档于24 January 2018) (英语).
- ^ Goodin, Dan. Serious flaw that lurked in sudo for 9 years hands over root privileges. Ars Technica. 4 February 2020 [2023-04-12]. (原始内容存档于2022-05-07) (美国英语).
- ^ Fish in a Barrel. fishinabarrel.github.io. [21 September 2022]. (原始内容存档于2023-05-23).
- ^ 10.0 10.1 Crichton, Will. CS 242: Memory safety. stanford-cs242.github.io. [2022-09-22]. (原始内容存档于2022-09-22).
- ^ References. The Rustonomicon. Rust.org. [2017-03-13]. (原始内容存档于2023-05-12) (英语).
- ^ Bessey, Al; Engler, Dawson; Block, Ken; Chelf, Ben; Chou, Andy; Fulton, Bryan; Hallem, Seth; Henri-Gros, Charles; Kamsky, Asya; McPeak, Scott. A few billion lines of code later. Communications of the ACM. 2010-02-01, 53 (2): 66–75 [2017-03-14]. doi:10.1145/1646353.1646374 . (原始内容存档于2017-06-07).
- ^ Berger, Emery D.; Zorn, Benjamin G. DieHard: Probabilistic Memory Safety for Unsafe Languages (PDF). Proceedings of the 27th ACM SIGPLAN Conference on Programming Language Design and Implementation (ACM). 2006-01-01: 158–168 [2017-03-14]. S2CID 8984358. doi:10.1145/1133981.1134000. (原始内容存档 (PDF)于2012-02-05) (英语).
- ^ Memory Debugging in Allinea DDT. (原始内容存档于2015-02-03).
- ^ Gyllenhaal, John. Using Valgrind's Memcheck Tool to Find Memory Errors and Leaks. computing.llnl.gov. [2017-03-13]. (原始内容存档于2018-11-07).
- ^ Memcheck: a memory error detector. Valgrind User Manual. valgrind.org. [2017-03-13]. (原始内容存档于2023-09-28) (英语).
- ^ Kreinin, Yossi. Why custom allocators/pools are hard. Proper Fixation. [2017-03-13]. (原始内容存档于2023-05-12).
- ^ Using the Garbage Collector as Leak Detector. www.hboehm.info. [2017-03-14]. (原始内容存档于2022-10-13) (美国英语).
- ^ Semantic Designs: CheckPointer compared to other safety checking tools. www.semanticdesigns.com. Semantic Designs, Inc. [2023-06-18]. (原始内容存档于2023-06-05).
- ^ Konstantin Serebryany; Derek Bruening and Alexander Potapenko and Dmitriy Vyukov. AddressSanitizer: A Fast Address Sanity Checker. USENIX ATC '12. 2012.
- ^ ARM pointer authentication. lwn.net. Jonathan Corbet. [2024-07-17].
- ^ qualcomm. Pointer Authentication on ARMv8.3 Design and Analysis of the New Software Security Instructions (PDF). qualcomm.com.
- ^ Gv, Naveen. How to Avoid, Find (and Fix) Memory Errors in your C/C++ Code. Cprogramming.com. [13 March 2017]. (原始内容存档于2023-04-18).
- ^ CWE-633: Weaknesses that Affect Memory. Community Weakness Enumeration. MITRE. [13 March 2017]. (原始内容存档于2023-04-15) (英语).
- ^ CWE-762: Mismatched Memory Management Routines. Community Weakness Enumeration. MITRE. [13 March 2017]. (原始内容存档于2023-05-30) (英语).