翻譯:為什么樹(shù)莓派不會(huì)受到 Spectre 和 Meltdown 攻擊

譯注:最近爆出來(lái)的 Intel CPU 的底層漏洞可謂是影響巨大,過(guò)去20年的電腦都可能會(huì)受影響。前幾天 Raspberry Pi 的官方 Twitter(@Raspberry_Pi) 轉(zhuǎn)推了這篇文章,通過(guò)簡(jiǎn)單的 Python 程序分析了各種硬件術(shù)語(yǔ)和漏洞攻擊模式,內(nèi)容簡(jiǎn)單易懂,看后神清氣爽。今天抽空將其翻譯,分享給大家,如有錯(cuò)誤請(qǐng)多多包涵?!?018年1月8日


原文地址:https://www.raspberrypi.org/blog/why-raspberry-pi-isnt-vulnerable-to-spectre-or-meltdown

在過(guò)去的幾天里,有許多關(guān)于一對(duì)叫 Spectre 和 Meltdown 的安全漏洞的討論。這影響到所有近代的英特爾處理器,許多AMD處理器(在 Spectre 漏洞下)和 ARM 核心。 Spectre 允許攻擊者繞過(guò)軟件檢查,去讀取當(dāng)前地址空間中任意位置的數(shù)據(jù); Meltdown 允許攻擊者去讀取操作系統(tǒng)內(nèi)核地址空間中(通常對(duì)用戶程序不可訪問(wèn))任意位置的數(shù)據(jù)。

這兩個(gè)漏洞利用許多現(xiàn)代處理器常見(jiàn)的性能特征(緩存和預(yù)測(cè)執(zhí)行),通過(guò)所謂的側(cè)信道攻擊(side-channel attack)來(lái)泄漏數(shù)據(jù)。幸運(yùn)的是,樹(shù)莓派不會(huì)受到這些漏洞的影響,因?yàn)槲覀兪褂锰貏e的(particular)ARM 內(nèi)核。

為了幫助我們理解為什么,這里有一點(diǎn)關(guān)于現(xiàn)代處理器設(shè)計(jì)中的一些概念。我們將使用像下面那樣的簡(jiǎn)單的 Python 程序去說(shuō)明這些概念:

t = a+b
u = c+d
v = e+f
w = v+g
x = h+i
y = j+k

雖然計(jì)算機(jī)中的處理器不直接執(zhí)行 Python ,但這里的語(yǔ)句很簡(jiǎn)單,它們大致相當(dāng)于一個(gè)機(jī)器指令。我們將詳細(xì)介紹一些細(xì)節(jié)(尤其是流水線(pipelining)和寄存器重命名(register renaming)),這對(duì)于處理器設(shè)計(jì)者來(lái)說(shuō)非常重要,但并不是理解 Spectre 和 Meltdown 所必須的。

為了綜合描述處理器設(shè)計(jì)和現(xiàn)代計(jì)算機(jī)體系結(jié)構(gòu)的其他方面,你不能做得比 Hennessy and Patterson’s classic Computer 體系結(jié)構(gòu)更好:一種定量方法。(原文:you can’t do better than Hennessy and Patterson’s classic Computer Architecture: A Quantitative Approach.

什么是標(biāo)量處理器

最簡(jiǎn)單的現(xiàn)代處理器每周期執(zhí)行一條指令,我們稱之為標(biāo)量處理器(scalar processor)。上面的示例將在標(biāo)量處理器上以六個(gè)周期執(zhí)行。

標(biāo)量處理器的例子包括 Intel 486 和在 Raspberry Pi 1 與 Raspberry Pi Zero 上使用的 ARM1176 核心。

什么是超標(biāo)量處理器

使標(biāo)量處理器(實(shí)際上是任何處理器)運(yùn)行得更快的明顯方法是增加它的時(shí)鐘速度(clock speed)。但是,我們很快達(dá)到了處理器內(nèi)部邏輯門(mén)運(yùn)行速度的極限。因此,處理器設(shè)計(jì)者開(kāi)始尋找?guī)追N同時(shí)執(zhí)行多個(gè)指令的方法。

順序(in-order)超標(biāo)量處理器(superscalar processor)檢查傳入的指令流,并嘗試在一個(gè)流水線(pipelines -> pipes)中同時(shí)執(zhí)行多個(gè)指令流,但要遵守指令之間的依賴關(guān)系。依賴關(guān)系很重要:你可能認(rèn)為雙路(two-way)超標(biāo)量處理器可以結(jié)對(duì)(dual-issue)六個(gè)指令,像下面的例子一樣:

