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。如果 FIDL 定義包含 Box<Type>,FIDL 會自動將訊息的該部分複製到 VMO。

另一種處理方式可能是讓所有超過 64k 的 FIDL 訊息自動以 VMO 傳送。這是已撤銷的 RFC 所採用的方法。

在這兩種情況下,郵件收件者都無法驗證郵件在閱讀時不會遭到變造。如果可以確認 VMO 不會遭到修改,就能更輕鬆地驗證 VMO 是否安全無虞,甚至可能發現最佳化機會。

Overnet

Overnet 是管道層級的 Proxy,可透過網路傳輸訊息。

Overnet 圖表

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

使用 Overnet 的應用程式會看到以下內容:

從應用程式的角度查看

Overnet 目前不支援傳送含有 VMO 的訊息。對於需要傳送大於現有 64k 管道限制訊息的 Overnet 用戶而言,這一直是痛點,否則他們會以 VMO 傳送大量資料 (包裝在 fuchsia.mem.Buffer 或 fuchsia.mem.Data FIDL 型別中)。

在單一機器中,無論 VMO 是否可變動,都會以參照方式傳遞,也就是說,在管道中傳送 VMO 不會導致 Zircon 隱含複製該 VMO。這種做法簡單有效率。 不過,透過網路在多部機器之間傳遞參照會缺乏效率,也不切實際。在這些情況下,適合使用傳遞副本。

但問題是,透過複製傳遞並不一定安全。由於 Overnet 是透明 Proxy,遠端程序會預期對相同 VMO 的參照會保持同步。以副本傳遞會變更 VMO 的語意,並可能導致應用程式發生細微的錯誤。舉例來說,假設媒體播放器程序將 VMO 傳送至遠端音箱程序,然後將歌曲寫入 VMO,音箱永遠不會收到歌曲,也無法播放,因為歌曲在 VMO 首次傳送時並不存在。

幸好,在某些情況下,以副本傳遞是安全的。其中一個例子是抵達 Overnet 代理伺服器的 VMO 不可變更。應用程式已放棄變更資料的能力,因此可確保任何資料副本都會保持一致。

如果 Overnet 能夠偵測到不可變動的 VMO,遇到可變動的 VMO 時,就能快速宣告失敗。這樣做可以避免細微錯誤,以免應用程式無聲無息地中斷。

設計

zx_info_vmo_tflags 欄位中新增 ZX_INFO_VMO_IMMUTABLE 標記,指出 VMO 是否以可保證和驗證的方式保持不變。

目前,如果 VMO 是使用 zx_vmo_create_child 系統呼叫建立,且設定了 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 後,費用會大幅增加。

不過請注意,相較於整體建立-寫入-複製-讀取-銷毀流程,複製的相對成本會隨著大小增加而降低:

快照副本的相對效果

如果是 64k 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 傳輸」。如果 Overnet 能夠判斷特定 VMO 是否具備這項屬性,就能決定是否要傳送或拒絕 VMO。

您可以透過下列方式將這項資訊傳達給 Overnet:

  • 將其插入把手上未使用的鑽頭
  • 新增機制,將位元儲存在 Overnet 可讀取的 VMO 上
  • 在 FIDL 訊息標頭中保留 64 位元,將 VMO 控制代碼標示為不可變動
  • 讓 Overnet 透過通訊協定與用戶端通訊

可惜的是,這些方法都有缺點,因此較為複雜,也不如建議的解決方案。

既有技術和參考資料

VMO 密封 已廢止的 RFC 提議採用一種機制,限制可對 VMO 執行的動作。這項功能可用於建立及測試不可變動的 VMO,而不必進行複製。不過,與簡單的不變性檢查相比,VMO 密封對 VMO 實作的侵入性高出許多。

我們最近為 zx_vmar_map 推出安全功能,例如 ZX_VM_ALLOW_FAULTS。日後可能需要將處理不可變動性的功能新增至 zx_vmar_map