LWN:BPF與安全!
關(guān)注了就能看到更多這么棒的文章哦~
BPF and security
By Jake Edge
October 4, 2023
LSSEU
ChatGPT translation
https://lwn.net/Articles/946389/
eBPF內(nèi)核虛擬機即將迎來其加入Linux十周年,它已經(jīng)發(fā)展成為生態(tài)系統(tǒng)中多種用途的工具。eBPF的創(chuàng)始人,也是其早期開發(fā)的主要負(fù)責(zé)人Alexei Starovoitov,在2023年Linux安全峰會歐洲的開幕演講中談到了BPF與安全的關(guān)系。他在演講中分享了一些有趣的歷史,從一個不同的角度來看待這段歷史。他說,其中一些內(nèi)容展示了BPF如何在發(fā)展過程中既是安全問題又是安全解決方案。
Universal assembly
BPF有點像vi編輯器,Starovoitov開始說,人們要么喜歡它,要么討厭它,但兩者都只是一系列的命令而已。關(guān)于BPF有很多定義,但他想在演講中使用的是它是"通用匯編語言(universal assembly language)"。它是第一個嚴(yán)格類型化的匯編語言;在BPF中沒有"指向內(nèi)存的指針",所有指針都指向具體的類型。

由于它是通用的,因此它遠遠不能局限于用戶空間告訴內(nèi)核要做什么的這種使用場景;現(xiàn)在有硬件設(shè)備會將BPF發(fā)送到內(nèi)核,以描述如何使用它們。還有用戶空間到用戶空間的應(yīng)用程序,內(nèi)核根本不參與;一個應(yīng)用程序可能會告訴另一個應(yīng)用程序,在世界的另一邊,要做什么。
他經(jīng)常被問及BPF和WebAssembly之間的區(qū)別。BPF不像WebAssembly或瀏覽器中的JavaScript那樣是一個受沙箱保護的環(huán)境;沙箱環(huán)境不知道它們將要運行什么代碼,因此它們必須限制執(zhí)行環(huán)境。它們創(chuàng)建了一個邊界,但由于所有運行時檢查,這會降低性能。
相反,BPF是靜態(tài)驗證的,因此 "實際上沒有運行時檢查";唯一的檢查是用于驗證器無法靜態(tài)確定的事情。主要區(qū)別在于BPF程序在執(zhí)行之前其意圖是已知的,而沙箱則完全不同,它們必須運行任意代碼。他經(jīng)常在評論中看到(尤其是在LWN上)關(guān)于將WebAssembly添加到內(nèi)核的評論;他的回應(yīng)是讓那些開發(fā)者"引入它"。他認(rèn)為內(nèi)核足夠大,可以容納WebAssembly或其他沙箱環(huán)境以及BPF。
當(dāng)eBPF首次在2013年提出時,它被稱為 "內(nèi)部BPF"(iBPF);最終發(fā)展為 "擴展BPF"(eBPF)。此外,eBPF本身已經(jīng)多次擴展;在LLVM中,這些較新的指令集是使用值 v1 到 v4 的 ?mcpu 選項選擇的不同CPU模型。 v4 支持是在2023年7月添加的,這是GCC現(xiàn)在也支持的指令集。
Starovoitov說他幻燈片中的下一張(第9張)是他演講中最重要的一張。每個項目都應(yīng)該有一個使命宣言,他說。BPF的使命有兩個部分,首先是 "創(chuàng)新",其次是 "使其他人創(chuàng)新"。該項目繼續(xù)滿足他對 "創(chuàng)新的渴望",但它還使其他人能夠做新的事情。他在BPF上工作最喜歡的部分之一是幫助在郵件列表上發(fā)布他們正在嘗試做的新事情的人;這是 "作為內(nèi)核維護者的最佳時刻"。
他認(rèn)為該項目的這種態(tài)度在內(nèi)核中BPF開發(fā)社區(qū)的增長中表現(xiàn)出來。他展示了自2019年初以來每月獨立開發(fā)人員的圖表,顯示了該時段內(nèi)總體從大約50人增加到100人以上,而他所在的Meta BPF團隊在同一時段內(nèi)一直保持在10到15名開發(fā)人員左右。
Tracing and networking
他說:"BPF起源于tracing(跟蹤)這個場景的需求"。eBPF程序可以連接到的第一個掛鉤是kprobes和uprobes;接下來是tracepoints,然后是函數(shù)入口(fentry)和退出(fexit)。人們經(jīng)常認(rèn)為BPF可以在內(nèi)核內(nèi)部做任何事情,但實際上它受到相當(dāng)大的限制。對于tracepoints,BPF程序可以讀取內(nèi)核數(shù)據(jù),但無法進行任何修改。網(wǎng)絡(luò)BPF程序可以讀取和修改數(shù)據(jù)包數(shù)據(jù)并丟棄數(shù)據(jù)包,但它們不能修改內(nèi)核狀態(tài)。各種類型的BPF程序可以做的事情的限制是基于這些程序的用例。
他舉了一些BPF跟蹤的例子。Android使用BPF程序來跟蹤基于所聯(lián)系的主機的網(wǎng)絡(luò)使用情況,他說。因此,如果用戶想要在手機上查看他們的Facebook使用情況與YouTube使用情況等,他們通過BPF程序獲取了這些信息。PyPerf程序使用BPF來分析
Python程序。此外,BPF程序可以附加到用戶空間程序,使用uprobes,以便對程序的每次調(diào)用都附加了BPF程序。這可用于查看GCC在處理包含文件與編譯代碼的時間之間花費了多少時間;并行構(gòu)建中的每次GCC調(diào)用都將被正確地儀器化以收集這些數(shù)據(jù)。
還有多個網(wǎng)絡(luò)用例可以使用BPF。他指出,內(nèi)核的快速數(shù)據(jù)路徑(XDP)特性是一種應(yīng)對分布式拒絕服務(wù)(DDoS)攻擊的方式。曾經(jīng),F(xiàn)acebook遭受了一次500Gbps的DDoS攻擊,通過在網(wǎng)絡(luò)驅(qū)動程序?qū)蛹壣鲜褂肵DP放置了一個BPF程序來緩解這種攻擊。這種方式吸收攻擊提供了比早期DDoS緩解技術(shù)提供的10倍的改進,他說。
Security
將BPF程序附加到Linux安全模塊(LSM)掛鉤的能力(也稱為BPF-LSM)是內(nèi)核的一個最近添加的功能,用于 "防范所關(guān)注的安全攻擊",Starovoitov說。與其他類型的BPF程序一樣,可以附加到LSM掛鉤(或系統(tǒng)調(diào)用)的BPF程序具有與跟蹤、網(wǎng)絡(luò)或其他程序類型不同的特定功能。這些程序可以讀取任意內(nèi)核數(shù)據(jù)并拒絕操作,但它們也可以休眠,這對于BPF程序是一種新的特性。這意味著如果它正在訪問的用戶空間地址已被交換(swap)出去,程序可以引發(fā)輕微錯誤,因此不可能通過引用交換出的內(nèi)存來回避這些掛鉤。
不幸的是,BPF-LSM程序通常不是公開可用的,不像跟蹤和網(wǎng)絡(luò)中的程序那樣。他所知道的這些領(lǐng)域的程序中至少有90%是免費提供的;特別是許多大型互聯(lián)網(wǎng)公司正在共同努力開展DDoS防護等工作,分享他們的代碼,互相學(xué)習(xí)。在跟蹤中也是如此,但對于BPF-LSM代碼來說,"情況并不那么好"。
至于網(wǎng)絡(luò)、跟蹤甚至安全方面的BPF功能集目前幾乎已經(jīng)成熟;它們已經(jīng)完成了95%,盡管最后的5%需要更多時間。但仍然在BPF中添加了新功能,包括最近新增的人機接口設(shè)備(HID)的BPF功能。這允許BPF程序修改HID設(shè)備(如鍵盤和鼠標(biāo))在內(nèi)核中的呈現(xiàn)方式,以糾正問題(怪癖)或以某種方式更改行為。
Starovoitov說他對可擴展的調(diào)度器類感到興奮,它允許BPF程序執(zhí)行調(diào)度功能以測試新的調(diào)度算法。始終存在一些需要更專門的調(diào)度器的小眾用例,尤其是在云工作負(fù)載中,其中虛擬機中的調(diào)度程序最終會與超級調(diào)度程序(hypervisor scheduler)爭奪。到目前為止,至少直到目前為止,使用BPF進行可插拔調(diào)度已經(jīng)被拒絕,盡管在演講中沒有提到這一點。
Unprivileged BPF
30年前創(chuàng)建的最初的伯克利包過濾(BPF)指令集,作為 "經(jīng)典BPF"(cBPF)在Linux中繼續(xù)存在,由 tcpdump 和 seccomp() 在繼續(xù)使用。cBPF的用途是不需要特權(quán)的,因此eBPF也效仿了:32種BPF程序類型中的兩種可以在沒有特權(quán)的情況下使用,而且這兩種程序只允許讀取數(shù)據(jù)包數(shù)據(jù)和丟棄數(shù)據(jù)包。其中一種 BPF_PROG_TYPE_SOCKET_FILTER 完全沒有用過,他說,所有其他程序類型總是需要root權(quán)限。
他說,這在eBPF存在于內(nèi)核的頭幾年內(nèi)還可以,直到2017年。那一年,Project Zero的Jann Horn編寫了一些演示了猜測執(zhí)行問題的BPF代碼,最終成為了所謂的Spectre v1問題?,F(xiàn)代CPU都進行猜測執(zhí)行,但它們的錯誤預(yù)測的副作用仍然存在于緩存中,因此無法隱藏。正如在2017年底(以及之后幾年)所看到的,這些副作用可以被轉(zhuǎn)化為安全漏洞。
硬件供應(yīng)商對這些問題的解決方案是建議通過在代碼中添加"load fence"(lfence)指令來阻止任何可能的分支錯誤預(yù)測。Microsoft遵循了這一建議,改變了其編譯器以在代碼的各個地方發(fā)出這些指令;然后重新構(gòu)建了Windows和其他工具。
硬件供應(yīng)商要求Linux內(nèi)核也執(zhí)行相同的操作,但內(nèi)核開發(fā)人員有其他想法,他說。=lfence= 指令是一個大錘,具有重大性能影響,因此決定內(nèi)核通過將其引導(dǎo)到安全方向來管理猜測,而不是通過 lfence 關(guān)閉它。這需要大量的工作來說服英特爾和Arm這種技術(shù)是可行的,但它最終導(dǎo)致了對該問題更好的解決方案. 該補丁中的 array_index_nospec() 宏已經(jīng)在內(nèi)核中使用了240次,其中一些位于非常繁忙的路徑中,例如查找文件描述符表中的索引。使用 lfence 指令的影響將會很大。
BPF在Horn的攻擊中被用到,因此也需要對BPF進行一些更改。BPF不能直接使用這個宏,但它進行了等效的更改,以避免Spectre v1。然而,僅僅幾個月后,Horn回來了,用BPF解釋器實現(xiàn)了Spectre v2攻擊,這引起了關(guān)于BPF安全問題的進一步擔(dān)憂。實際上,攻擊并沒有加載BPF代碼到內(nèi)核中,而是使用了用戶空間中的BPF指令的解釋器進行猜測執(zhí)行。
解決方案是避免在內(nèi)核可執(zhí)行代碼中具有解釋器代碼。BPF代碼可以被解釋或使用即時(JIT)編譯器運行,因此添加了 BPF_JIT_ALWAYS_ON 選項來始終啟用BPF的編譯器并刪除解釋器。雖然BPF進行了更改以避免此問題(實際上是CPU硬件中的問題),但他認(rèn)為內(nèi)核中的任何解釋器都可以以這種方式使用;至少還有另外三種解釋器,所以內(nèi)核仍然沒有完全安全,Starovoitov說。
這是一個有趣的情況,BPF JIT編譯器的認(rèn)知如何隨著時間的推移發(fā)生了變化,他說。在2011年,曾發(fā)生過JIT spraying攻擊,這使一些內(nèi)核開發(fā)人員懷疑JIT編譯是否適合放到內(nèi)核中。當(dāng)時解決了這個問題,但現(xiàn)在必須啟用JIT編譯器以避免Spectre v2。BPF開發(fā)人員還發(fā)現(xiàn)JIT編譯器解決了一些性能損失問題(由retpolines 引入,這是另一種Spectre v2防護措施)。
2019年,BPF開發(fā)人員決定通過驗證器變得更聰明,以避免其他猜測執(zhí)行問題。Daniel Borkmann對驗證器的更改進行了修改,以檢測和避免這些問題。為此,驗證器模擬了正常和猜測執(zhí)行,這在行業(yè)中是 "獨特的,沒有其他靜態(tài)分析工具可以進行這種猜測分析"。
接下來出現(xiàn)了Spectre v4,通過驗證器中的少數(shù)代碼來凈化堆棧來進行了緩解。但其他Spectre變種仍然在不斷出現(xiàn),因此最終決定添加配置選項以完全禁用無權(quán)限的BPF??梢栽跊]有特權(quán)的情況下使用的這兩種程序類型是 "極其小眾的用例,用戶數(shù)量少于一只手的手指數(shù)量";繼續(xù)支持該功能對BPF社區(qū)的繁榮和增長無益。BPF_UNPRIV_DEFAULT_OFF選項默認(rèn)設(shè)置為 "on",以便發(fā)行版不允許無特權(quán)的BPF程序,盡管管理員可以修改這個選擇。
CAP_BPF
多年來,一直有要求將一些BPF權(quán)限從執(zhí)行幾乎所有BPF操作所需的根root特權(quán)(實際上是CAP_SYS_ADMIN)中分離出來的呼聲。CAP_PERFMON功能是由perf子系統(tǒng)添加的,但它也被BPF采納;它允許讀取內(nèi)核內(nèi)存。CAP_BPF功能添加了用于管理各種BPF操作的權(quán)限;它可以與CAP_PERFMON組合使用,以允許加載有用的跟蹤程序,或者與CAP_NET_ADMIN組合使用,以允許加載有用的網(wǎng)絡(luò)程序,而無需CAP_SYS_ADMIN。
然而,CAP_BPF存在一個理解問題;不清楚它實際上是用來管理什么。問題的一部分是BPF沒有受到命名空間的限制;如果您可以查看內(nèi)核內(nèi)存,那么您可以查看所有內(nèi)核內(nèi)存,而不僅僅是單個容器中的內(nèi)存。CAP_BPF的設(shè)計目的類似于CAP_SYS_MODULE,這是加載內(nèi)核模塊所需的權(quán)限;該功能實際上授予了讓內(nèi)核crash的權(quán)限,因為惡意(或有缺陷的)模塊可以這樣做。
但是,驗證器錯誤可能導(dǎo)致BPF程序崩潰內(nèi)核,盡管這應(yīng)該被視為可能性,但卻被視為一個安全漏洞,Starovoitov表示。因此,每個驗證器錯誤都會獲得一個CVE,這是一個真正的問題。他提到了9月中旬的LWN文章,該文章涉及到 "虛假CVE" 的問題,這也是BPF項目的問題。錯誤會被修復(fù),但會在早期的內(nèi)核版本中提出CVE,而尚未進行后期支持;有時這些CVE甚至引用BPF運行的自測代碼,以確保錯誤仍然得以修復(fù)。CVE的存在會導(dǎo)致緊急修復(fù)舊內(nèi)核。
一些安全初創(chuàng)公司正在以奇怪的方式使用BPF。他提到了一個未具名的初創(chuàng)公司,抱怨BPF可以做什么,以及存在BPF的系統(tǒng)的危險性;當(dāng)然,這都是為了推銷初創(chuàng)公司的產(chǎn)品。奇怪的地方在于,該產(chǎn)品使用BPF來防護免受其抨擊的所有BPF問題。"最終,他們說 'BPF很糟糕,使用BPF來保護免受BPF的威脅'。"
他沒有多少時間了,所以他開始迅速地過了他的演講中剩下的部分。他指出,很少有BPF-LSM程序是開源的,其中一個程序被systemd用于根據(jù)允許和拒絕列表防止掛載文件系統(tǒng)類型。他展示了systemd如何使用BPF來執(zhí)行各種安全策略的幾個示例。在某些情況下,它使用BPF-LSM掛鉤,但在其他情況下,它使用其他BPF程序類型(如網(wǎng)絡(luò)和跟蹤)來執(zhí)行其工作。
Starovoitov表示,他認(rèn)為所有內(nèi)核模塊實際上都應(yīng)該編寫為BPF程序。內(nèi)核模塊的優(yōu)勢在于它們可以編寫為任意的C代碼,具有對內(nèi)核符號的完全訪問權(quán)限,但這也意味著它們可能會因為錯誤而導(dǎo)致內(nèi)核崩潰。對于BPF程序,安全性是通過驗證器內(nèi)置的。此外,BPF程序比內(nèi)核模塊更具可移植性。這種可移植性是一種被低估的優(yōu)勢,特別是對于擁有大規(guī)模系統(tǒng)的公司來說,其中的一些長尾系統(tǒng)將具有各種不同的內(nèi)核版本。
他最后簡要提到,他認(rèn)為BPF所使用的C語言版本是內(nèi)核編程的更好版本。通過驗證器內(nèi)置了可以進行驗證的C版本的安全性,對于內(nèi)核編程來說是更好的選擇。他的幻燈片顯示了一些可以避免的錯誤構(gòu)造,但他無法詳細介紹這些細節(jié),盡管其中一些已經(jīng)在一年前的一個演講中提到。有人懷疑這個觀點在BPF社區(qū)之外尚未被廣泛接受。
全文完
LWN 文章遵循 CC BY-SA 4.0 許可協(xié)議。
長按下面二維碼關(guān)注,關(guān)注 LWN 深度文章以及開源社區(qū)的各種新近言論~
