http://www.gatech.edu/~klu38。
操作系統内核作為可信計算基(TCB),其安全性是至關重要的。為保證内核的安全性,多種安全機制已經被普遍應用,其中主要包括kASLR和StackGuard。然而這兩種安全機制的有效性的前提是内核中沒有信息洩漏。換句話說,如果有信息洩漏,kASLR和StackGuard将會變得沒有意義,因為它們所依賴的一些随機值會被洩漏。那麼問題來了,内核中是否存在大量的信息洩露?我們通過調研分析發現答案是肯定的:内核中存在大量的信息洩漏漏洞。比如,僅2013年就有近60個有CVE的Linux内核信息漏洞。随着進一步研究發現大部分信息洩漏(大約60%)是因為讀取未初始化内存(uninitializeddataread)導緻,如圖1所示。
可以想象一下,如果建立一個内存對象(memoryobject)但不去初始化它,那麼讀取這個内存對象所得到的數據将會是以前遺留在内存中的一些數據。如果這些數據包含kASLR随機過的地址,StackGuard使用的随機canary,或者是之前用戶遺漏的密碼等重要信息,那麼就會造成這些重要信息的洩漏。而導緻讀取未初始化内存的原因又分為兩種:程序員的疏忽和編譯器的優化。編譯器的問題導緻了一個嚴重且普遍的問題:即使程序員初始化一個對象中的所有成員,仍然有一些字節沒有被初始化。代碼1列出了一個例子。在這個例子中程序員初始化了内核堆棧對象“ci”的每一個成員,然而編譯器在第二個成員“slow”之後插入的3個填充字節仍然沒有初始化,當“copy_to_user”把未初始化字節拷貝給用戶空間時導緻信息洩漏。目前已經有很多攻擊利用這樣的信息洩漏來突破kASLR和StackGuard然後實現iOS越獄,Androidroot等等攻擊。雖然這個問題這麼嚴重,然而,目前并沒有有效地防止讀取未初始化内存的方法或工具。
圖1導緻Linux内核信息洩漏原因統計(2013-2016年)為了解決這個問題讀取未初始化導緻信息安全洩漏,我們提出了一種基于LLVM編譯器的方法。簡單來說,需要通過細顆粒度的(字節級别)、跨函數的、精确的可到達性和初始化性程序分析來檢查是否存在這樣一些不安全内存對象:存在至少一條程序路徑使得一個内存對象在離開内核空間的時候并沒有完全被初始化。當檢測出這樣的内存對象以後,我們會在内核中插入一些代碼對這些内存對象進行置零初始化。在實現UniSan過程中,通過設計很多方法來保證程序分析的完全性(也就是沒有漏報),比如解決了找間接調用對象的問題。目前已經基于LLVM實現了UniSan,并且用它來保護最新的Linux内核和Android内核。實驗結果顯示UniSan保護過的内核運行非常穩定,毫無問題。作為驗證,我們發現UniSan能完全防止已有的信息安全漏洞并且發現大量的新漏洞。
代碼1編譯器優化插入未初始化的填充字節,導緻信息洩漏從性能角度看,UniSan幾乎沒有引起任何性能的降低(大多數情況下,性能影響小于0.5%)。綜合這些實驗結果,UniSan以一種有效的,高性能的,無漏報的方法解決了操作系統内核中由讀取未初始化内存導緻的普遍的信息洩漏問題。
(注:原文發表在ACMCCS2016,更多細節請看UniSan文章:http://www.gatech.edu/~klu38/publications/unisan-ccs16.pdf)