Rust 社群目前正在討論最近釋出的 mem-isolate
庫,該庫承諾透過一種在分叉程序中隔離潛在危險操作的技術來安全地執行不安全程式碼。雖然這種方法很巧妙,但安全和系統程式設計專家對其侷限性和潛在誤用提出了重大擔憂。
mem-isolate
透過在透過 POSIX fork()
系統呼叫建立的隔離子程序中執行函式來工作。這種方法建立了父程序記憶體的副本,允許不安全操作執行而不影響原始程序的記憶體空間。當函式完成時,結果透過管道序列化回父程序,子程序終止。
安全侷限性
社群中的安全專家指出,mem-isolate
實際上並不提供其名稱所暗示的安全級別。該庫不符合 Rust 對安全程式碼的定義,後者要求同時保證空間和時間記憶體安全。
「我認為這不符合安全 Rust 中的安全定義:安全不僅僅意味著不會因空間記憶體錯誤而崩潰,還意味著程式碼在事實上是空間和時間上記憶體安全的。」
從安全形度來看,所提供的隔離是表面的。分叉程序保留了程式狀態的完整副本,包括記憶體中的任何機密資訊。這意味著可被利用的不安全程式碼仍然可以在隔離環境中訪問敏感資訊。此外,子程序保持與父程序相同的許可權,因此程式碼執行漏洞仍然可被利用。
mem-isolate 的主要侷限性:
- 僅適用於 POSIX 系統(Linux、macOS、BSD)
- 每次函式呼叫會引入約 1.9ms 的額外開銷(相比直接呼叫的 1.5ns)
- 需要在程序間序列化返回的資料
- 在多執行緒應用中可能存在危險
- 無法防止不導致崩潰的漏洞利用
- 子程序與父程序擁有相同的許可權和記憶體訪問能力
- 如果在持有互斥鎖時進行 fork 操作,可能導致死鎖
關於 Fork 的技術問題
系統程式設計專家強調,對於這種用例,fork()
是一個有問題的 API,尤其是在多執行緒應用程式中。當程序分叉時,整個記憶體狀態被複制,包括互斥鎖和鎖的狀態。如果在分叉時有任何執行緒持有鎖,子程序可能會遇到死鎖。
即使是列印或記憶體分配等簡單操作也可能導致分叉程序凍結。在回撥完成之前未重新整理的使用者空間緩衝區將丟失。這些問題使 mem-isolate
在複雜應用中潛在危險。
效能影響
該庫引入了顯著的效能開銷,基準測試顯示 execute_in_isolated_process()
大約需要 1.9 毫秒,而直接函式呼叫僅需 1.5 納秒 - 慢了一百多萬倍。雖然作者以半開玩笑的方式承認這一限制,稱程式碼執行慢了 1 毫秒,但社群成員指出,這種開銷使該庫在許多用例中不切實際。
許多開發者使用不安全程式碼專門用於效能最佳化,因此將此類程式碼包裝在引入大量開銷的機制中會破壞原始目的。正如一位評論者所指出的,大量使用這種方法可能會抵消 Rust 相對於像 Python 或 Ruby 這樣嚴重依賴分叉來實現並行性的語言的效能優勢。
效能基準測試:
- 直接函式呼叫:約 1.5 納秒
- fork() + wait:約 1.7 毫秒
- execute_in_isolated_process():約 1.9 毫秒
實際應用
儘管有侷限性,一些開發者仍然認為 mem-isolate
在特定場景中有價值。該庫在處理無法改進的固有不安全第三方程式碼時可能很有用,特別是在與 C 庫介面時。在這些情況下,所提供的隔離可能是為增加安全性而做出的可接受權衡,尤其是在非效能關鍵路徑上。
然而,對於大多數應用程式,專家推薦更強大的隔離方法。設計良好的多程序架構,具有受限的 IPC 通道和作業系統級沙箱 API,提供更強的安全保障。Chrome 等瀏覽器引擎使用這種方法,而不是簡單的程序隔離。
圍繞 mem-isolate
的討論突顯了系統程式設計中平衡安全性、效能和實用性的持續挑戰。雖然 Rust 生態系統歡迎記憶體安全的創新方法,但必須根據既定最佳實踐和安全模型仔細評估它們。