RFC-0080:偵測 VMO 不變性

RFC-0080:偵測 VMO 不變性
狀態已接受
區域
  • 核心
說明

這個 RFC 提議採用「ZX_INFO_VMO_IMMUTABLE」新標記,藉此判斷 VMO 是否不可變動,且自建立以來即已建立。

作者
審查人員
提交日期 (年/月)2021-02-18
審查日期 (年/月)2021-03-29

摘要

這個 RFC 建議了新的標記 ZX_INFO_VMO_IMMUTABLE,以便判斷 VMO 是否不可變更,且自建立後即存在。

提振精神

有三個主要動機,可以新增用於檢查不變性機制的機制。

改善共用記憶體的使用安全性

使用共用記憶體,編寫安全且正確的程式並不容易。有些程式不需要完全可變動的 VMO,因此可能會一次寫入 VMO,然後才從中讀取。

如果可以測試 VMO 不變性等限制,您可以更輕鬆地減少安全性途徑並確認正確性。

FIDL 中大型訊息支援

明確選擇將管道訊息限制在 64K,並使應用程式必須負責需要較大的應用程式才能找到解決方法。

日後,FIDL 可能會支援大型郵件。其中一個可能的解決方法,是將訊息封裝為現成 VMO。包含 Box<Type> 的 FIDL 定義會由 FIDL 自動將訊息區段複製到 VMO。

另一個處理這個問題的方式可能是,讓所有超過 64k 的 FIDL 訊息自動在 VMO 中傳送。這是在撤銷的 RFC 中採取的做法。

在這兩種情況下,訊息收件者都無法確認訊息在讀取時不會遭到變更。如果能夠確認 VMO 未遭修改,較容易驗證 VMO 是否安全使用,也可能導致最佳化機會。

超網

Overnet 是一個管道層級 Proxy,能夠透過網路傳輸訊息。

超網路圖

Overnet 是透明 Proxy。Overnet 用戶端傳送管道訊息的方式與傳送至本機程序的方式相同。接著,訊息會由 Overnet 序列化、透過網路傳送,然後在另一端重新建構。接收程序接收訊息的方式與其他管道訊息相同。

使用 Overnet 的應用程式會顯示下列項目:

從應用程式視角查看

Overnet 目前不支援傳送含有 VMO 的訊息。多虧了多個 Overnet 用戶端,他們必須傳送大於現有 64K 管道限制的訊息,才能傳送大量資料至 VMOs (包裝在 fuchsia.mem.Buffer 或 fuchsia.mem.Data FIDL 類型中)。

在單一機器中,無論 VMO 是可變動還是不可變動,系統都會透過參照傳遞 VMO;也就是說,在管道中傳送 VMO 並不會導致 Zircon 隱含複製該 VM。導入方式簡單又有效率。但是,透過網路在多部機器之間進行參照是效率不彰的做法。轉傳遞功能適用於這類情境。

困難之處在於,透過複製傳遞的方式並不安全。由於 Overnet 是透明 Proxy,因此遠端程序會預期參照同一個 VMO 才能保持同步。透過複製來傳遞會變更 VMO 的語意,且可能會稍微破壞應用程式。舉例來說,假設假設的媒體播放器程序將 VMO 傳送至遠端喇叭程序,然後將歌曲寫入 VMO,那麼揚聲器在首次傳送時,並不會在 VMO 中顯示歌曲,也不會播放歌曲。

幸運的是,在某些情況下,透過文案傳遞的方式很安全。其中一個情況是當 VMO 抵達 Overnet Proxy 時,無法變更。應用程式已經讓其能夠修改資料,因此可以保證用資料建立的任何副本均保持一致。

如果 Overnet 能夠偵測不可變的 VMO,則在遇到可變動的 VMO 時,它可能會快速失敗。如此一來,即可避免應用程式突然停止運作的細微錯誤。

設計

將新的 ZX_INFO_VMO_IMMUTABLE 旗標新增至 zx_info_vmo_tflags 欄位,指示 VMO 是否無法變更,且可透過保證及驗證的方式運作。

現在,如果使用 zx_vmo_create_child 系統呼叫建立 VMO,並設定了 ZX_VMO_CHILD_SNAPSHOTZX_VMO_CHILD_NO_WRITE 旗標,且未設定 ZX_VMO_RESIZABLEZX_VMO_DISCARDABLE 旗標,則 VMO 無法變更。然而,假如有新的建立不可變動 VMO 的方式,用於判斷 VMO 是否無法變更的公式。

應用程式決定何時將 VMO 設為不可變動。

實作

