跳至內容

記憶體安全

本頁使用了標題或全文手工轉換
維基百科,自由的百科全書

記憶體安全(Memory safety)是在存取記憶體時,不會出現像是緩衝區溢位或是迷途指標等,和記憶體有關的程式錯誤漏洞[1]。像Java語言的執行時期錯誤檢測,會檢查陣列存取時的索引範圍,以及指針解除參照(dereference),因此是記憶體安全的語言[1]。而C語言C++的指針可以進行許多的指針運算,存取記憶體時也不會進行邊界檢查,因此是記憶體不安全的語言[2]

歷史

記憶體安全一開始是在資源管理英語Resource management (computing)分時系統下考量,目的是為了避免像是Fork炸彈之類的問題[3]。最初的研究大部份都是純理論的,直到後來莫里斯蠕蟲出現,此蠕蟲在finger協定中造成了緩衝區溢位[4]。此後電腦安全的領域快速發展,之後像是Return-to-libc攻擊等大量新的網路攻擊手法不斷的升級,而防禦機制也持續升級,例如非執行堆疊英語Executable space protection[5]位址空間組態隨機載入(ASLR)。隨機化避免了大部份緩衝區溢位攻擊,攻擊者需要用heap spraying英語heap spraying或是其他和應用程式有關的方式才能取得位址,只是其採用的速度還不快[4]。而這類防禦機制的應用只限在函式庫位置以及堆疊位置的隨機化。

影響

微軟的安全工程師曾在2019年提出,安全漏洞中有70%是因為記憶體安全的問題所造成[6]。2020年時,有一個Google的團隊也提出Google Chromium的「嚴重安全問題」中,有70%是因為記憶體安全問題造成。許多備受關注的漏洞以及關鍵軟體的漏洞利用也是因為缺乏記憶體安全,這些漏洞包括心臟出血漏洞[7],以及為時已久,在Sudo中權限提昇的錯誤[8]。記憶體安全問題帶來的漏洞及漏洞利用如此普遍,而且相當嚴重,許多軟體安全研究者認為在程式中發現記憶體安全問題,是像「甕中捉鱉」一樣簡單的事[9]

作法

大部份高階程式語言本來就有記憶體安全的特性,不過只檢查本身的程式碼,不會檢查與其互動的系統,因此不是完整的記憶體安全。在預防記憶體安全問題的對策中,最常見的是使用垃圾回收功能的自動記憶體管理功能,此作法可以避免執行時組態資料出現use-after-free的問題[10]。若再結合陣列存取的邊界檢查,以及不支援原生的指標運算,垃圾回收可以有效確保記憶體安全性(不過若配合像是像是外部函式呼叫英語foreign function interface等,已視為不安全的低階運算,其記憶體安全性會比較弱)。其缺點是會影響效能,因此有些要求效能的任務關鍵應用程式,就無法使用此功能[1]

使用手動記憶體管理的程式語言,一般無法在執行時確保記憶體安全性。需要透過靜態程式分析自動化定理證明,或是程式開發者在程式執行時的小心管理,才能確保記憶體安全性[10]。像Rust程式語言就用借用檢查器(borrow checker)來確保記憶體安全性[11],而C語言或C++語言則不保證記憶體安全性。用C語言和C++語言撰寫的軟體很多,因此也就有許多外部記憶體分析工具:像Coverity英語Coverity有C語言的靜態記憶體分析[12]

DieHard[13]Allinea Distributed Debugging Tool英語Allinea Distributed Debugging Tool有特別的heap分配器,將物件分配在隨機的虛擬記憶體頁中,不合法的記憶體讀寫就會停止程式。,此保護是用硬體記憶體保護為基礎,額外的運算量不大,不過若大量的使用heap分配,運算量會顯著增加[14]。用隨機化來避免記憶體錯誤,仍然有機率會出錯,不過此作法很容易在已有的軟體實現(例如使用relinking二進位碼)。

