RFC-0080:偵測 VMO 不變性

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

這份 RFC 提出了新的旗標 `ZX_INFO_VMO_IMMUTABLE`,可用於判斷 VMO 是否不可變動,以及自建立以來是否一直處於不可變動狀態。

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

摘要

這份 RFC 建議使用新的旗標 ZX_INFO_VMO_IMMUTABLE,以便判斷 VMO 是否不可變動,以及自建立以來是否一直處於不可變動狀態。

提振精神

新增檢查不變性機制的三大動機如下:

提升使用共用記憶體的安全性

使用共用記憶體編寫安全且正確的程式並不容易。某些程式不需要完全可變動的 VMOs,因為這些程式可能只會寫入 VMO 一次,然後只從 VMO 讀取資料。

如果可以測試 VMO 的不可變動性等限制,就更容易減少安全性途徑並驗證正確性。

FIDL 中的大型訊息支援

我們明確選擇將管道訊息限制在 64k 以內,並讓需要更大訊息大小的應用程式自行尋找解決方法。

日後,FIDL 可能會支援大型訊息。處理這項問題的一種可能方式,是將訊息的裝箱作業設為離線 VMOs。如果 FIDL 定義包含 Box<Type>,則 FIDL 會自動將該部分訊息複製到 VMO。

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

無論是哪種情況,收件者都無法確認訊息在讀取時不會發生變異。如果有方法可以確認 VMO 不會遭到修改,就能更輕鬆地驗證 VMO 是否安全使用,並找出最佳化機會。

Overnet

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

Overnet 圖表

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

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

從應用程式觀點查看

Overnet 目前不支援傳送含有 VMOs 的訊息。這對多個 Overnet 客戶來說是個痛點,因為他們需要傳送的訊息大於現有的 64k 管道限制,而且會在 VMOs 中傳送大量資料 (以 fuchsia.mem.Buffer 或 fuchsia.mem.Data FIDL 類型包裝)。

在單一機器中,無論 VMOs 是否可變動,都會透過參照傳遞,也就是說,在管道中傳送 VMO 不會導致 Zircon 隱含複製 VMO。這麼做既簡單又有效率。不過,透過網路在多部機器之間以參照傳遞資料,效率低且不切實際。相反地,傳遞複本則適合用於這些情況。

挑戰在於,複製傳遞不一定安全。由於 Overnet 是透明 Proxy,因此遠端程序會預期相同 VMO 的參照項目會同步。以複製方式傳遞會變更 VMOs 的語意,並可能會輕微破壞應用程式。舉例來說,假設媒體播放器程序將 VMO 傳送至遠端音箱程序,然後將歌曲寫入 VMO,音箱就不會收到或播放歌曲,因為歌曲在首次傳送時並未出現在 VMO 中。

幸運的是,在某些情況下,傳遞複本是安全的。其中一個情況是,到達 Overnet proxy 的 VMO 不可變更。應用程式已放棄變異資料的能力,因此可確保資料的任何副本都會保持一致。

如果 Overnet 能夠偵測不可變動的 VMOs,那麼當遇到可變動的 VMO 時,Overnet 就能快速失敗。這樣一來,就能避免導致應用程式無聲中中斷的細微錯誤。

設計

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% 的時間,這是相當可觀的額外負擔,但隨著大小增加,額外負擔會逐漸減少至零。

安全性考量

請務必驗證聲稱透過這項技術可變更的 VMOs 是否確實可變更。如果 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

根據基礎狀態設定多個標記的好處是,不需要專用標記就能確保不可變性,而且只會公開可用於不同組合的基礎屬性。

不過,透過這些標記判斷不可變性相對複雜,而且不可變性本身可視為基本屬性。透過專屬的不變性標記,更容易避免安全性錯誤。此外,單一標記可讓您更輕鬆地引入建立不變 VMOs 的新方法。

檢查是否有最後剩餘的參照

您可能會想使用 ZX_INFO_HANDLE_COUNT 檢查程序是否保留 VMO 的最後一個句柄,並假設如果是的話,就沒有其他程序可以變更 VMO。不過,這項假設並不正確,因為在 Zircon 中,句柄只是一種參照。舉例來說,VMO 的記憶體對應不會影響 ZX_INFO_HANDLE_COUNT 值,因此程序無法使用這個值來判斷是否擁有 VMO 的專屬存取權。

透過 FIDL 向 Overnet 傳送安全性資訊

您可以變更 FIDL 語言,標示 VMO 是否不可變動或「可 Overnet」。如果 Overnet 能夠判斷特定 VMO 是否具有此屬性,就能決定是否傳送 VMO 或拒絕。

您可以透過多種方式將這項資訊傳送給 Overnet:

  • 在句柄的未使用位元中傳送
  • 新增機制,在 VMO 上儲存 Overnet 可讀取的位元
  • 在 FIDL 訊息標頭中保留 64 位元,將 VMO 句柄標示為不可變動
  • 讓 Overnet 透過通訊協定與用戶端通訊

不幸的是,這些方法都有一些缺點,因此會變得複雜,且不如建議的解決方案。

既有技術與參考資料

VMO Sealing Abandoned RFC 提出了一種機制,可限制可在 VMOs 上執行的動作。您可以使用這個方法,而非複製,來建立及測試不可變動的 VMO。不過,與簡單檢查不變性相比,VMO 封存作業對 VMO 實作作業的影響更大。

我們最近已著手處理 zx_vmar_map 的安全功能,例如 ZX_VM_ALLOW_FAULTS。建議日後新增可處理 zx_vmar_map 不變性的功能。