t, u = a+b, c+d
v, w = e+f, v+g
x, y = h+i, j+k

但這沒(méi)有意義:在計(jì)算 w 之前,我們必須計(jì)算 v ,所以第三和第四指令不能同時(shí)執(zhí)行。我們的雙路超標(biāo)量處理器實(shí)際上不可能找到任何與第三指令相匹配的指令,所以我們的示例將以四個(gè)周期執(zhí)行:

t, u = a+b, c+d
v    = e+f                   # second pipe does nothing here
w, x = v+g, h+i
y    = j+k

超標(biāo)量處理器的例子包括 Intel Pentium ,在 Raspberry Pi 2 與 Raspberry Pi 3 上使用的 ARM Cortex-A7 與 Cortex-A53 核心。 Raspberry Pi 3 的時(shí)鐘速度只比 Raspberry Pi 2 快了 33% ,但性能近乎翻倍:額外性能的部分原因是 Cortex-A53 的指令結(jié)對(duì)能力比 Cortex-A7 具有更廣泛的指令范圍。

什么是亂序處理器

回到我們的例子,我們可以看到,雖然我們?cè)?v 和 w 之間有一個(gè)依賴項(xiàng),但是在程序的后面有其他的獨(dú)立指令,我們或許可以在第二個(gè)周期中用來(lái)填充流水線。亂序(out-of-order)超標(biāo)量處理器具有打亂即將到來(lái)的指令的能力(遵循依賴關(guān)系),以便提高流水線的效率。

在我們的示例中,亂序處理器可能有效的交換 w 和 x 的定義:

t = a+b
u = c+d
v = e+f
x = h+i
w = v+g
y = j+k

將其以三個(gè)周期執(zhí)行:

t, u = a+b, c+d
v, x = e+f, h+i
w, y = v+g, j+k

亂序處理器的例子包括 Intel Pentium 2 (絕大多數(shù)的 Intel 和 AMD x86 處理器,除了一些 Intel Atom 和 Intel Quark 設(shè)備),最新的 ARM 核心,像 Cortex-A9, -A15, -A17, and -A57 。

什么是分支預(yù)測(cè)器

上面的例子是一段順序代碼。當(dāng)然,真正的程序不是這樣的:它們還包含向前分支(forward branches,用于實(shí)現(xiàn)條件操作,如if語(yǔ)句)和向后分支(backward branches,用于實(shí)現(xiàn)循環(huán))。分支可能是無(wú)條件的(總是執(zhí)行),或條件的(是否執(zhí)行取決于計(jì)算值)。

在獲取指令時(shí),處理器可能遇到依賴于尚未計(jì)算值的條件分支。為了避免停頓,處理器必須猜測(cè)下一個(gè)要取的指令:在內(nèi)存中的一個(gè)指令(對(duì)應(yīng)不執(zhí)行分支),或分支目標(biāo)中的一個(gè)(對(duì)應(yīng)執(zhí)行分支)。分支預(yù)測(cè)器(branch predictor)可幫助處理器對(duì)是否執(zhí)行分支進(jìn)行智能猜測(cè)。它通過(guò)收集有關(guān)過(guò)去特定分支的執(zhí)行頻率的統(tǒng)計(jì)數(shù)據(jù)來(lái)做到這一點(diǎn)。

現(xiàn)代分支預(yù)測(cè)是非常復(fù)雜的,可以產(chǎn)生非常準(zhǔn)確的預(yù)測(cè)。Raspberry Pi 3 的額外性能的部分原因是由于分支預(yù)測(cè)在 Cortex-A7 和 Cortex-A53 之間的改進(jìn)。然而,通過(guò)執(zhí)行精心編制的一系列分支,攻擊者可以錯(cuò)誤地訓(xùn)練分支預(yù)測(cè)器,從而做出糟糕的預(yù)測(cè)。

什么是推測(cè)

重排(reordering)順序指令是使更多指令級(jí)并行的強(qiáng)有力方法,但是隨著處理器變得更強(qiáng)大(能夠?qū)⑷蛩膫€(gè)指令結(jié)對(duì)),要使所有這些流水線忙起來(lái)變得困難。因此,現(xiàn)代處理器推測(cè)(speculation)的能力也變得更強(qiáng)。推測(cè)執(zhí)行允許我們發(fā)出可能不需要的指令(因?yàn)榇a可能會(huì)存在分支),這會(huì)使流水線保持繁忙(使用或丟棄),如果結(jié)果表明該指令未被執(zhí)行,我們就可以將其丟棄。