Valgrind的memcheck工具使用指令集模擬器英語instruction set simulator,在有記憶體檢查的虛擬機器中執行編譯的程式,可以檢測一部份的執行時記憶體問題,不過會讓程式執行速度只有原來的1/40[15],而且若有自訂的記憶體組態器,需明確告知[16][17]

若在分析時可以存取原始碼,有函式庫可以蒐集並追蹤指標的合法值(metadata),並且將指標存取的位置和metadata比對,看位置是否有效,像貝姆垃圾回收器(Boehm garbage collector)就是一個例子[18]。一般而言,記憶體安全可以用跟蹤垃圾回收(tracing garbage collection)以及在每一次存取時插入執行時的檢查來確保:此作法會有額外的運算量,但是比Valgrind要好。所有有垃圾自動的語言都使用此一作法[1]。若是C語言或是C++語言,有工具可以在編輯時進行轉換,以在執行時進行記憶體安全檢查,像是CheckPointer[19]Code sanitizer英語AddressSanitizer,程式執行速度約是原來的一半。 Address Sanitizer(ASAN),可以在程式運行時,對堆積、堆疊、全域變數、動態記憶體分配等錯誤進行實時檢測。[20],這類工具在模糊測試用於檢測程式是否進入異常狀態。

ARM架構指令集實作了指標驗證(Pointer Authentication, PAC)指令,可以對指標進行簽章,在使用指標前驗證指標是否受到竄改,因此攻擊者需要計算出正確的 PAC 才能進行利用。[21][22]

記憶體錯誤的種類

記憶體的錯誤有很多種[23][24]

  • 存取錯誤:記憶體的讀取或寫入無效
    • 緩衝區溢位:寫入的資料量超過緩衝區範圍,會修改到鄰近的物件或是內部資料(像是堆疊中的控制資料或是呼叫堆疊中的返回位置。)
    • 緩衝區過讀:讀取的資料量超過緩衝區範圍,可能會讀到敏感資料,也可能有助於駭客避開位址空間組態隨機載入
    • 競爭危害:二個或多個程式同時讀取及寫入共享記憶體。
    • 無效頁缺失:存取到虛擬位址空間中,沒有載入實體記憶體中的一個分頁。大部份的環境,存取空指標指向的內容常常會造成例外或是程式中止執行,不過若在沒有記憶體保護核心或作業系統,或是該空指標的使用有很多負面的影響,那麼無效頁缺失會破壞系統。
    • 使用已釋放記憶體(UseAfter Free):在物件已刪除後,依其物件的位置存取資料(指向這種位置的指標稱為迷途指標)。
  • 未初始化變數英語Uninitialized variable:使用了未初始化的變數,其中可能有不想要的資料,有些語言中則會是受損的資料。
    • 解除參照空指標:解除參照無效的指標,或是解除參照指向未組態記憶體的指標 。
    • 野指標:使用到尚未初始化指標,其問題類似迷途指標,不過這種問題比較容易發現。
  • 記憶體流失:記憶體管理不當,無法追蹤到特定記憶體,或是追蹤到的內容不正確。
    • 堆疊溢位:程式將呼叫堆疊用完,原因多半是因為遞迴控制不當,或是呼叫堆疊組態不足。一般來說堆疊保護頁(guard page)會中止程式執行,避免記憶體破壞,不過若函式的stack frame很大,可能會跳過該頁面。
    • 記憶體用盡英語Out of memory:程式想要組態記憶體的大小超過系統可提供的量。在有些程式語言裡,需要在每次動態組態記憶體後另外進行檢查。
    • 雙重釋放(Double free):對已經釋放的記憶體再度釋放,若該位置已組態了其他的物件,會錯誤的釋放其他的物件。若該位置尚未被使用,可能會出現其他的問題,特別是使用自由表的分配器(allocators)。
    • 無效釋放(Invalid free):試圖釋放無效的位置,可能會破壞Heap記憶體。
    • 不匹配的釋放(Mismatched free):使用多個分配器,試圖用其他分配器的解分配函式(deallocation function)來釋放記憶體[25]
    • 非預期的別名:同一個記憶體同時被二個不同用途的函式組態及修改,

