iT邦幫忙

2018 iT 邦幫忙鐵人賽
DAY 17
3
Security

網路安全概述系列 第 17

番外篇: 談 CPU Bug、FDIV/F00F bug

  • 分享至 

  • xImage
  •  

原本這篇要講 SQL Injection,不過這問題應該很老了,而且剛好這幾天關於 CPU bug 的討論還蠻熱烈的。
按照慣例,還是必須先講一下相關的資訊。

前言

一般在寫程式的時候,都會假設硬體上沒有 bug。如果是寫比較低階的(例如 kernel、驅動等等,或是要做重度運算的程式),可能就會去參考一下 CPU Errata(指 CPU 勘誤表),看看哪些動作會有問題,或是參考 CPU 本身的文件,了解一下各平台的特性。

不過,大多數人,都會假設「硬體是安全的」。即硬體不會有 bug,大部分 bug 都是存在於軟體之中。之前的文章,也都是注重在軟體的部分。

上面提到的假設,並不是真的。畢竟不太可能有系統是安全/沒有 bug 的,只是 bug 或多或少,或是安全度高或低而已。例如,作者最近在研究的 USB TEMPEST,即是從實體的角度去思考,透過軟體定義無線電,配合外接的 USB 裝置,以電磁波的方式去傳輸資料。

總而言之,任何系統都會不安全的地方。

談 CPU Bug

而且,很少懷疑 CPU 會出 bug。大部分狀況下都不會覺得 CPU 有問題,說 CPU 都一定會正常運行,如果給了錯誤的指令,就一定會返回錯誤。但畢竟 CPU 是個複雜的系統,其實也難免會出 bug。

以下舉兩個例子。

早期的 CPU bug

Pentium FDIV Bug

在很早期,約莫 1994 的時候,Intel Pentium 剛出不久。有個美國大學的數學系教授,原本寫了一些程式,能夠計算質數/多胞胎質數。直到 1994 左右,學校幫他買了一台 Pentium 的系統,然後他用那台算,發現計算結果會變得不正確。這個教授很 nice(教授姓 Nicely),先怪自己,他先查自己的程式有沒有 bug,然後查看看是不是主機板等。最後他回報這個問題給 Intel,Intel 才承認 Nicely 發現這問題的半年前,Intel 就知道這個 bug 了。

這個 bug,主要是 CPU 在計算浮點數時,其返回結果會是不正確的。原因是因為當時為了加快效能,所以 CPU 中的浮點運算單位在處理浮點數除法時,會用查表的方式來作輔助。不過這張表是用一支程式產生的,這支程式又有 bug,導致某些數值沒寫進表,結果這張有缺陷的表還是被放進 CPU 中,導致用除法算某些數字就會出問題。

剛好那幾年 Intel 打廣告打很兇,這問題打了自己臉,但 Intel 從這個機會中獲得了不少經驗,且使 CPU 的測試流程變得嚴謹許多。

Pentium F00F Bug

不過,五年之後,又出現了一個重大的 bug。在對 CPU 下特定的指令時,會讓執行到這個指令的系統進入死當的狀況,只有重開機一途。

由於當初這 bug 是出在 x86 上面,所以以 x86 (32bit) 的角度來解釋。

有問題的指令是 lock cmpxchg8b eax (F0 0F C7 C8) 。
cmpxchg8b 指令是「比較並互換兩個 64bit 的東西」,將記憶體中 <暫存器EDX的數值>:<暫存器EAX的數值> 的內容拿去和目標(這個例子裡面,目標是 eax)比較(註:去記憶體要拿資料的話,要知道地址)。附上 psuedo code:

if(EDX:EAX == Destination) {
	ZF = 1;
	Destination = ECX:EBX;
}
else {
	ZF = 0;
	EDX:EAX = Destination;
}

lock cmpxchg8b eax 這指令其實同時有兩個錯誤,分別是:

  1. cmpxchg8b 的目標只能是指向某個記憶體位置的東西,不能對暫存器用,所以這指令會是不合法的
  2. #LOCK 也只能對同時做了「讀-修改-寫」的指令用,但是這指令很明顯沒有同時做這些事

理想的狀況下,CPU 應該會直接去執行 #UD 的指令,去做錯誤處理。不過同時把兩個錯誤疊在一起,就會發生可怕的事情。

在那時候,這執行過程會變成這樣:

  1. 解析 cmpxchg8b eax,發現這指令不合法,要執行 #UD
  2. 這時候,CPU 出 bug 了,明明不該發 #LOCK,但卻還是發了 #LOCK
  3. CPU 去試著查 #UD 的位置
  4. 因為 (2) 發了 #LOCK,CPU 必須要等待「寫」這個動作,才會釋放 lock,結果因為根本沒有人下寫的指令,把自己給先鎖死了

簡單來說,只要能執行這程式碼:

char x[5] = { 0xf0, 0x0f, 0xc7, 0xc8 };

int main (void) {
  void (*f)() = x;
  f();
  return 0;
}

就會讓當時有這個 bug 的處理器直接卡死,只能重開機。而且執行這些指令是不用權限的,跑一跑就可以幫人家把機子敲下線了。
當年 Intel 給了幾個解決方案,其中一個解決方案是在觸發這 bug 時發出 Page Fault (記憶體控制器在你試著取不存在的資料時,會發出的錯誤),讓作業系統去做處理。

下一篇會講最近火紅的 KPTI (Meltdown, Spectre) 問題。


上一篇
談 Heartbeat 協定與 CVE-2014-0160 (Heartbleed)
下一篇
番外篇: CPU 預設執行 bug, Spectre, CVE-2017-5753
系列文
網路安全概述31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

2 則留言

0
SunAllen
iT邦研究生 1 級 ‧ 2018-01-05 17:25:22

有神快拜!

0
LeeBoy
iT邦新手 4 級 ‧ 2018-01-05 19:18:28

今天講古

我要留言

立即登入留言