RFC-0064:Box <Knox>

RFC-0064:Box <Knox>
狀態已遭拒
區域
  • FIDL
說明

導入機制,透過與 FIDL 訊息相關聯的輔助 VMO 傳輸大型結構化資料物件。

作者
提交日期 (年-月-日)2018-09-27
審查日期 (年-月-日)2020-06-17

拒絕原因

透過 FIDL 在對等互連裝置之間傳輸大型訊息的問題非常重要, 這是開發人員目前面臨的重大問題,也是 FIDL 目前的缺點。 管道限制、定義訊息的容易程度 (視執行階段行為而定,訊息可能會超出這些限制),以及訊息大小的固有難度 (例如達到分頁上限),使得這個問題在應用程式程式碼中難以解決 (至少在規模上是如此)。必須由 FIDL 提供解決方案

簡而言之,FIDL 必須提供方法,讓程式庫作者消除因通訊協定限制 (例如 Zircon 通道中的位元組和控制代碼硬性限制) 而導致執行階段發生意外的可能性,而 FIDL 開發人員必須能夠依賴繫結來實作可能交換大型訊息的通訊協定。

這項 RFC 包含許多實用成分,未來可做為參考。請特別注意以下幾點:

  • 問題陳述和動機。
  • 偏好使用靜態驗證,確保郵件符合通訊協定限制,或選擇使用繫結提供的執行階段機制「調整」郵件 (可能產生額外費用)
  • 依據值類型與資源類型之間的差異,在便利性/效能取捨連續體中提供不同點。

儘管如此,這項 RFC 仍因下列原因遭到拒絕:

  • 訊息的位元組和控制代碼如何從一個對等互連裝置傳輸到另一個裝置,並非線路格式問題,而且不需要修改線路格式即可滿足需求。
  • 導入 box<T> 符號有助於識別訊息中可發生裝箱的位置,也就是說,這是一種細微的機制,可指出訊息中 (幾乎) 任意位置的裝箱行為。目前的想法是,最好在方法層級提供註解,也就是在用途網站 (而非宣告網站) 提供較粗略的說明機制。
  • 隨著 FIDL 逐步普及,支援 Zircon 管道以外的各種傳輸方式,我們必須考量到每種傳輸方式都有各自的限制。舉例來說,當我們將 FIDL 擴展至 Zircon FIFO 時,大小限制會有所不同,且不允許使用控制代碼。這進一步支持了方法層級註解 (而非以型別為中心的註解) 較為合適的觀點。

目前,應用程式中解決這個問題的方法是使用 fuchsia.mem/Data 型別,這個聯集代表內嵌或 VMO 中的資料。使用語法糖將這個方法一般化至任何要求和/或回應 (如錯誤),並支援繫結,是目前最有可能在這個主題上取得進展的方法。

摘要

導入機制,透過與 FIDL 訊息相關聯的輔助 VMO 傳輸大型結構化資料物件。

提振精神

透過 Zircon 管道在程序之間傳輸的 FIDL 訊息大小上限,如 ZX_CHANNEL_MAX_MSG_BYTES 所定義。撰寫本文時,上限為 64 KB,包括 FIDL 標頭。

雖然這個限制對許多應用程式來說都足夠,但有時還是需要傳輸較大的物件。這對 FIDL API 設計人員來說是一項挑戰,因為他們必須設計傳輸這些物件的替代方式,例如:

  • 分頁:傳輸物件集合 (通常是向量) 時,請分批遞增傳送物件,而非一次傳送所有物件。
    • 只要每個物件都小於限制 (將標頭和其他欄位納入考量),就能正常運作。
    • 開發人員難以判斷如何將物件有效率地封裝到訊息中,且不超過限制,因為目前沒有任何 API 可估算 FIDL 訊息的大小,或逐步建構訊息。
  • VMO 封裝:不要在 FIDL 訊息本身傳輸大型物件,而是將這些物件複製到 VMO 中。這通常會以 fuchsia.mem/Buffer 表示。
    • 適合用於位元組向量 (Blob)。
    • 結構化資料物件很麻煩,因為開發人員必須負責叫用序列化/還原序列化。
    • 需要嚴格控管,才能降低共用記憶體造成的安全威脅。

如果無法預期這個問題,是導致執行階段不穩定的主要原因。

我們認為 FIDL 應提供內建安全可靜態驗證有效率的機制,用於傳輸大型資料物件。

設計

簡介 Box

「方塊」是可能需要頻外傳輸的大型資料物件的容器,因為訊息總大小 (包括標頭) 超過 Zircon 通道1 的限制。

方塊只能保存資料物件,無法保存具有控點的物件 2

設計階段,FIDL 通訊協定作者可以:

  • 預期含有這些物件的訊息可能會超出管道限制時,將資料物件封裝到方塊中

編譯階段,FIDL 編譯器會執行下列動作:

  • 剖析聲明
  • 靜態驗證每個方塊只包含資料物件 (不含控制代碼)
  • 靜態驗證每個 FIDL 訊息的最大可能大小是否超過管道限制 (在靜態訊息大小強制執行模式中)
    • 假設向量和字串的大小與其界限允許的大小相同
    • 假設可擴充的聯集和結構體 (資料表) 盡可能變大
    • 拒絕遞迴結構,除非遞迴是由方塊中斷

編譯階段,每個 FIDL 程式碼產生器...

  • 產生足夠的程式碼,可序列化及還原方塊資料

執行階段,FIDL 編碼器會...

  • 將盡可能多的裝箱物件放入訊息內文,並遵循所有其他行外物件
  • 將所有未裝入 VMO 的剩餘裝箱物件打包