推測(cè)執(zhí)行不必要的指令(底層需要支持推測(cè)和重排)消耗額外的時(shí)間,但在許多情況下,這被認(rèn)為是獲得額外單線程性能的一個(gè)合算的折衷。分支預(yù)測(cè)器被用來(lái)選擇程序最可能的路徑,最大限度地提高推測(cè)的回報(bào)。

為了演示推測(cè)的好處,讓我們看看另一個(gè)例子:

t = a+b
u = t+c
v = u+d
if v:
   w = e+f
   x = w+g
   y = x+h

現(xiàn)在我們有了從 t 到 u 到 v ,從 w 到 x 到 y 的依賴關(guān)系,所以沒(méi)有推測(cè)的雙路亂序處理器永遠(yuǎn)不能填滿它的第二個(gè)流水線。處理器花費(fèi)三個(gè)周期計(jì)算 t 、 u 和 v ,之后將知道 if 語(yǔ)句的主體部分是否執(zhí)行,在執(zhí)行 if 語(yǔ)句主體的情況下,再花費(fèi)三個(gè)周期計(jì)算 w 、 x 和 y 。假設(shè) if 語(yǔ)句(由一個(gè)分支指令實(shí)現(xiàn))需要一個(gè)周期,我們的示例將花費(fèi)四個(gè)周期(如果 v 為 0)或七個(gè)周期(如果 v 為非 0)。

如果分支預(yù)測(cè)器表明該 if 語(yǔ)句體可能執(zhí)行,經(jīng)推測(cè)有效地打亂后的程序是這樣的:

t = a+b
u = t+c
v = u+d
w_ = e+f
x_ = w_+g
y_ = x_+h
if v:
   w, x, y = w_, x_, y_

因此我們現(xiàn)在有了額外的指令并行來(lái)保持我們的流水線繁忙:

t, w_ = a+b, e+f
u, x_ = t+c, w_+g
v, y_ = u+d, x_+h
if v:
   w, x, y = w_, x_, y_

循環(huán)計(jì)數(shù)在推測(cè)亂序處理器中定義不太好(原文:Cycle counting becomes less well defined in speculative out-of-order processors),但 w 、 x 和 y 的分支和條件更新是大約不占用時(shí)間的,所以我們的示例大約在三個(gè)周期中執(zhí)行。

什么是緩存

在過(guò)去的好日子里,處理器的速度與內(nèi)存訪問(wèn)速度匹配得很好。我的 BBC Micro 有 2MHz ,執(zhí)行一條指令大約 2μs ,存儲(chǔ)周期(memory cycle time)為 0.25μs 。在接下來(lái)的35年里,處理器已經(jīng)變得很快,但內(nèi)存還僅僅是那樣。在 Raspberry Pi 3 中的一個(gè) Cortex-A53 核心,執(zhí)行一條指令大約 0.5ns ,但可能需要多達(dá) 100ns 去訪問(wèn)主存。

乍一看,這聽(tīng)起來(lái)像一個(gè)災(zāi)難:我們每次訪問(wèn)內(nèi)存,要等待 100ns 后才得到結(jié)果返回。下面這個(gè)例子需要花費(fèi) 200ns :

a = mem[0]
b = mem[1]

然而,在實(shí)際中,程序傾向于以相對(duì)可預(yù)測(cè)的方式去訪問(wèn)內(nèi)存,同時(shí)顯示時(shí)間局部性(temporal locality ,如果我訪問(wèn)一個(gè)位置,我很可能很快就會(huì)再次訪問(wèn)它)和空間局部性(spatial locality ,如果我訪問(wèn)一個(gè)位置,我很可能很快就會(huì)訪問(wèn)它附近的位置)。緩存利用了這些特性,以減少訪問(wèn)內(nèi)存的平均成本。

緩存是一個(gè)容量小的芯片存儲(chǔ)器,靠近處理器,存儲(chǔ)最近使用的地址(及其附近)的內(nèi)容的副本,以便它們?cè)诤罄m(xù)訪問(wèn)中很快可用。有了緩存,上面的例子會(huì)執(zhí)行一個(gè)多 100ns :

a = mem[0]    # 100ns delay, copies mem[0:15] into cache
b = mem[1]    # mem[1] is in the cache

