ZenonXiu修志龍
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? MindShare思享? ? ? ? ? ? ? ? ? ? ? 1月17日
原創(chuàng)聲明:未經(jīng)作者許可,不許轉(zhuǎn)載。本文純屬個(gè)人觀點(diǎn)
Meltdown概述
Meltdown破壞了位于用戶和操作系統(tǒng)之間的基本隔離,允許惡意代碼訪問內(nèi)核內(nèi)存,從而竊取其他應(yīng)用程序以及OS數(shù)據(jù)。這個(gè)漏洞“熔化”了由硬件來實(shí)現(xiàn)的安全邊界。低權(quán)限用戶級別的應(yīng)用程序能利用它“越界”間接獲取內(nèi)存數(shù)據(jù)。
Meltdown 允許能夠在存
在漏洞的處理器上運(yùn)行代碼,
獲得整個(gè)內(nèi)核的數(shù)據(jù)。 Meltdown 的根本
原因是推測性和亂序執(zhí)行造成的。?
有關(guān)Meltdown的介紹文章已經(jīng)有很多,在本文中不再贅述.
本文主要說明arm64 Linux kernel是如何通過KPTI(Kernel Page Table Isolation 技術(shù)來解決Meltdown的問題。
需要說明的是,在Meltdown和Spectre問題爆發(fā)之前,Arm已經(jīng)有計(jì)劃利用KPTI (Kaiser)技術(shù)實(shí)現(xiàn)KASLR(Kernel Address Space Layout Randomization). ?Kaiser?防御機(jī)制具有阻
止 Meltdown攻擊的作用.
Meltdown on Arm processor
Arm
Cortex-A processors 中只有Cortex-A75受到Meltdown(Variant3) 的影響,Cortex-A75
DynamIQ處理器是目前Arm公布的最高性能的處理器(所以out of order最多). 在Arm的security
whitepaper里Meltdown對應(yīng)的是Variant 3 和 3a. KPTI是針對variant 3. 因?yàn)?a
只能讀到部分更高EL的system registers(不能改寫), 到目前為止,還沒有能利用這個(gè)實(shí)現(xiàn)攻擊的辦法.?
并且Cortex-A75也不受3a影響。
White paper 可以從這里下到?https://developer.arm.com/support/security-update/download-the-whitepaper?
問題描述:
Cortex-A75在更低的EL(比如user
application in EL0)時(shí),可以speculatively去訪問更高EL(比如kernel in
EL1)才有訪問權(quán)限的數(shù)據(jù),雖然最終application代碼不能通過load/store指令得到kernel的data(最終如果這個(gè)load被architectually執(zhí)行會(huì)觸發(fā)MMU訪問權(quán)限fault,
導(dǎo)致segment fault), 但是speculation已經(jīng)將某些數(shù)據(jù)帶到cache里,通過精心設(shè)計(jì)的cache
FLUSH+RELOAD 側(cè)邊道攻擊,可以通過間接手段得到kernel的數(shù)據(jù)。
所以這個(gè)問題的發(fā)生由 (在低特權(quán)級的ELspeculative access 更高EL數(shù)據(jù))+(精心設(shè)計(jì)的低特權(quán)級代碼)+(Cache FLUSH+RELOAD side channel attack)來觸發(fā)的。
參考white paper 提供的代碼
在EL0,有下面代碼
1 ?LDR X1, [X2] ; 精心設(shè)計(jì)這個(gè)load產(chǎn)生cache miss, 以便CPU會(huì)speculative做 4和7的訪問
2 ?CBZ X1, over ; 這個(gè)跳轉(zhuǎn)按代碼邏輯會(huì)跳
3 ? ? ? ? ? ? ? ; 但是CPU預(yù)測不跳
4 ?LDR X3, [X4] ; X4指向只能kernel訪問的kernel space地址
5 ?LSL X3, X3, #imm
6 ?AND X3, X3, #0xFC0 ?;移位成 probe buffer的offset7 ?LDR X5, [X6,X3] ; X6 是user application可以訪問的地址,用來做cache side
;channel attack 的probe buffer8 over
1.首先 用Cache FLUSH+RELOAD的辦法講probe buffer對應(yīng)的數(shù)據(jù)都從cache 趕出去。
2. 制作條件(讓上面1對應(yīng)的load miss in cache)
3. 執(zhí)行以上代碼
3. 因?yàn)镾peculation, CPU會(huì)speculatively load 以上代碼的4和7,因?yàn)槭莝peculation, 指令不會(huì)retire和回寫到X3和X5. 但是數(shù)據(jù)可以帶進(jìn)cache.
? 比如如果kernel數(shù)據(jù)是1的話,在probe buffer 里offset 為0x1000對應(yīng)的數(shù)據(jù)會(huì)進(jìn)cache
? 如果kernel數(shù)據(jù)是0的話,在probe buffer 里offset 為0x0000對應(yīng)的數(shù)據(jù)會(huì)進(jìn)cache
4. 如果最終architectually 執(zhí)行?
4 ?LDR X3, [X4]
會(huì)導(dǎo)致訪問權(quán)限segment fault.
5. 攻擊者可以以cache line size 為stirde遍歷訪問probe buffer的數(shù)據(jù), 并測量每個(gè)訪問時(shí)間,如果那個(gè)訪問時(shí)間短,說明其數(shù)據(jù)已經(jīng)在cache 里面。再利用step3,可以反推出kernel data是 0還是1.
比如如果對probe buffer offset 為0x000的數(shù)據(jù)訪問時(shí)間短,那么可推出kernel data是0
比如如果對probe buffer offset 為0x000的數(shù)據(jù)訪問時(shí)間短,那么可推出kernel data是1
理解上面的內(nèi)容需要對CPU設(shè)計(jì)有比較好的理解,因?yàn)楸救艘恢敝С諥rm 構(gòu)架和CPU, 所以理解并不費(fèi)事。
為了幫助理解,貼一個(gè)我認(rèn)為最接近的比喻,
我們把CPU比做學(xué)校食堂,把黑客比作兩個(gè)男生A,B,用戶則是女神。
這天,男生A,B總要想辦法獲得女神的一點(diǎn)私密信息——比如,女神今天午飯吃的啥~
中午,女神來到食堂打飯,點(diǎn)了一份小籠包。
男生A在女神后面跟食堂大娘說:我也來一份,跟她一樣的~
然而這會(huì)食堂大娘表示,你等會(huì),你前面還有人哦。
好吧,雖然說是這么說,但是后面的廚房師傅已經(jīng)聽到了對話,已經(jīng)提前開始準(zhǔn)備好了另一份小籠包.....
后來女神點(diǎn)好走了,輪到男生A點(diǎn),他表示,我要一個(gè)跟她一樣的...
然而這會(huì),
食堂大娘表示,人家是人家,你是你,我們不能透露女神隱私喔,你可以走了,下一個(gè)! (這就是目前CPU的內(nèi)置的安全防線)
然而!
當(dāng)下一個(gè)男生B走到食堂大娘面前,直接說:隨便,哪道菜最快給我上哪道...
于是乎,既然之前廚房師傅已經(jīng)提前多準(zhǔn)備好了一份小籠包,就干脆直接把小籠包給了男生B....
這下男生知道了,女神中午吃了小籠包......
關(guān)鍵信息,就這么被泄露了,
雖然以上比喻中有很多不貼切
并沒有關(guān)鍵的用kernel data作為 probe buffer index部分。。。
男生和女生的包子應(yīng)該是隔離的
Meltdown 防御 on Arm64 Linux
接下來的內(nèi)容假設(shè)大家對Arm Linux 比較熟悉。
通過以上分析,我們知道了這個(gè)問題最關(guān)鍵的地方是,
在EL0運(yùn)行application時(shí),application
speculatively訪問kernel address時(shí), MMU可以做這個(gè)地址轉(zhuǎn)換,MMU
hardware并不檢查訪問權(quán)限,只做VA到PA的地址轉(zhuǎn)換. 得到PA后可以做memory access到cache中。
有人會(huì)問,為什么MMU不做訪問權(quán)限檢查,地址轉(zhuǎn)換同時(shí)做訪問權(quán)限檢查不是隨便可以做的事情嗎?對software
engineer來說好像工作并不多,但是對CPU的設(shè)計(jì)者來說,在RTL關(guān)鍵路徑(critical
path)上作一些看似不多的工作會(huì)帶來后端綜合的timing收斂的問題,影響到CPU可以跑到的最高頻率。
并且現(xiàn)代CPU的設(shè)計(jì)也不能禁止 speculation, out of order,我們不能因噎廢食,否則性能可能一夜回到解放前。
解決方案是什么呢? 方法就是,
在運(yùn)行user
application 的時(shí)候,將kernel mapping 減少到最少,只保留必須的user到kernel的exception entry
mapping. 其他的kernel mapping 在運(yùn)行user application時(shí)都去掉,變成無效mapping,
這樣的話,如果user訪問kernel data, 在MMU地址轉(zhuǎn)換的時(shí)候就會(huì)被擋掉(因?yàn)闊o效mapping).
設(shè)計(jì)方面就是設(shè)計(jì)一個(gè)trampoline 的kernel PGD給運(yùn)行user時(shí)用。
Trampoline kernel mapping PGD只包含exception entry必需的mapping.
當(dāng)user
通過系統(tǒng)調(diào)用,或是timer或其他異常進(jìn)入kernel是首先用trampoline的mapping,
接下來tramponline的vector處理會(huì)將kernel mapping 換成正常的kernel mapping
(SWAPPER_PGD_DIR), 并直接跳轉(zhuǎn)到kernel原來的vector entry, 繼續(xù)正常處理。
我們把上述過程稱之為map kernel mapping.
當(dāng)從kernel返回到user時(shí),正常的kernel_exit會(huì)調(diào)用trampoline的exit,tramp_exit會(huì)重新將kernel mapping 換成是trampoline. 這個(gè)過程叫unmap kernel mapping.
相關(guān)代碼請看Will Deacon 的patch set
[v2,13/18] arm64: entry: Hook up entry trampoline to exception vectors- - -2017-11-30Will DeaconNew
[v2,12/18] arm64: entry: Explicitly pass exception level to kernel_ventry macro- - -2017-11-30Will DeaconNew
[v2,11/18] arm64: mm: Map entry trampoline into trampoline and kernel page tables- - -2017-11-30Will DeaconNew
[v2,10/18] arm64: entry: Add exception trampoline page for exceptions from EL0
https://patchwork.kernel.org/patch/10085275/
如果大家對Arm MMU, TLB工作原理比較熟悉的話,可能會(huì)進(jìn)一步想到一個(gè)問題。User和kernel的translation是共享同一TLB 硬件的。
大家知道MMU會(huì)現(xiàn)在TLB里lookup,
我們需要避免user application發(fā)出的kernel address可以在TLB
hit,因?yàn)門LB中可能已經(jīng)有了用過的SWAPPER_PGD_DIR mapping的entry.
在KPTI之前kernel的mapping都是global(不匹配ASID). User application發(fā)出的kernel
address translation可以直接在TLB hit。這樣的話KPTI的translation table隔離機(jī)制就不work了。
這個(gè)問題可以用在kernel和user切換時(shí)對整個(gè)TLB invalidate,但是代價(jià)很高,我們要避免。
怎么處理呢?KPTI會(huì)
1. 給kernel space 也分配ASID,kernel mapping也變成和userspace一樣的non global mapping.
2. kernel分配ASID的時(shí)候,分出一個(gè)奇偶ASID對,kernel用偶的,user用奇的,從2開始分。比如kernel ASID 2, user ASID3 或kernel ASID 4, user ASID 5.
這樣的話因?yàn)檫\(yùn)行在user時(shí)的ASID和kernel時(shí)的ASID不一樣,user發(fā)出的地址沒法hit到kernel的TLB entry。
這個(gè)問題會(huì)在后面的圖示里表達(dá)比較清楚。
代碼請參照 ?Will Deacon patch set
[03/18] arm64: mm: Move ASID from TTBR0 to TTBR1- - -2017-11-17Will DeaconNew
[02/18] arm64: mm: Temporarily disable ARM64_SW_TTBR0_PAN- - -2017-11-17Will DeaconNew
[01/18] arm64: mm: Use non-global mappings for kernel space
[07/18] arm64: mm: Allocate ASIDs in pairs- - -2017-11-17Will DeaconNew
[06/18] arm64: mm: Fix and re-enable ARM64_SW_TTBR0_PAN
需要說明的是,雖然trampoline是個(gè)聰明的辦法,但是KPTI的開發(fā)還在演化中,以后可能會(huì)有變化。
Arm64 KPTI 圖示
代碼可以大家去看,但是看懂代碼需要對Arm 構(gòu)架和Linux kernel要比較了解。
為了幫助大家理解,我連夜畫了幾個(gè)圖。
圖1 沒有KTPI時(shí)運(yùn)行在user application的地址轉(zhuǎn)換和訪問
圖2 沒有KTPI時(shí)運(yùn)行在kernel的地址轉(zhuǎn)換和訪問
圖3 有KTPI時(shí)運(yùn)行在user application的地址轉(zhuǎn)換和訪問
圖4 ?有KTPI時(shí)運(yùn)行在kernel的地址轉(zhuǎn)換和訪問
圖5 Trampoline工作流程
總結(jié)
Arm64 Linux KPTI巧妙地通過trampoline以最小kernel改動(dòng)實(shí)現(xiàn)了Meltdown的防御。
KPTI可以通過enable一下kernel configuration來開關(guān)。
UNMAP_KERNEL_AT_EL0
Kernel patch set可以在這里找到,
https://patchwork.kernel.org/project/linux-arm-kernel/list/?submitter=will+deacon
Meltdown(variant 3) 只對Cortex-A75有影響。