誰來拯救存量 SGX1 平臺?又一個內核特性合并的血淚史
前言
自從Intel內核開發人員Jarkko Sakkinen于2017年9月2日在intel-sgx-kernel-dev@lists.01.org郵件列表上發出v1版的SGX in-tree驅動以來,時間已經過去了3年多了。這期間這個驅動前前后后共修改了41個版本,終于在2020年11月13日,v41版本的補丁合入了5.11-rc1內核。Jarkko松了一口氣,任務完成啦!不過,為什么合并一個普通的驅動模塊會這么難?
事實上,由于SGX所代表的新的機密計算領域的特殊性,圍繞著它的爭議和討論就從未停止過。甚至在最終的v41補丁中,也沒能看到大佬們整齊劃一的LGTM(社區黑話,Looks Good To Me的縮寫,表示自己認可這個補丁),它依舊存在一些問題,同時還有人在不斷提出修改建議。這不禁讓人聯想到另一個x86處理器特性FSGSBASE合入upstream時的命運多舛:后者的合入前后花了5年時間,甚至最后都不是Intel的人合入的(當然也不是AMD的人合入的,大家可以猜猜是誰)。
今天的故事主角,是一個被稱為Flexible Launch Control(簡稱FLC)的SGX平臺特性。對這個特性的爭議,最終導致SGX in-tree驅動無法支持不具備FLC特性或關閉了FLC特性的所有SGX系統。大白話是啥意思呢?意思就是一批較早的支持Intel SGX的機器用Linux內核自帶的官方驅動是無法加載和使用SGX的......納尼!這個驅動竟然不向下兼容!哎,現在讓我們先對FLC技術背景進行一個簡單的介紹,然后再對其被社區拋棄的故事簡單做個復盤,最后再給出我們的思考,以及我們的解決方案。
FLC的技術背景
友情提醒:想聽故事不想了解技術細節的同學請直接跳到下一節。
在執行SGX EINIT指令初始化enclave的時候,被初始化的enclave必須通過Launch Control機制的檢查,因此Launch Control是一種用于控制enclave能否啟動的安全檢查機制。
被初始化的enclave分為兩類:Launch Enclave和普通enclave。Launch Enclave與普通的enclave的不同之處在于其SECS.ATTRIBUTES.EINITTOKEN_KEY == 1,即具有通過執行SGX EGETKEY指令派生出einit token key的權限。Einit token本質上是一個帶有CMAC簽名的token,其中的CMAC是Launch Enclave用einit token key生成的,意在對einit token本身提供完整性保護;在執行EINIT初始化普通enclave的時候,從Launch Enclave處返回的einit token需要作為輸入參數的一部分,然后EINIT指令的微碼會將einit token作為派生出einit token key的輸入因子,最后用派生出的einit token key來驗證輸入的einit token中的CMAC,以確保einit token從生成到傳給EINIT的過程中沒有被篡改。
上面的流程中提到的einit token完整性檢查適用于所有的普通enclave,而對于Launch Enclave,再執行EINIT初始化時有特殊處理。下面是在執行EINIT時完整的Launch Control流程:
(上面的流程圖是對Intel SDM手冊中關于EINIT指令在微架構層的執行流程進行的總結;其中Launch Enclave驗證兩次IA32_SGXLEPUBKEYHASH的邏輯應該是冗余的,但我們還是按照原始流程的邏輯把它給完整地畫出來了)
可以簡單地將上述流程總結為以下兩點規則:
1.只要在初始化enclave時能提供合法的einit token, 就能通過Launch Control的檢查。
2. 如果在初始化enclave時沒有提供合法的einit token, 則enclave的MRSIGNER必須與IA32_SGXLEPUBKEYHASH的值一致。
此外,還可以從第二點規則推導出適用于Launch Enclave的特殊規則:
1. Launch Enclave必須能夠通過IA32_SGXLEPUBKEYHASH的校驗才能被初始化。
2. 通過EINIT初始化Launch Enclave的時候,可以不提供einit token。
在處理器復位時,IA32_SGXLEPUBKEYHASH的默認值是Intel的MRSIGNER。如果處理器不支持FLC,任何普通的enclave在初始化時,必須持有合法的einit token,而負責頒發einit token的Launch Enclave則可以制定自己的策略來決定是否給一個普通enclave頒發einit token。 具體到Intel在不支持FLC的SGX1上實施的策略則是:用戶運行的enclave必須是由Intel授權的密鑰簽過的,這樣才能得到Intel開發的Launch Enclave的授權,并獲得einit token,否則用戶無法運行自己開發的普通enclave(嚴格講是無法運行產品級的普通enclave,debug級的普通enclave還是可以運行的)。
這就是利用Launch Control達成的控制效果:誰掌握Launch Enclave,誰控制普通enclave的運行授權。
相反,如果處理器支持FLC且BIOS沒有鎖死IA32_SGXLEPUBKEYHASH,IA32_SGXLEPUBKEYHASH MSRs對系統軟件不僅可見而且可寫,配合SGX in-tree驅動就可以實現如下功能:每當加載任意enclave時,都無條件將其MRSIGNER寫入到IA32_SGXLEPUBKEYHASH中,從而繞過Launch Control機制,也就是說所有普通enclave都能像Launch Enclave那樣無需提供合法的einit token。FLC中的F,指的就是IA32_SGXLEPUBKEYHASH寄存器在某些情況下是可寫的。 至于要不要利用它繞過Launch Control機制,則是具體的實現策略問題,而SGX in-tree驅動就恰恰采取了繞過Launch Control的策略,原因是Linux kernel不希望提供對配置鎖定的系統提供支持。
至于這種策略是否正確,我們暫先不討論。先休息下,聽聽故事。
故事脈絡
故事的導火索出自v22 SGX in-tree驅動。這里作者Jarkko定義了處理器是否支持FLC的bit,并對FLC的作用進行了說明。留意作者的最后一段話:
“內核不會對某種被鎖定的配置提供支持,因為這奪走了內核(對啟動enclave)的啟動控制權。”結合前面關于FLC的技術原理講解,這里的鎖定指的就是如果BIOS鎖死了IA32_SGXLEPUBKEYHASH,那么普通enclave的運行方式就要被Intel的Launch Enclave的授權策略所控制。這段描述引起了Reviewer Borislav的警覺,并給出了硬核回復:如果FEATURE_CONTROL_SGX_LE_WR位決定了IA32_SGXLEPUBKEYHASH是否被鎖死,那么誰控制的FEATURE_CONTROL_SGX_LE_WR位?內核能夠控制嗎?我可不想讓該死的BIOS去控制,真出點啥問題我們自己能搞定,用不著看OEM的臉色。
這時候另一個作者Sean站出來回答了reviewer的問題:
“是的,就是BIOS控制著FEATURE_CONTROL_SGX_LE_WR位。這個我們都想好了,FEATURE_CONTROL_SGX_LE_WR位如果為0,那我們就把SGX功能完全禁止掉。這個想法在第四個補丁里有具體實現。”到目前看起來還是正常的社區review流程,大家也比較平和,直到reviewer看過了第四個補丁的內容。首先我們自己先看下第4個補丁到底干了什么:
- +static void __maybe_unused detect_sgx(struct cpuinfo_x86 *c)+{+ unsigned long long fc;++ rdmsrl(MSR_IA32_FEATURE_CONTROL, fc);...+ if (!(fc & FEATURE_CONTROL_SGX_ENABLE)) {+ pr_err_once("sgx: SGX is not enabled in IA32_FEATURE_CONTROL MSR\n");+ goto err_unsupported;+ }++ if (!cpu_has(c, X86_FEATURE_SGX1)) {+ pr_err_once("sgx: SGX1 instruction set is not supported\n");+ goto err_unsupported;+ }++ if (!(fc & FEATURE_CONTROL_SGX_LE_WR)) {+ pr_info_once("sgx: The launch control MSRs are not writable\n");+ goto err_msrs_rdonly;+ }++ return;++err_unsupported:+ setup_clear_cpu_cap(X86_FEATURE_SGX);+ setup_clear_cpu_cap(X86_FEATURE_SGX1);+ setup_clear_cpu_cap(X86_FEATURE_SGX2);++err_msrs_rdonly:+ setup_clear_cpu_cap(X86_FEATURE_SGX_LC);+}
一句話描述這段代碼的含義就是:如果FEATURE_CONTROL_SGX_LE_WR位為0(一種情況是BIOS鎖死了IA32_SGXLEPUBKEYHASH,另一種情況是平臺不支持FLC)的話,意味著MSR_IA32_SGXLEPUBKEYHASH{0, 1, 2, 3}這四個MSR寄存器對內核來說就是只讀或者不可見的,那么內核也就無法控制普通enclave的啟動,這是Linux upstream不想看到的情況。對此,代碼會簡單地清楚掉FLC的cpu feature flag,但會保留其他SGX相關的cpu feature flag。
Borislav此刻的心情肯定是:() 這和你前面說的不一樣啊?這哪里是你所謂的“把SGX功能完全禁止掉”了??你明明只是清除了FLC cpu feature flag,沒有完全禁止SGX功能啊!!!難道是我的理解有問題嗎 (⊙_⊙)?
作者Sean應該是知道自己說錯話了,沒有回復這個問題;而Borislav此刻只想要一個東西:MSR_IA32_SGXLEPUBKEYHASH{0, 1, 2, 3}這四個MSR寄存器對內核來說必須可寫!!!!
然后作者Sean開始擺事實講道理:還有好多不支持FLC或者禁用FLC(即BIOS可能非故意地鎖死了IA32_SGXLEPUBKEYHASH)的SGX的平臺啊!
Sean說這句話的時候是在2019年9月25日,其實如今來看這句話依舊正確。
Borislav沒有被說服,并且開始談起BIOS是多么的“糟糕”,并多次提到不想提供對帶有配置鎖定的系統的支持:
Linux以及開源社區一向反感各種配置鎖定和DRM。筆者印象中早些年Win7剛出來時強行默認開UEFI Secure Boot且BIOS setup中不提供關閉選項導致無法安裝Linux,這讓開源社區非常不爽,經過Linux Foundation、Intel和微軟的交涉,才最終有了今天的shim bootloader。當然,終端用戶不用自己去找微軟簽名bootloader,這是每個Linux發行版本自己找微軟簽名的事情了,而且這個默契也已經持續很久了。
最后作者Sean認了,并且說我們本來就是那么想的:
一口老血噴了出來!合著前面的迷之發言逗我們玩呢???顯然作者Sean的邏輯出現了矛盾,他說他的本意其實是這樣的:
“即使MSR_IA32_SGXLEPUBKEYHASH不可寫,但我們依舊保留其他SGX cpu feature flag,好讓用戶明白到底是我的系統不支持SGX硬件能力,還是因為缺少FLC功能導致無法運行普通的enclave,現在看起來可能是我想多了。” 關于Sean的這個結論,需要這樣理解:雖然在不支持FLC或禁用FLC的平臺上仍舊能夠運,debug級的普通enclave,但是debug級的enclave是能夠被sgx-gdb通過特殊的enclave debug指令訪問到enclave中的敏感數據的,因此debug級的enclave不適合于生產環境;要運行不能被debug的產品級enclave,就必須要遵守Intel的Launch Enclave的授權策略,但這需要實施一些對常人來說不具可實施性的操作,這與一般用戶想簡單地就能運行普通enclave的需求確實相悖,因此大多數情況下,不支持FLC或禁用FLC就等價于用戶無法運行普通enclave,而cpu feature flag可以反映出原因。
最終,從v25開始,如果處理器不支持FLC,所有SGX特性都將無法使用:
- +update_sgx:+ if (!cpu_has(c, X86_FEATURE_SGX) || !cpu_has(c, X86_FEATURE_SGX_LC)) {+ clear_sgx_caps();+ } else if (!(msr & FEAT_CTL_SGX_ENABLED) ||+ !(msr & FEAT_CTL_SGX_LC_ENABLED)) {+ if (IS_ENABLED(CONFIG_INTEL_SGX))+ pr_err_once("SGX disabled by BIOS\n");+ clear_sgx_caps();+ }
不過這個做法真的對么?難道不支持FLC或禁用FLC的平臺就不應該得到SGX in-tree驅動的支持嗎?
思考
回答這個問題前,我們先看下目前Intel對SGX驅動的支持情況。
除了SGX in-tree驅動,Intel在開源社區還提供了SGX Legacy驅動。這個SGX驅動是由于漫長的review流程而逐漸興盛起來的副產物,目的是在標準內核尚不支持SGX的情況下滿足用戶對SGX平臺的支撐需求。這個SGX Legacy驅動支持沒有FLC的平臺,也是目前支持SGX1平臺的唯一選擇。可以預見,在SGX in-tree驅動合入到upstream后,這個驅動會逐步退出歷史的舞臺,那么問題也來了。
首先,即使具備大量EPC內存的SGX2機器已經上線了(可以參考阿里云的SGX2邀測申請鏈接:https://pages.aliyun.com/aliyunpage/activity/alibabacloud-pilot.html ),但是仍有一批存量SGX1平臺正在服役;此外,采用Intel client platform的CPU很多都支持SGX但不支持FLC,難道這些平臺的用戶將來就只能用著缺少新特性支持的SGX Legacy驅動嗎?
其次,有些人一定已經提前考慮到需要將現有業務應用拿到基于SGX in-tree驅動的環境里跑一跑,測一測SGX in-tree驅動本身的性能和穩定性,因此在缺乏SGX2平臺的情況下,也需要SGX in-tree驅動能夠支持SGX1平臺。
最后,不支持FLC和禁用FLC其實是兩碼事。被社區真正詬病的是老的SGX1平臺不支持FLC,進而只能被逼實施不具可實施性的Launch Enclave運行授權;到了支持FLC平臺特性的時代,即便禁用了FLC,但只要鎖定的內容是用戶可控的,或者是實施一種更為寬松的Launch Enclave運行授權,那么這種配置鎖定也是有益的。 就拿云租戶使用裸金屬服務器的場景為例:即使CSP在BIOS里禁用了FLC,只要CSP在自己定制的Launch Enclave中確保合法的云租戶能正常運行該租戶自己簽的enclave就可以了,而這其實這反倒是為租戶多增加了一重保護。如果像現在SGX in-tree驅動的這種實現方式,包括成功入侵到系統的攻擊者都可以任意運行自己編寫的惡意Enclave了。
那我們該怎么辦呢?
解決方案
我們的觀點還是想大家之所想,急大家之所急。根據目前的實際情況,Inclavare Containers開源項目組( https://github.com/alibaba/inclavare-containers )編寫了幾個補丁,目的就是讓不支持FLC或禁用FLC的系統能夠運行SGX in-tree驅動。補丁的使用和驗證方法詳見:https://github.com/alibaba/inclavare-containers/tree/master/hack/no-sgx-flc
目前我們已經在SGX1機型中成功部署了這種運行方式,并實際應用于Inclavare Containers開源項目的CI/CD測試中。
此外,Inclavare Containers開源項目會研發一個支持用戶自定義策略的Launch Enclave;目的是提升用戶控制的平臺的安全性,即只允許運行用戶預期的普通enclave,杜絕攻擊者在用戶平臺上運行惡意enclave的情況出現。
最后有幾個關鍵概念的關系要再次澄清,重點提醒下大家:
1. 是否支持FLC和CPU是否支持SGX1/2沒有關系;SGX1/2是處理器SGX架構的指令集特性;FLC是平臺特性。
2. 之所以強調SGX1和FLC的關系,是因為FLC是在SGX1和SGX2之間出現的平臺特性,因此很多SGX1平臺都缺少FLC特性。
3. 即使是在支持FLC的平臺,只要在BIOS中禁用了FLC(不管是出于安全原因禁用,還是BIOS不支持FLC,或是被CSP鎖定),都會像不支持FLC的SGX1平臺那樣遇到相同的問題。也許到了我們能把這個問題像上面提到的那樣做到真正的精細化處理,即能夠明確區分出“平臺控制權民主化”和“非預期的平臺配置鎖定”并為前者提供實在的安全解決方案的時候,我們會再給社區發送一組patch,以允許SGX in-tree驅動支持那些用戶明確希望禁用FLC的平臺。




































