現(xiàn)代CPU為了優(yōu)化速度,會(huì)在等待某些慢速操作(內(nèi)存訪(fǎng)問(wèn),分支判斷,權(quán)限檢查等)返回之前,提前執(zhí)行后面的代碼,如果執(zhí)行的不對(duì),就回退掉之前執(zhí)行的結(jié)果,但是回退的不是很干凈,會(huì)存在某些殘余(加載到CPU緩存中的內(nèi)存數(shù)據(jù)不會(huì)被清除等),基于這些殘余,就有可能繞過(guò)一些安全機(jī)制,獲取到一些非法數(shù)據(jù)。
Meltdown
利用了CPU中的亂序執(zhí)行(Out-of-order execution)。CPU在內(nèi)存訪(fǎng)問(wèn)和權(quán)限檢查之間有個(gè)競(jìng)爭(zhēng),CPU訪(fǎng)問(wèn)一個(gè)沒(méi)有權(quán)限的地址,在亂序執(zhí)行期間被暫時(shí)加載到CPU的緩存中。如果權(quán)限檢查沒(méi)有通過(guò),加載到緩存中的內(nèi)容不會(huì)被清除。通過(guò)測(cè)量?jī)?nèi)存訪(fǎng)問(wèn)的時(shí)間(加載到緩存里的數(shù)據(jù)用時(shí)少),就可以推斷出沒(méi)有權(quán)限的地址里的內(nèi)容。
1 ; rcx = kernel address, rbx = probe array
2 xor rax, rax
3 retry:
4 mov al, byte [rcx]
5 shl rax, 0xc
6 jz retry
7 mov rbx, qword [rbx + rax]
Meltdown的核心:一個(gè)無(wú)法訪(fǎng)問(wèn)的內(nèi)核地址被移到一個(gè)寄存器中(指令4),引發(fā)異常。后面的指令(指令5,6,7)應(yīng)該不會(huì)被執(zhí)行到,但是由于CPU存在亂序執(zhí)行,所以后面的指令(指令5,6,7)在異常發(fā)生前被執(zhí)行了。導(dǎo)致qword中的數(shù)據(jù)被加載到cpu緩存中,通過(guò)測(cè)量qword數(shù)組中數(shù)據(jù)的訪(fǎng)問(wèn)時(shí)間,找出用時(shí)最快的就可以推導(dǎo)出rax的值,從而得到rcx的值。
spectre
利用了CPU的分支預(yù)測(cè)(branch prediction),有很多變種,這里只介紹幾個(gè)最簡(jiǎn)單的,存在CPU分支預(yù)測(cè)的點(diǎn),幾乎都可以通過(guò)訓(xùn)練,使CPU提前運(yùn)行非法代碼。
- Bounds Check Bypass
if (x < array1_size)
y = array2[array1[x] * 4096];
if語(yǔ)句中對(duì)于x的判斷,可以保證x的合法,重復(fù)運(yùn)行這段代碼,引導(dǎo)CPU的分支預(yù)測(cè)提前運(yùn)行后面的代碼。這時(shí)把x的值設(shè)成大于array1_size的值,后面的代碼也會(huì)在條件檢查結(jié)果出來(lái)之前提前被CPU運(yùn)行。通過(guò)測(cè)量array2的CPU緩存就能推導(dǎo)出array1越界內(nèi)存中的值。
- Branch Target Injection
懶了,直接看這個(gè)吧Static calls in Linux 5.10