X86 CPU特性之(2)-eagerfpu

1. 從一個漏洞說起

被稱爲LazyFP的安全漏洞與浮點單元 (FPU) 相關, 也稱爲數學協處理器。FPU 在進程間切換時由操作系統使用-它保存當前進程的狀態並還原新進程的狀態。

FPU有兩種類型的切換, 惰性 fpu 和熱切的 fpu 切換。惰性 FPU 轉換爲性能提供了一些好處, 但在現代系統中, 增益已變得微不足道, 這導致了越來越多的使用渴望切換。

研究人員最近發現, 如果使用惰性方法, 攻擊者可能會訪問 FPU 狀態數據, 其中可以包含敏感信息 (如加密密鑰)。LazyFP vulnerability found in Intel processors

"系統軟件可以選擇使用惰性 FP 狀態恢復, 而不是在上下文切換時對狀態進行熱切的保存和恢復。當一個進程通過推測其值的投機執行方通道推斷其他進程的寄存器值時, 惰性恢復狀態可能容易受到攻擊。

作爲 CVE-2018-3665 跟蹤的漏洞類似於崩潰, 具體來說是變型 3a, 但該問題僅被指定爲 “中等” 嚴重性等級。

2. 研究進展

朱利安 Stecklina 從亞馬遜德國, 托馬斯 Prescher 從 Cyberus 技術和 Zdenek Sojka 從 SYSGO AG 被稱讚了爲發現弱點。科林. 波斯也被譽爲, 但研究員說, 他只是寫了一個漏洞的利用。

Cyberus 發佈了一個LazyFP 漏洞的博客帖子, 但它在英特爾的請求中隱瞞了一些細節。

每個關於 LazyFP 的諮詢、博客文章和討論都提供了一些關於哪些系統可能受到影響的線索。

英特爾表示, 此漏洞會影響其核心處理器, 它們將作爲服務器的至強銷售。該公司聲稱該問題已由操作系統和管理程序軟件開發人員處理多年, 而且仍受到影響的供應商應該在未來幾周內發佈更新。

使用 AMD 或 ARM 處理器的系統似乎不會受到影響。“根據我們迄今的分析, 我們不相信我們的產品會受到最近在惰性 FPU 切換中發現的安全漏洞的影響,” AMD 告訴SecurityWeek.

微軟還沒有確切地說出哪些版本的 Windows 是易受攻擊的, 但該公司指出, 默認情況下, 在操作系統的所有版本中都啓用了 “惰性還原”, 並且無法禁用。該技術巨頭向客戶保證, 在 Azure 運行的 vm 沒有風險。

AWS告訴客戶, 其基礎結構不受影響, 但建議他們確保其操作系統始終是最新的。xen 項目說運行任何版本的 xen 的系統都是易受攻擊的。

3. 開啓eagerfpu 特性

在 Linux 的例子中, 內核的最新版本使用了熱切的 FPU(x86/fpu: Default eagerfpu=on on all CPUs)。在使用較舊處理器的系統上, 通過使用 “eagerfpu = on” 參數引導內核以啓用急切的 FPU, 可以減輕此漏洞。紅帽, DragonflyBSD和OpenBSD發表了建議。

那麼FPU eager和lazy mode到底有什麼區別呢?

  • lazy:對於早期的CPU,FPU context的save/restore是一件非常耗時的動作 – 要把多個128/256bit的寄存器寫到內存或者從內存取回。如果每次進程(在內核態所有的線程也都是進程形式存在)切換都去做這一工作,則會很耗時。但是,另外一個事實是,很多進程根本就沒有進行過浮點運算,也就沒有使用到FPU。對這種進程也去執行FPU save/restore顯然是一種浪費。所以,一個變通的方法是,直到進程執行了第一個浮點指令時,纔去執行FPU的restore操作,這就是所謂的lazy,跟Linux kernel裏其它的lazy概念是一致的。同樣,只有一個進程曾經執行過浮點指令,才需要在進程切換save它的FPU context。lazy FPU依賴於kernel及CPU的exception處理機制,即Device Not Available trap,參見其handler,do_device_not_available()。
  • eager: 現代CPU爲FPU context切換進行了優化,所以save/restore的開銷不再是一個問題。因此,新kernel的做法就是在每次進程切換時都執行FPU context switch。但是,新kernel還是做了一些優化:線程被換出時,只有它使用過浮點計算,它的FPU register纔會save下來;對於被換入的線程,只有它上一次使用過浮點寄存器,其FPU context纔會被restore。eager與lazy 模式最大的區別就是eager沒有使用exception機制,減少了一次context switch。另外,eager模式中,在判斷線程是否使用過FPU寄存器時使用了很技巧性的方法(依賴於fpstate_active和fpregs_active變量):所有的用戶線程都會設fpstate_active;在restore時,會調用fpu_want_lazy_restore來決定是否preload新線程的FPU。正是這些eager模式的變化引起了測試結果的變化。

不管是eager還是lazy模式,我們都會發現,FPU context的save/restore都是發生在進程切換時。如果進程在kernel態時,比如在一個系統調用中執行了浮點運算就會使用浮點寄存器,進程從kernel態返回用戶態時可能根本沒有意識到自己的FPU context已經發生改變,這就破壞了進程的FPU context。所以,無論如何,如果kernel函數裏使用了浮點運算,都應該進行保護,保護的方法就是調用kernel_fpu_begin/kernel_fpu_end。恰恰是這一點的疏忽,導致了eager模式在新kernel裏不能正確地工作。通過仔細地分析,最終發現是kernel裏的一個driver使用了-msse/-msse2編譯選項,GCC在此情況下會盡可能地使用浮點寄存器進行數學運算,查看彙編代碼可以發現,即使非浮點運算也會用到浮點寄存器如xmm, ymm等。去掉這個編譯選項,把引起問題的浮點運算轉換成定點運算,所有這一切問題都消失了,即使eager模式下也能很好地工作了。

參考:
https://www.cesafe.com/html/5527.html
https://blog.csdn.net/weixin_42094175/article/details/80520124
https://stackoverflow.com/questions/15883947/why-am-i-able-to-perform-floating-point-operations-inside-a-linux-kernel-module

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