歡迎光臨
每天分享高質量文章

KPTI:核心頁表隔離的當前的發展 | Linux 中國

英特爾處理器曝出了一個嚴重的硬體設計漏洞,迫使包括 Linux、Windows 在內的主要作業系統和各大雲端計算服務商都忙著打補丁。因為漏洞資訊沒有解密,所以目前只能透過已釋出的補丁反推這個漏洞。這裡是一篇對該漏洞的技術分析文章。
— Jonathan Corbet


本文導航
編譯自 | https://lwn.net/SubscriberLink/741878/eb6c9d3913d7cb2b/ 
 作者 | Jonathan Corbet
 譯者 | qhwdw

英特爾處理器曝出了一個嚴重的硬體設計漏洞,迫使包括 Linux、Windows 在內的主要作業系統和各大雲端計算服務商都忙著打補丁[1]。因為漏洞資訊沒有解密,所以目前只能透過已釋出的補丁反推這個漏洞[2]

這裡是一篇對該漏洞的技術分析文章。

在十月底的時候,KAISER[3] 補丁集被披露了;它做了一項工作,將核心空間與使用者空間使用的頁表page tables進行了隔離,以解決 x86 處理器上向攻擊者透露核心佈局的安全漏洞。這些補丁是自它們被公佈以來,這一星期中最值關註的事情,但是,它們似乎正在接近最終的狀態。這應該是再次審視它們的合適機會。

這項工作被重新命名為 “核心頁表隔離kernel page-table isolation” (KPTI),但是目的是一樣的:分割頁表,將現在被使用者空間和核心空間共享使用的這張表分成兩套,核心空間和使用者空間各自使用一個。這對內核的記憶體管理產生了根本性的變化,並且,這是本來人們期望先爭論多年再做決定的,尤其是考慮到它的效能影響的時候。不過,KPTI 仍然處於快速發展的軌道上。一組預備補丁[4] 已被被合併到 4.15 – rc4 之後的主版本線上了 — 一般情況下僅重要的修複才被允許這樣做 — 並且其餘的似乎被確定進入 4.16 版的合併視窗中。許多核心開發者都在這項工作上投入了大量的時間,並且 Linus Torvalds 要求[5] 將這項工作回遷backport到長期穩定核心中。

也就是說,KPTI 已經在最後期限的壓力下安全補丁的所有標記都已經就緒了。對於任何基於 ARM 的讀者,在這裡值的註意的是,在這項工作中有一個 為 arm64 的等效補丁集[6]

51 個補丁乃至更多

在這篇文章中,x86 補丁系列正處在 163 版本[7]。它包含 51 個補丁,因此,我們應該感謝那些沒有公開的版本。最初的補丁集,由 Dave Hansen 釋出,由 Thomas Gleixner、Peter Zijlstra、Andy Lutomirski、和 Hugh Dickins 根據許多其它人的建議,做了大量的修訂。任何還存在的缺陷都不是由於沒有足夠多的有經驗的開發人員過目所導致的。

在現代系統中,頁表是以一個樹形結構進行組織的,這樣可以高效地儲存稀疏記憶體對映和支援巨頁特性;可以檢視這篇 2005 年的文章[8] 瞭解更多細節以及它是怎麼工作的示意圖。在一個有四級頁面表的系統上(目前的大多數大型系統都是這樣),頂級是頁面全域性目錄(PGD)。緊接著是頁面上層目錄(PUD)、頁面中層目錄(PMD)和頁面表條目(PTE)。有五級頁面表的系統是在 PGD 下麵插入了一層(稱為 P4D)。

頁面故障解析通常遍歷整個樹去查詢所需的 PTE,但是,巨頁可以被更高層級的特定條目所表示。例如,一個 2MB 的記憶體chunk既可以由 PMD 層級的一個單個的巨頁條目表示,也可以由一個單頁 PTE 條目的完整頁面表示。

在當前的核心中,每個處理器有一個單個的 PGD;在 KPTI 系列補丁中所採取的第一步的其中一個措施是,去建立一個第二個 PGD。當核心執行時,原來的仍然在使用;它對映所有的地址空間。當處理器執行在使用者空間時,(在打完該系列補丁之後)第二個被啟用。它指向屬於該行程的頁面的相同目錄層次,但是,描述核心空間(位於虛擬地址空間的頂端)的部分通常都不在這裡。

