血淚控訴:曝光數月的AMD微代碼bug毀掉了我的週末

AMD公司發佈的 Ryzen 3000處理器中,存在一項與隨機數生成器相關的嚴重微代碼bug。也正是因爲這個早已曝光數月卻沒有得到有效解決的“錯誤”,本篇文章的主人公 Jim Salter 度過了一個非常糟糕的週末。你能想象,一整天都在跟蹤錯誤的問題,尋找 bug 原因的場景嗎?

Ryzen 3000的RDRAND函數(本應成爲一款高質量的僞隨機數生成器)每次都會返回0xFFFFFFFF,修復過程也是相當煎熬。

上週末,Jim 懷着激動的心情坐在家中的工作間裏,在他打算部署自己的第一臺 Ryzen 3000工作站時,卻遺憾的發現,AMD的一項微代碼bug(最初公佈於今年7月,但直到現在仍廣泛存在於各類系統當中)打破了這美好的幻想。

雖然 Jim 經過努力,讓這套 Ryzen 3700X 系統恢復了正常,而且速度也相當驚人,但他不得不承認的是:AMD 的微代碼 bug仍然存在,而且現在缺少簡單易行的修復方法。

事實上,在 Ryzen 3700X 發佈後不久,AMD Ryzen 3000的使用者就已經發現了這款嶄新的CPU存在問題:Windows用戶無法順利啓動Dstiny 2(原因是存在電源管理bug,不過困擾 Jim 的倒不是這個問題),Linux用戶甚至發現自己經常無法正常啓動系統。

福布斯雜誌作者 Jason Evangelho 在今年7月的文章中報告了初步現象與總結意見,AMD公司的代表則通過電子郵件發出了回覆聲明:

AMD公司已經確認了引發問題的根本原因,並對BIOS程序進行了修復,旨在解決Ryzen 3000處理器無法運行某些Linux發行版以及Destiny 2的功能問題。我們已經將更新後的BIOS發放給各主板合作伙伴,希望消費者能夠在未來幾天之內使用到新的BIOS。

AMD的迴應乍聽起來似乎不錯,AMD自己的態度也很樂觀,但現實情況卻並非如此。因爲一旦CPU的微代碼中存在bug,就只能由主板供應商發佈新的系統BIOS才能實現修復。換句話說,用戶並不能簡單通過AMD提供的下載鏈接自行安裝修復。

而且AMD方面雖然在今年7月就做出了回覆,但截至到目前爲止,AMD方面也僅僅只是發送了通知郵件而已。它並沒有在其他渠道做出任何的相關提醒,不僅沒有新聞稿,而且這封郵件看起來誠意略顯不足,彷彿在說這只是一個能夠在一兩週之內輕鬆解決的小問題。

現在,已經過去三個月的今天,踩了坑的 Jim 表示:AMD的微代碼bug問題非常嚴重。

從 RDRAND 開始

存在 bug 的代碼會對 RDRAND 指令做出錯誤的響應,而這個 bug 之所以非常嚴重,則需要從 RDRAND 說起。

事實上,從英特爾的Broadwell以及AMD Zen架構開始,現代x86_64 CPU必須內置高質量的板載隨機數生成器(RNG),負責利用熱“噪聲”快速向具有內核級訪問權限的用戶提供高熵僞隨機數。而RDRAND,正是觸發隨機數生成的指令。

AMD的整個設計體系本應該非常安全,然而真實的情況卻存在一些偏差。

首先是一條CPUID函數調用,用於檢查RDRAND的可用性;此外,RDRAND調用的返回值中還包含一條“進位”,該位會在CPU RNG無法生成足夠的隨機數時通知作爲調用方的應用程序。遺憾的是,在安裝修復補丁之前,Ryzen 3000會對CPUID 01H調用迴應“yes”,並將進位設置爲“1”,表明其已經成功創建了有機高質量隨機數……同時每一次的“隨機”數都被賦值爲了0xFFFFFFFF。

只有在足夠廣泛的數據集當中,連續20個0xFFFFFFFF纔可能被視爲有效的“隨機”分組。但在大多數情況下,我們使用的數據集都沒那麼廣泛。

大家不要嘗試使用/dev/hwrng來檢查自己的設備是否受到AMD微代碼bug的影響,這是因爲/dev/hwrng可能會從其他來源獲取數據。在Jim的案例中,它倒確實是從RDRAND處獲取數據,這讓Jim很快就發現了問題。

RDRAND bug的主要影響

今年6月,Ryzen 3000中的RDRAND bug首次被發現時,Linux用戶開始大量發佈報告,表示自己基於Ryzen 3000的操作系統無法正常啓動。而引導失敗正是由systemd使用RDRAND引發的;不過遺憾的是,這已經不是systemd第一次與AMD CPU上存在問題的隨機數生成器發生衝突了。

