可怕!CPU竟成了黑客的幫兇!

本故事根據CPU真實漏洞改編

前情回顧

還記得我嗎,我是阿Q,就是那個CPU一號車間的阿Q啊。如果你忘記了我,記得看看這裏回憶一下哦:完了!CPU一味求快出事兒了!

自從我們車間用上了亂序執行分支預測後,生產效率那是大大提升,領導不僅在全廠的員工大會表揚了我們,還把這兩項技術向全廠推廣,在我們8個CPU核心車間都鋪開了,性能甩開競爭對手CPU幾條街。

可是,就在我們還沉醉在取得的成績時,不知不覺我們竟埋下了災難的種子······

事情還得從不久前的一個晚上說起。

神祕代碼

這天晚上,我們一號車間遇到了這樣一段代碼:

uint8_t array1[160] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16};
uint8_t array2[256 * 512];
uint8_t temp = 0;

void bad_guy(int x) {
	if (x < 16) {
		temp &= array2[array1[x] * 512];
	}
}

不到一會兒功夫,我們就執行了這個bad_guy()函數很多次,這不,又來了。

負責取指令的小A向內存那傢伙打了一通電話,讓內存把參數x的內容傳輸過來,我們知道,以內存那蝸牛的速度,估計得讓我們好等。

這時,負責指令譯碼的小胖忍不住說了:“你們看,我們這都執行這個函數好多次了,每次的參數x都是小於16的,這一次估計也差不多,要不咱們啓動分支預測功能,先把小於16分支裏的指令先提前做一些?大家看怎麼樣”

我和負責數據回寫的老K互相看了一眼,都點頭表示同意。

於是,就在等待的間隙,我們又給內存那傢伙打了電話,讓他把array1[x]的內容也傳過來。

等了一會兒,數據總算傳了過來:

x: 2
array1[x]: 3

拿到結果之後,我們開始一邊執行x<16的比較指令,一邊繼續打電話給內存索要array2[3]的內容。

比較指令執行的結果不出所料,果然是true,接下來就要走入我們預測的分支,而我們提前已經將需要的數據準備到緩存中,省去了不少時間。

就這樣,我們成功的預測了後續的路線,我們真是一羣機智的小夥伴。

遭遇滑鐵盧

天有不測風雲,不久,事情發生了變化。

“呀!比較結果是false,這一次的x比16大了”,我執行完結果後發現和我們預期的有了出入。

小A聞訊而來,“額,咱們提前執行了不該執行的指令不會有問題吧?”

老K安慰道:“沒事兒,咱們只是提前把數據讀到了我們的緩存中,沒問題的,放心好啦”

我想了想也對,大不了我們提前做的準備工作白費了,沒有多想就繼續去執行>16的分支指令了。

隨後,同樣的事情也時有發生,漸漸的我們就習慣了。

災難降臨

夜越來越深,我們都有點犯困了,突然,領導來了一通電話,讓我們放下手裏的工作火速去他辦公室。

我們幾個不敢耽誤,趕緊出發。

來到領導的辦公室,裏面多了兩個陌生人,其中一個還被綁着,領導眉頭緊鎖,氣氛很是緊張。

“阿Q啊,你知不知道你們新發明的亂序執行分支預測技術闖了大禍了?”

我們幾個一聽傻眼了,“領導,這是從何說起啊?”

領導從椅子上站了起來,指着旁邊的陌生人說到:“給你們介紹一下,這是操作系統那邊過來的安全員,讓他告訴你們從何說起吧!”

這位安全員向大家點了點頭,指着被捆綁那人說道:“大家好,我們抓到這個線程在讀取系統內核空間的數據,經過我們的初審,他交代了是通過你們CPU的亂序執行分支預測功能實現的這一目的。”

我和小A幾個一聽都是滿臉問號,我們這兩個提升工作效率的技術怎麼就能泄漏系統內核數據呢?

真相大白

安全員顯然看出了我們的疑惑,指着被捆綁的那個線程說道:“你把之前交代的再說一遍”

“幾位大爺,你們之前是不是遇到了分支預測失敗的情況?”,那人擡頭看着我們。

