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