參考資料

  1. ^ 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) (英語). 
  2. ^ 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). 
  3. ^ Anderson, James P. Computer Security Planning Study (PDF) 2. Electronic Systems Center. [2023-04-09]. ESD-TR-73-51. (原始內容存檔 (PDF)於2023-08-22). 
  4. ^ 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). 
  5. ^ Wojtczuk, Rafal. Defeating Solar Designer's Non-executable Stack Patch. insecure.org. [13 March 2017]. (原始內容存檔於2023-02-16). 
  6. ^ Microsoft: 70 percent of all security bugs are memory safety issues. ZDNET. [21 September 2022]. (原始內容存檔於2023-09-29) (英語). 
  7. ^ CVE-2014-0160. Common Vulnerabilities and Exposures. Mitre. [8 February 2018]. (原始內容存檔於24 January 2018) (英語). 
  8. ^ 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) (美國英語). 
  9. ^ Fish in a Barrel. fishinabarrel.github.io. [21 September 2022]. (原始內容存檔於2023-05-23). 
  10. ^ 10.0 10.1 Crichton, Will. CS 242: Memory safety. stanford-cs242.github.io. [2022-09-22]. (原始內容存檔於2022-09-22). 
  11. ^ References. The Rustonomicon. Rust.org. [2017-03-13]. (原始內容存檔於2023-05-12) (英語). 
  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). 
  13. ^ 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) (英語). 
  14. ^ Memory Debugging in Allinea DDT. (原始內容存檔於2015-02-03). 
  15. ^ Gyllenhaal, John. Using Valgrind's Memcheck Tool to Find Memory Errors and Leaks. computing.llnl.gov. [2017-03-13]. (原始內容存檔於2018-11-07). 
  16. ^ Memcheck: a memory error detector. Valgrind User Manual. valgrind.org. [2017-03-13]. (原始內容存檔於2023-09-28) (英語). 
  17. ^ Kreinin, Yossi. Why custom allocators/pools are hard. Proper Fixation. [2017-03-13]. (原始內容存檔於2023-05-12). 
  18. ^ Using the Garbage Collector as Leak Detector. www.hboehm.info. [2017-03-14]. (原始內容存檔於2022-10-13) (美國英語). 
  19. ^ Semantic Designs: CheckPointer compared to other safety checking tools. www.semanticdesigns.com. Semantic Designs, Inc. [2023-06-18]. (原始內容存檔於2023-06-05). 
  20. ^ Konstantin Serebryany; Derek Bruening and Alexander Potapenko and Dmitriy Vyukov. AddressSanitizer: A Fast Address Sanity Checker. USENIX ATC '12. 2012. (原始內容存檔於2024-11-22) 使用|archiveurl=需要含有|url= (幫助). 
  21. ^ ARM pointer authentication. lwn.net. Jonathan Corbet. [2024-07-17]. (原始內容存檔於2024-11-12). 
  22. ^ qualcomm. Pointer Authentication on ARMv8.3 Design and Analysis of the New Software Security Instructions (PDF). qualcomm.com. [2024-07-17]. (原始內容存檔 (PDF)於2024-10-15). 
  23. ^ Gv, Naveen. How to Avoid, Find (and Fix) Memory Errors in your C/C++ Code. Cprogramming.com. [13 March 2017]. (原始內容存檔於2023-04-18). 
  24. ^ CWE-633: Weaknesses that Affect Memory. Community Weakness Enumeration. MITRE. [13 March 2017]. (原始內容存檔於2023-04-15) (英語). 
  25. ^ CWE-762: Mismatched Memory Management Routines. Community Weakness Enumeration. MITRE. [13 March 2017]. (原始內容存檔於2023-05-30) (英語).