Meltdown 漏洞
/ MELT-down /
特權界線本應是絕對的:使用者程式不能讀作業系統核心的記憶體,因為 CPU 在每次存取都檢查權限,不被允許就拒絕(見 privilege-level 與 memory-protection)。Meltdown 與 Spectre 在 2018 年一同揭露,正是那令人不安的發現:在許多處理器上,這個權限檢查發生得太晚,以致對側通道而言為時已晚。CPU 會在檢查完成前就推測性地使用那筆被禁止的資料,而即使它在發現存取違法時把結果丟掉,資料早已在快取裡留下痕跡。
機制上:使用者程式發出一次對它無權讀取的核心位址的載入。亂序執行一路向前衝,取了那個核心位元組並轉送給一條相依指令——它用那個秘密位元組去索引陣列,拉進一條依秘密而定的快取列——這一切都在權限檢查算出來之前發生。當檢查終於觸發,CPU 引發一個故障並抹除架構效果,於是程式官方上從沒讀到核心資料。但那條快取列是暖的。一次快取時序量測(Flush+Reload)就從快取狀態還原出那個核心位元組。在各位址間重複,一個使用者程式就能傾印它被斷然禁止看見的核心記憶體(見 cache-timing-attack)。
為什麼 Meltdown 被誠實地教成有別於 Spectre:Meltdown 在受影響的晶片上直接攻破了使用者對核心的特權壁壘,這也是它能以高昂代價用軟體修補的原因。修法 KPTI(核心分頁表隔離)乾脆完全不再把核心記憶體映射進使用者行程的位址空間,於是推測載入根本無物可讀——但這讓每次系統呼叫更昂貴,在某些工作負載上有實在的拖慢。關鍵是:Meltdown 大致是某些處理器把權限檢查相對於推測排序的方式之瑕疵;較新的設計在轉送前就檢查,並不受影響。相比之下,Spectre 是推測本質的一部分,要根除難得多。但兩者都教了同一個硬道理:微架構能跨越 ISA 承諾穩固的界線洩漏。
一個使用者程式執行:byte secret = *(kernel_address); y = probe[ secret x 256 ]。它無權讀 kernel_address,故障無可避免。但亂序執行在故障被送達前,已把核心位元組轉送給第二行、並快取了 probe[secret x 256]。程式接住故障,再對 probe 各列計時:那唯一快的列洩漏出 secret——一個從使用者模式讀到的核心記憶體位元組。
違法的讀取被抹除了,但它已先快取一條列——權限檢查來得太晚,攔不住側通道。
與 Spectre 不同,Meltdown 源於把權限檢查相對於推測排得太晚,所以能用軟體(KPTI)緩解,而在轉送前就檢查的新 CPU 免疫——但這個修法對每次系統呼叫課稅。這裡的「已修補」意味著變慢,而非免費。