實作方式相對簡單。計算 ZX_INFO_VMO_IMMUTABLE 值所需的大多數狀態,已經儲存在核心中。

例外狀況表示 VMO 在建立時無法寫入。這可以儲存在 VmObjectDispatcher 中。

效能

新增標記不會對效能造成直接影響。

不過,建立不可變更的 VMO 目前會產生低廉的成本,因此只在必要時使用。如果是 Overnet 的情況,則不可變更本機副本的相對負載可能較不顯著,因為 Overnet 已經開始執行大量工作 (包括網路通訊) 傳送訊息。

系統已執行並比較以下兩種基準,以便進一步瞭解不可變更副本的費用 (CL):

  • 建立 VMO、寫入、讀取、關閉
  • 建立 VMO、寫入、不可變更的本機副本、讀取副本 VMO、關閉

請務必強調,此基準測試能反映目前的效能,但仍有空間最佳化本機副本,特別是對於不可變更的本機副本情況。

這項基準測試記錄在 Intel NUC NUC7i5DNHE 中。

快照複製的影響

直到 64 KiB 為止,執行不可變的本機副本需要 2 次費用,之後副本才會開始增加。2 MiB 之後,費用會大幅增加。

值得注意的是,當大小增加時,本機副本的相對成本 (相較於整體「建立、寫入、本機」讀取、刪除) 而降低:

快照複製的相對影響

對 64,000 個 VMO 來說,不可變副本會額外增加 20% 的時間;造成負擔極大,但由於大小較大,負擔也會逐漸降低至零。

安全性考量

請務必確認宣稱透過這項技巧聲明無法變更的 VMO。如果 VMO 遭誤認為不可變動,就會是安全性錯誤。

隱私權注意事項

沒有影響。

測試

系統會透過單元測試驗證標記實作情形。

說明文件

目前,ZX_INFO_VMO_* 旗標缺少說明文件,而讀取器參照存放區。本次變更中,將會撰寫說明文件來說明 ZX_INFO_VMO_* 選項。

缺點、替代方案和未知

判定不變性的不同屬性有多個旗標

這個 RFC 建議使用單一 ZX_INFO_VMO_IMMUTABLE 標記來判斷不變性。反之,您可以在使用者端使用多個不同的標記來判斷情況:

  • ZX_INFO_VMO_IS_CHILD_SNAPSHOT
  • ZX_INFO_VMO_IS_INITIALLY_WRITABLE
  • ZX_INFO_VMO_IS_RESIZABLE
  • ZX_INFO_VMO_IS_DISCARDABLE

根據基礎狀態使用多個標記的好處是,不變性不需要使用特殊的旗標,且只有基礎屬性可在不同組合中使用。

不過,透過這些旗標判斷不可變動性,則相對複雜,可視為本身的基本屬性。使用不可變動的專屬標記可以更輕鬆地避免安全性錯誤。此外,單一旗標也讓您更容易引進新的方法建立不可變更的 VMO。

正在檢查最後的參考資料

您可能會想使用 ZX_INFO_HANDLE_COUNT 檢查程序是否保留了 VMO 的最後一個處理常式,並假設如果遵循,其他程序就無法變更 VMO。但這樣假設並不正確,因為處理在 Zircon 中只有一種參照類型。舉例來說,VMO 的記憶體對應不會計入 ZX_INFO_HANDLE_COUNT 值,因此程序無法使用這個值判斷其是否擁有 VMO 的專屬存取權。

透過 FIDL 向 Overnet 告知安全性

我們可以對 FIDL 語言進行變更,以標示 VMO 不可變或「Overnet-able」。如果 Overnet 能夠判斷特定 VMO 是否有此屬性,就能決定要傳送 VMO 還是拒絕該 VMO。

有數種方式可將這類資訊傳達給 Overnet:

  • 在帳號代碼上未使用的位元中傳送
  • 新增機制,以將 Overnet 可讀取的位元儲存在 VMO 上
  • 在 FIDL 訊息標頭中保留 64 位元,將 VMO 控點標示為不可變動
  • 允許 Overnet 透過通訊協定與用戶端通訊

可惜的是,這些缺點會使複雜性降低,且較不適合建議的解決方案。

先前的圖片和參考資料

VMO 密封放棄的 RFC 提議採用機制來限制可在 VMO 上執行的動作。可以取代複製,以建立及測試不可變更的 VMO。不過,比起簡單的「不變性」檢查,VMO Sealing 更積極投入 VMO 實作。

最近我們改善了 zx_vmar_map 的安全功能,例如 ZX_VM_ALLOW_FAULTS。日後建議您針對 zx_vmar_map 新增處理不變性的功能。