早期一批CPU中存在的一項bug,會導致某些AMD系統在從掛起狀態恢復至正常工作狀態後停止生成正確的“隨機”數。而此次發生的新bug,則會導致Ryzen 3000用戶全程得不到任何合適的隨機數。這兩個問題,都導致了systemd在Linux操作系統中被長期鎖定。因此今年5月,systemd提交了一項修復程序,如果systemd從RNG得到的返回值爲0xFFFFFFFF,則該修復程序會轉而使用備用RNG源。(但這種修復方式本身也有問題,因爲從技術上講0xFFFFFFFF是個完全有效的隨機數——因此,只要經過足夠長的時間,systemd終究會在收到這個哪怕本應正確的隨機數時,將其誤判爲錯誤狀況,而後再直接切換至RNG。)

Systemd的補丁雖然做的很一般,但確實能夠讓操作系統順利啓動。只是它並不能真正從根本上解決問題,畢竟這種治標不治本的方式壓根沒有觸及到隨機數生成器那個層面。

Jim 在自己的系統上,用了一整個週末的時間,跟蹤一個個錯誤問題。這個過程讓 Jim非常抓狂。

Jim 首先懷疑問題來自系統上安裝的全新RX 590顯卡,然後就一次次更新操作系統發行版以及內核版本。但這些更新根本沒用。

嶄新的系統不斷報出一個個討厭的BUG:軟鎖定——22項錯誤徹底卡住了PU#n,並迅速令系統整體陷入癱瘓。/var/log/syslog中的調用信息也沒什麼指導意義,不過至少讓Jim找到了第一條線索。

最終,在經歷了一系列嘗試、氣急敗壞的咒罵、無數杯咖啡和幾口小酒之後,Jim 終於把CPU的頻繁鎖定與調用跟蹤記錄聯繫了起來。因爲 Jim 發現,在每一次跟蹤中,都會出現“WireGuard”的身影。

Jim 稱:事實證明,WireGuard依靠RDRAND(如果可用)生成新的會話ID。會話ID必須唯一,WireGuard還要求其不是簡單的連續整數,因此它會從RDRAND中獲取僞隨機值,將其與現有會話ID清單進行比較以確保不存在衝突,而後將該ID分配給新會話。

請注意這裏的最後一句“確保不存在衝突“,它的意義在於,如果現有會話同新會話擁有相同的ID,則WireGuard會向RDRAND請求另一個“隨機”數,並再次檢查其唯一性,依此類推。由於系統上的RDRAND(以及任何未經更新的Ryzen 3000系統)始終返回0xFFFFFFFF,因此這個過程就會無限循環。內核代碼發生無限循環當然不是什麼好事,緊隨其後的必然是系統崩潰與硬件重啓。

但,問題並不是出在WireGuard身上!WireGuard正確地檢查了RDRAND是否可用、是否給出一個值,以及該值是否正確設置了進位。它的責任,就是確保不僅獲得了一個值,而且確定該值確實是適當的隨機值。然而即使這樣,愛崗敬業的WireGuard還是讓用戶的系統陷入了癱瘓。

現代系統需要高質量的僞隨機數來完成衆多任務,其中“隨機”的含義當然不可能是“始終返回0xFFFFFFFF”。還有另一種直觀的候選方法,也就是地址空間佈局隨機化(ASLR)。Windows與Linux都只採用RDRAND作爲隨機機制中的一部分,旨在確保永遠不會以相同的順序加載同一段代碼,從而減輕相關軟件棧遭到破壞的風險。

修復問題,至少是發現問題

正如AMD公司代表在今年7月接受採訪時所言,真正的修復手段是對主板進行BIOS更新,同時確保BIOS當中包含針對CPU本體的微代碼補丁。然而當 Jim 這樣做時,卻發現並沒有那麼簡單。

Jim 使用dmidecode程序檢查了自己的BIOS,發現日期是2019年8月12日,所以當看到華碩主板下載頁面上更新到9月的BIOS時,Jim 非常驚喜。爲了儘快結束這糟糕的一天,Jim 選擇了立刻下載BIOS更新,並將其保存到U盤當中,重新啓動系統,然後進入設置程序,一氣呵成。

不過遺憾的是,在成功更新並再次重新啓動之後,Jim 突然意識到自己犯了個錯誤——沒錯,華碩雖然列出了BIOS的更新日期,但其提供的實際版本與 Jim 之前使用的一樣,都是3.2.0。所以Jim 的CPU仍然堅持認爲0xFFFFFFFF是隨機度最高、質量最好的生成數。

遭遇這一系列事情的 Jim ,火氣一下就上來了:systemd雖然悄悄解決了這個bug,但大多數應用程序只是直接忽略,我怎麼知道問題到底有沒有得到修復?如果兩年之後,事實證明ASLR根本無法提供真正的隨機數,那我因此遇到的破壞性攻擊又由誰來負責?

不過,好在幾番冥思苦想後,Jim 突然發現自己完全可以使用Linux上的hexdump工具對內核設備/dev/hwrng進行檢查,以證明確實存在這一問題。