頁表條目包含許可權描述位,它們記錄了記憶體該如何被訪問;不用說都知道,這些位是用來設定阻止使用者空間訪問核心頁面的,即便是透過那些被對映到該地址空間的頁面訪問。不幸的是,一些硬體級的錯誤允許使用者空間的攻擊者去確定一個給定的核心空間地址是否被對映,而不管那個頁面上對映的地址是否被允許訪問。而那個資訊可以被用於擊敗核心地址空間佈局隨機化,可以使一個本地的攻擊者更易於得逞。在 KPTI 背後的核心思想是,切換到一個沒有核心空間對映的 PGD,將會使基於這個漏洞的攻擊失效,而到現在為止,我們還沒有看到這些攻擊。

細節

這個想法很簡單,但是,就像經常發生的那樣,有各種各樣麻煩的細節使得這個簡單的想法變成了一個由 51 個部分構成的補丁集。其中最初的一個是,如果處理器在使用者樣式執行時響應一個硬體中斷,處理中斷需要的核心程式碼將在地址空間中不存在。因此,必須有足夠的核心程式碼對映在使用者樣式中,以能夠切換回到核心 PGD,使剩餘的程式碼也可用。對於 traps、非遮蔽中斷、和系統呼叫,也存在一個相似的情況。這個程式碼很小而且可以與其它部分隔離,但是,在處理安全且有效地切換時,涉及到一些很複雜的細節。

另一個難題來自 x86 本地描述符表(LDT)的構成,它可以被用於去改變使用者空間的記憶體佈局。它可以使用鮮為人知的 modify_ldt()[9] 系統呼叫來做微調。例如,在 Linux 上早期的 POSIX 執行緒實現,使用了 LDT 去建立一個本地執行緒儲存區域。在現在的 Linux 系統上,LDT 幾乎不再使用了,但是,一些應用程式(比如,Wine)仍然需要它。當它被使用時,LDT 必須能夠被使用者空間和核心空間都可以訪問到,但是,它必須一直處於核心空間中。KPTI 補丁集打亂核心附近的記憶體,在 PGD 級別上為 LDT 保留一個完全的條目;因此,vmalloc() 呼叫的可用空間收縮到僅有 12,800TB。那是一個非常巨大的 LDT 空間數,可以滿足有很多 CPU 系統的需要。這種變化的其中一個結果是,LDT 的位置將是固定的,並且已知道使用者空間 ——因此這將是個潛在的問題,由於覆寫 LDT 的能力很容易被用來破壞整個系統。在這個系列的最終的補丁是對映為只讀 LDT,以阻止此類攻擊。

另一個潛在的安全缺陷是,如果內核可以一直被操縱直至傳回用戶空間,以至於不切換回經過過濾的 PGD。因為核心空間 PGD 也對映使用者空間記憶體,這種疏忽可能在一段時間內不會被察覺到。對此問題的應對方法是將虛擬記憶體空間中使用者空間的部分以非可執行的方式對映到內核的 PGD。只要使用者空間(的程式)開始從一個錯誤的頁面表開始執行,它將會立即崩潰。

最後,雖然所有已存在的 x86 處理器似乎都會受到這個資訊洩露的漏洞影響,但是,以後的處理器可能不會受此影響。KPTI 有一個可測量的執行時成本,估計在 5%。有些使用者也許不願意為這些成本埋單,尤其是他們拿到了不存在這個問題的新處理器之後。為此將會有一個 nopti(核心)命令列選項來在啟動的時候禁用這個機制。這個補丁系列也增加了一個新的“特性”標識(X86_BUG_CPU_INSECURE)去標識有漏洞的 CPU;它被設定在現在所有的 x86 CPU 上(LCTT 譯註:AMD 表示不背這鍋),但是在以後的硬體上可能沒有。如果沒有該特性標識,頁面隔離將自動被關閉。

在 4.16 版的合併視窗開啟之前剩下將近一個月。在這期間,針對一些新發現而不可避免的小毛病,KPTI 補丁集毫無疑問的將迎來一系列的小修訂。一旦所有的事情都敲定了,看起來這些程式碼將會被合併同時以相對快的速度遷回到穩定版本的核心。顯而易見的是,我們將會收到一個更慢,但是更安全的核心作為一個新年禮物。


via: https://lwn.net/SubscriberLink/741878/eb6c9d3913d7cb2b/

作者:Jonathan Corbet[10] 譯者:qhwdw 校對:wxy

本文由 LCTT 原創編譯,Linux中國 榮譽推出

LCTT 譯者

qhwdw ? ? ? ?
共計翻譯:38 篇
貢獻時間:65 天


推薦文章

< 左右滑動檢視相關文章 >

點選圖片、輸入文章 ID 或識別二維碼直達

贊(0)

分享創造快樂