“有啊,跟這有什麼關係?失敗了很正常嘛,既然是預測那就不能100%打包票能預測正確啊”,我回答道。

您說的沒錯,不過如果這個失敗是我故意策劃的呢?

聽他這麼一說,我的心一下懸了起來,“納尼,你乾的?”

“是的,就是我,我先故意給你連續多次小於16的參數,誤導你們,誤以爲後面的參數還是小於16的,然後突然來一個特意構造的大於16的參數,你們果然上鉤了,預測失敗,提前執行了一些本不該執行的指令。”

“那又如何呢?我們只是把後面需要的數據提前準備到了緩存中,並沒有進一步做什麼啊”,我還是不太明白。

這就夠了!

“你小子都被捆上了,就別吊胃口了,一次把話說清楚”,一旁急性子的老K忍不住了。

“好好好,我這就交代。你們把數據提前準備到了緩存中,我後面去訪問這部分數據的時候,發現比訪問其他內存快了很多”

“那可不,我們的緩存技術可不是吹牛的!哎等等,怎麼又扯到緩存上去了?”,老K繼續問道。

那人繼續說道:“如果我想知道某個地址單元內的值,我就以它作爲數組的偏移,去訪問一片內存區域。利用你們會提前預測執行而且會把數據緩存的機制。你們雖然預測失敗了,但對應的那一塊數據已經在緩存中了,接着,我依次去訪問那一片內存,看看誰的訪問時間明顯比其他部分短,那就知道哪一塊被緩存了,再接着反推就能知道作爲偏移的數值是多少了,按照這個思路我可以知道每一個地址單元的內容”

我們幾個一邊聽着一邊想着,琢磨了好一會兒總算弄清楚了這傢伙的套路,老K氣得火冒三丈,差點就想動手修理那人。

“好你個傢伙,倒是挺聰明的,可惜都不用在正途上!好好的加速優化機制竟然成爲了你們的幫兇”,我心中也有一團火氣。

亡羊補牢

事情的真相總算弄清楚了,我們幾個此刻已經汗流浹背。

經過和安全員的協商,操作系統那邊推出了全新的KPTI技術來解決這個問題,也就是內核頁表隔離

以前的時候,線程執行在用戶態和內核態時用的是同一本地址翻譯手冊,也就是人們說的頁表,通過這本手冊,我們CPU就能通過虛擬地址找到真實的內存頁面。

現在好了,讓線程運行在用戶態和內核態時使用不同的手冊,用戶態線程的手冊中,內核地址空間部分是一片空白,來一招釜底抽薪!

本以爲我們可以回去了,沒想到領導卻給我們出了難題,“這禍是你們闖下的,人家操作系統那邊雖然做了保護,你們是不是也該拿出點辦法來呢,要不然以後我們CPU還怎麼擡得起頭來?”

你有什麼好辦法嗎,幫幫我們吧!

幕後

本文描述的是兩年前爆發的大名鼎鼎的CPU的熔斷幽靈漏洞。

亂序執行分支預測是現代處理器普遍採用的優化機制。和傳統軟件漏洞不同,硬件級別的漏洞影響更大更深也更難以修復。

通過判斷內存的訪問速度來獲知是否有被緩存,這類技術有一個專門的術語叫側信道,即通過一些場外信息來分析得出重要結論,進而達成正常途徑無法達成的目的。

後面的文章中此類手法的故事還將繼續上演,敬請期待!

特別鳴謝:網友幾多風雨勁提供的技術支持

往期熱門回顧

完了!CPU一味求快出事兒了!

哈希表哪家強?幾大編程語言吵起來了!

內核地址空間大冒險4:線程切換

震撼!全網第一張源碼分析全景圖揭祕Nginx

一個整數+1引發的災難

一網打盡!每個程序猿都該瞭解的黑客技術大彙總

看過無數Java GC文章,這5個問題你也未必知道!

一個Java對象的回憶錄:垃圾回收

誰動了你的HTTPS流量?

路由器裏的廣告祕密

一個HTTP數據包的奇幻之旅

我是一個流氓軟件線程

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章