記憶體安全

記憶體安全（Memory safety）是在存取存储器時，不會出現像是缓冲区溢出或是迷途指针等，和記憶體有關的程序错误或漏洞。像Java語言的執行時期錯誤檢測，會檢查陣列存取時的索引範圍，以及指針的解引用（dereference），因此是記憶體安全的語言。而C語言和C++的指針可以進行許多的指針運算，存取記憶體時也不會進行边界检查，因此是記憶體不安全的語言。

 歷史 
記憶體安全一開始是在及分時系統下考量，目的是為了避免像是Fork炸弹之類的問題。最初的研究大部份都是純理論的，直到後來莫里斯蠕虫出現，此蠕虫在finger協定中造成了缓冲区溢出。此後電腦安全的領域快速發展，之後像是Return-to-libc攻击等大量新的网络攻击手法不斷的升級，而防禦機制也持續升級，例如及位址空間配置隨機載入（ASLR）。隨機化避免了大部份缓冲区溢出攻擊，攻擊者需要用或是其他和應用程式有關的方式才能取得位址，只是其採用的速度還不快。而這類防禦機制的應用只限在函式庫位置以及堆疊位置的隨機化。

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

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

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

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

Valgrind的memcheck工具使用，在有記憶體檢查的虛擬機器中執行編譯的程式，可以檢測一部份的執行時記憶體問題，不過會讓程式執行速度只有原來的1/40，而且若有自定的記憶體配置器，需明確告知。

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

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

 記憶體錯誤的種類 
記憶體的錯誤有很多種：

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

參考資料

Category:程式錯誤
Category:保全漏洞
Category:程式語言翻譯