從 Spectre 和 Meltdown 的角度來(lái)看,重要的一點(diǎn)是,如果能夠計(jì)算內(nèi)存訪問(wèn)的時(shí)間,就可以判定所訪問(wèn)的地址是否在緩存。

什么是側(cè)信道

維基百科zh-cn

側(cè)信道攻擊(英語(yǔ):Side-channel attack)是一種攻擊方式,它基于從密碼系統(tǒng)的物理實(shí)現(xiàn)中獲取的信息而非暴力破解法或是算法中的理論性弱點(diǎn)(較之密碼分析)。例如:時(shí)間信息、功率消耗、電磁泄露或甚是聲音可以提供額外的信息來(lái)源,這可被利用于進(jìn)一步對(duì)系統(tǒng)的破解。

Spectre 和 Meltdown 是側(cè)信道攻擊, 它推斷出內(nèi)存位置的內(nèi)容, 而內(nèi)存位置通常不應(yīng)使用定時(shí)來(lái)觀察當(dāng)前緩存中是否存在另一個(gè)可訪問(wèn)的位置。

綜上所述

現(xiàn)在讓我們來(lái)看看推測(cè)和緩存是如何結(jié)合在一起去允許一個(gè)像 Meltdown 的對(duì)處理器的攻擊??紤]下面的例子,這是一個(gè)用戶程序,從一個(gè)非法(內(nèi)核)地址讀取,導(dǎo)致一個(gè)錯(cuò)誤(崩潰):

t = a+b
u = t+c
v = u+d
if v:
   w = kern_mem[address]   # if we get here, fault
   x = w&0x100
   y = user_mem[x]

現(xiàn)在,如果我們能訓(xùn)練分支預(yù)測(cè)器相信 v 可能是非 0 的,我們雙路亂序超標(biāo)量處理器將會(huì)這樣打亂程序:

t, w_ = a+b, kern_mem[address]
u, x_ = t+c, w_&0x100
v, y_ = u+d, user_mem[x_]

if v:
   # fault
   w, x, y = w_, x_, y_      # we never get here

即使處理器總是從內(nèi)核地址地讀取, 它也必須延遲所產(chǎn)生的錯(cuò)誤, 直到它知道 v 是非零的。從表面上看,這感覺(jué)很安全,因?yàn)椋?/p>

  • v 為零,因此非法讀取的結(jié)果不會(huì)被提交到 w
  • v 為非零,但在將讀取提交到 w 之前發(fā)生故障

但是,假設(shè)我們?cè)趫?zhí)行代碼之前清空緩存,并排列 a、b、c 和 d, 以便 v 實(shí)際上是零。現(xiàn)在,在第三周期中推測(cè)讀取

v, y_ = u+d, user_mem[x_]

將訪問(wèn)用戶地址 0x000 或地址 0x100 ,具體取決于非法讀取的結(jié)果的第八位,將該地址及其附近加載到緩存中。由于 v 為零,因此將丟棄推測(cè)性指令的結(jié)果,并繼續(xù)執(zhí)行。如果我們對(duì)其中一個(gè)地址進(jìn)行后續(xù)訪問(wèn), 我們就可以確定哪個(gè)地址在緩存中。恭喜你,你剛剛從內(nèi)核的地址空間讀取了一位!

真正的 Meltdown 利用比這更為復(fù)雜(特別是為了避免錯(cuò)誤地訓(xùn)練分支預(yù)測(cè)器,作者更愿意無(wú)條件地執(zhí)行非法讀取并處理結(jié)果異常),但原理是一樣的。 Spectre 使用類似的方法來(lái)顛覆軟件數(shù)組邊界檢查。

結(jié)論

現(xiàn)代處理器不遺余力地保持抽象,即它們是直接訪問(wèn)存儲(chǔ)器的順序的標(biāo)量機(jī)器。而實(shí)際上使用許多技術(shù),包括緩存、指令重排和推測(cè),可以提供比簡(jiǎn)單處理器更高的性能。 Meltdown 和 Spectre 是我們?cè)诔橄蟮谋尘跋聦?duì)安全進(jìn)行推理的例子,然后在抽象和現(xiàn)實(shí)之間遇到細(xì)微的差異。

在 Raspberry Pi 中,ARM1176、Cortex-A7 和 Cortex-A53 核心的缺少推測(cè)功能使我們對(duì)這種類型的攻擊免疫。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容