執行階段,FIDL 解碼器會...

  • 存取含有封裝物件的 VMO (如有) 前,請確保 VMO 只有一個控制代碼 (VMO 未共用)
  • 從郵件內文和 VMO 擷取加上方框的物件

語言詳細資料

我們推出新的內建 FIDL 型別,以 box<T> 表示。T 指定要放入方塊的物件類型。

  • T 必須是不直接或間接包含控制代碼的參照型別。
  • T」不得為原始型別。
  • T 可以是選用型別。

在接受 structunionvector字串等參照型別的位置,都可以使用新類型 box<T>

方塊類型範例

  • box<string>
    • 含有無界字串的方塊
  • box<int>
    • 錯誤:方塊不得包含原始型別的物件
  • box<vector<T>:100>
    • 包含 T 物件的繫結向量的方塊
  • box<vector<T>>
    • 包含 T 物件無界向量的方塊
  • vector<box<T>>:100
    • 裝箱 T 物件的受限向量。
  • box<string:100>
    • 包含有界字串的方塊
  • box<string>
    • 含有無界字串的方塊
  • box<MyStruct>
    • 裝有結構的盒子
  • box<MyStruct?>
    • 包含選用結構的方塊
  • box<MyStruct>?
    • 錯誤:方塊不得為選填

聲明範例

interface Database {
    // OK
    1: SelectTop(string:1000 query) -> (box<Record> record);

    // ERROR: reply may exceed message size limit
    // consider wrapping large objects in a box<>,
    // "Record" size is unbounded
    2: BadSelectTop(string:1000 query) -> (Record record);

    // OK
    3: SelectAll(string:1000 query) -> (box<vector<Record>> records);

    // ERROR: reply may exceed message size limit
    // consider wrapping large objects in a box<>,
    // "vector<Record>" size is unbounded
    4: BadSelectAll(string:1000 query) -> (vector<Record> records);
};

struct Record {
    string name;
    string address;
};

Wire Format

(Section is incomplete.)

構想 1:在深度優先的序列化遍歷期間,將遇到的所有方塊加入佇列,完成第一遍後,依序封裝方塊項目,並遵循行外物件,直到沒有剩餘空間為止,然後計算剩餘方塊物件的大小、配置單一 VMO,並從該處繼續封裝方塊內容

構想 2:與構想 1 類似,但將每個方塊放入各自的 VMO 中,實作上稍微簡單,但可能會有更多限制

想法 3:或許我們應該完全放棄方塊的概念,改為在方法層級執行某些動作,例如註解,如 [Huge]

繫結

(Section is incomplete.)

強化 VMO 系統呼叫

(Section is incomplete.)

構想 1:定義「確保未共用」旗標、驗證 VMO 只有一個控制代碼、未與其他 VMO 共用任何頁面,且未對應,可將此旗標傳遞至 zx_vmo_read/write/map 等。

構想 2:定義新的系統呼叫,檢查 VMO 是否未共用

構想 3:如果 VMO 已取消共用,請確實反向 COW 快照 (應為無作業)

構想 4:放棄 VMO,改用 Views

導入策略

(Section is incomplete.)

人體工學

(Section is incomplete.)

說明文件和範例

(Section is incomplete.)

回溯相容性

除了提供移轉大型資料物件的機制,方塊也旨在解決靜態安全疑慮

目前,如果程式嘗試傳輸超過管道限制的 FIDL 訊息,就會在執行階段失敗,導致系統不穩定。一旦提供方塊,就應該可以在 FIDL 編譯器中導入靜態訊息大小強制執行,這樣我們就能在編譯時保證任何訊息都不會超過管道限制;多餘的內容可以由 FIDL 通訊協定作者移至方塊中。

不過,一次啟用靜態訊息大小強制執行功能,可能會導致現有程式碼中斷,並阻礙遷移作業。

我們建議採取下列做法解決這個問題:

  • 一開始,請在寬鬆模式中啟用靜態郵件大小檢查。
    • FIDL 編譯器應檢查訊息大小,並在可能超出管道限制時發出警告。建議 FIDL 通訊協定作者改用方塊。
  • 繼續進行遷移作業。
  • 完成後,請在強制執行模式中啟用靜態訊息大小檢查。
    • FIDL 編譯器應檢查訊息大小,並在可能超過管道限制時發出錯誤。停止編譯。

效能

(Section is incomplete.)

安全性

以 FIDL 語言繫結支援的正式解決方案,取代現有的臨時機制,有助於提升整體安全防護能力。

舉例來說,FIDL 語言繫結可確保含有裝箱資料的 VMO 只有一個擁有者,然後才嘗試存取其內容。這項功能可解決常見的共用記憶體威脅,例如:

  • VMO 提供者會在用戶端存取資料時修改資料。
  • VMO 的供應商在用戶端存取 VMO 時變更 VMO 的大小,或以其他方式導致用戶端發生頁面錯誤。

反之,導入這項功能可能會導致大型訊息的使用量增加,因此提高其他威脅的發生機率,例如:

  • VMO 的供應商傳送大量回覆給用戶端,導致用戶端在還原序列化時分配大量堆積。

測試

(Section is incomplete.)

缺點、替代方案和未知事項

(Section is incomplete.)

先前技術和參考資料

(Section is incomplete.)

編輯附註:遭拒的 RFC-0062:方法不可能,建議禁止所有可能超出通訊協定限制的方法。由於靜態分析的限制,這項做法過於受限,無法擷取執行階段行為來正確分頁顯示郵件,或手動「框選」郵件。



  1. 假設 FIDL 可透過其他管道傳輸,則裝箱作業的性質可能有所不同。如何實作這項功能不在本提案的討論範圍內。 

  2. 編輯附註:自 2018 年撰寫這項 RFC 後,值型別和資源的區別已正式成為 FIDL 語言的一部分,請參閱 RFC-0057。