但WireGuard項目的Jason Donenfeld卻警告稱,/dev/hwrng在某些系統上可能從其他來源處獲取隨機數,換句話說,當看到一大堆FF時肯定能夠證明它有問題,但當看到一大堆有效的僞隨機數時也不能說明它沒有問題。爲此,Jason Donenfeld 還慷慨地分享了多種測試程序,能夠幫助用戶安全地直接訪問RDRAND。

如果大家使用的是Linux,可以下載rdrand-test.zip,正常解壓,然後在文件夾中直接運行。通過./amd-rdrandbug命令,用戶可以查看自己的系統是否存在這一特定bug。./test-rdrand能夠輸出20條RDRAND取值,如果大家在測試中發現自己總是得到相同的值集,那麼無論其看起來是否隨機,都表明你已經成爲這項bug的受害者!

如果大家使用的是Windows,那還得額外做點工作。首先下載Ubuntu桌面安裝程序,然後創建一個Ubuntu啓動U盤。接下來,你可以啓動Ubuntu啓動盤的實時環境(點擊「Try Ubuntu」),然後下載並運行以下測試:

you@ubuntu-live:~$ wget https://cdn.arstechnica.net/wp-content/uploads/2019/10/rdrand-test.zip
you@ubuntu-live:~$ unzip rdrand-test.zip 
you@ubuntu-live:~$ cd rdrand-test
you@ubuntu-live:~$ ./amd-rdrand.bug

只有在足夠廣泛的數據集當中,連續20個0xFFFFFFFF纔可能被視爲有效的“隨機”分組。但在大多數情況下,用戶使用的數據集都沒那麼廣泛。

Jim 的總結

經歷了這樣一個糟糕的週末,Jim 最後總結了自己的收穫:

隨機數生成器bug是個相當嚴重的問題,但更加令人不安的是,在過去三個月的時間裏,AMD公司並沒有就此問題進行積極的努力與強調。雖然總體而言,Ryzen 3000確實是一款出色的CPU平臺,新系統也給我留下了深刻的印象……但是,整整一個週末令人頭痛的故障排查經歷,讓我對這套系統的整體安全性產生了嚴重的懷疑,我甚至不知道這個bug什麼時候才能得到修復。

日前,我與AMD公司的代表取得了聯繫。對方回答了我關於硬件的問題,但沒有給出具體解決方案。等有了最新消息,我會及時向大家彙報更新消息。

事件後續

事後,在 Jim 聯繫AMD就此事進行詢問時,AMD的一位代表詢問了Jim 的主板型號(Asrock Rack X470D4U),而後又聯繫了Asrock。Asrock團隊提供了已經完成微代碼修復的定製BIOS版本,但 Jim 認爲這種只適用於個人的BIOS版本沒有任何意義。

與此同時,有讀者在 Jim 的文章評論區給出了一些別的解決方法及緩解措施,儘管好像並沒有什麼用。

一種是在引導時將nordrand以參數形式傳遞給GRUB。這種方法並不能解決問題,因爲該方法實際上就是告知內核不要使用RDRAND指令,這往往無法影響該指令自身的實際可用性。指令仍然存在於系統當中,而一切通過CPUID檢查RDRAND可用性的代碼也仍然會受到影響。類似的random.trust_cpu Linux引導選項也是如此。這些緩解性措施都沒能真正禁用RDRAND,因此不能算是問題的理想解決方案。

另一種是安裝amd64-microcode軟件包。Jim 嘗試後表示,結果還是不行。因爲在默認情況下,amd64-microcode與intel-microcode軟件包被直接安裝在Ubuntu 19.10,以及所使用的系統當中,但這並不能解決RDRAND的故障。爲此,Jim 再次聯繫了AMD,要求對方代表檢查該軟件包的狀態並確定是否有必要對其做出更新。

以下是Jim的驗證:

目前最新Ubuntu 10.10系統上的amd-64微代碼補丁,其仍然無法解決RDRAND的問題。

在設置nordrand或者random-trust_cpu=0之後,rdrand在/proc/cpuinfo下仍顯示爲可用,因此這兩種引導選項均無法真正解決這個問題。

One more thing

截止到本文發佈,Jim的文章並沒有進一步更新,我們也無從得知他是否已經等到了AMD所承諾的那個“具有真正意義的通用BIOS版本”。

其實,對於某些“極客” 來說,處理器的微代碼早已不是什麼神祕的東西,它可以被看成是一種處理器固件,從主板的BIOS中進行加載。而主板製造商則可以通過在新的BIOS版本中集成新的處理器微代碼,支持新的處理器或是對一些所謂的bug進行修復。

但是真實情況我們也看到了,BIOS的更新並不容易,bug的修復難度也不低。不信,你看看隔壁的英特爾,它也同樣正在忙着修補自家的漏洞呢。

原文鏈接:
https://arstechnica.com/gadgets/2019/10/how-a-months-old-amd-microcode-bug-destroyed-my-weekend/#lg=1&slide=1

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