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 設計人員來說是一大挑戰,因為他們必須設計其他傳輸這些物件的替代方法,例如:

  • 分頁:傳送物件集合 (通常是向量) 時,請以批次方式逐漸傳送物件,而非一次傳送所有物件。
    • 只要每個個別物件都小於限制 (考量標頭和其他欄位),就會運作良好。
    • 由於目前沒有可用於估算 FIDL 訊息大小或逐步建構訊息的 API,因此開發人員難以判斷如何將物件有效地打包至訊息中,而不超過限制。
  • VMO 封裝:不要在 FIDL 訊息中傳送大型物件,而是將這些物件複製到 VMO。通常會以 fuchsia.mem/Buffer 表示。
    • 適合用於位元組向量 (blob)。
    • 開發人員負責叫用序列化/反序列化,因此不利於結構化資料物件。
    • 需要嚴格遵守規則,才能降低共用記憶體所造成的安全威脅。

未能預測這項問題,是導致執行階段不穩定的主要原因。

我們認為,FIDL 應提供內建安全可透過靜態方式驗證,以及高效的機制,用於傳輸大型資料物件。

設計

簡報中的方塊

box 是用來容納可能較大的資料物件,當郵件總大小 (包括標頭) 超過 Zircon channel1 的限制時,可能需要以非頻道傳輸。

box 只會保留資料物件,無法保留具有句柄的物件2

設計階段,由 FIDL 通訊協定作者。

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

編譯時間,FIDL 編譯器會...

  • 剖析宣告
  • 靜態驗證每個方塊只包含資料物件 (沒有句柄)
  • 靜態驗證每則 FIDL 訊息的可能最大大小不超過管道限制 (在靜態訊息大小強制執行模式下)
    • 假設向量和字串的大小可達到其邊界允許的大小
    • 假設可擴充的聯集和結構體 (資料表) 會變得越大越好
    • 除非迴圈被方塊中斷,否則會拒絕遞迴結構

編譯時間,每個 FIDL 程式碼產生器都會...

  • 產生的程式碼足以序列化和反序列化封裝的資料

執行階段,FIDL 編碼器。

  • 將所有離線物件之後,將可放入訊息主體的盒裝物件打包
  • 將所有不符合 VMO 的盒裝物件打包

執行階段,FIDL 解碼器。

  • 在存取包含已包裝物件的 VMO (如果有) 之前,確保該 VMO 擁有唯一的 VMO 句柄 (VMO 不會共用)
  • 從郵件內文和 VMO 中擷取框選物件

語言詳細資料

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

  • T 必須是不會直接或間接包含句柄的參照類型。
  • T 不得為基本型別。
  • T 可能是選用類型。

新類型 box<T> 可用於任何可接受參照類型 (例如 結構體聯集向量字串) 的情況。

範例 Box 類型

  • 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;
};

線路格式

(該部分未完成。)

想法 1:在序列化深度優先的檢查期間,將遇到的所有盒子加入佇列,一旦完成第一個階段,就依序將盒裝項目包裝成盒裝物件,直到沒有剩餘空間為止,然後計算剩餘盒裝物件的大小、分配單一 VMO,並從該處繼續包裝盒子內容

構想 2:與構想 1 相同,但將每個方塊放入各自的 VMO,實作起來稍微簡單,但可能較受限制

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

繫結

(該部分未完成。)

強化 VMO 系統呼叫

(該部分未完成。)

想法 1:定義「確保未共用」標記,驗證 VMO 是否只有一個句柄、是否與其他 VMOs 共用任何頁面,以及是否已對應,並可將此標記傳遞至 ZX_vmo_read/write/map 等。

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

想法 3:如果 VMO 已取消共用,請讓系統確實檢查是否要使用反向 COW 快照 (應為無操作)

建議 4:使用 VMOs 並改用 View

導入策略

(該部分未完成。)

人體工學

(該部分未完成。)

說明文件和範例

(該部分未完成。)

回溯相容性

除了提供傳輸大型資料物件的機制,Box 也旨在解決靜態安全性問題

目前,嘗試傳送超過管道限制的 FIDL 訊息的程式會在執行階段失敗,導致系統不穩定。一旦推出盒子,我們應該可以在 FIDL 編譯器中導入靜態訊息大小強制執行功能,這樣就能保證在編譯時,不會有任何訊息超過管道限制;FIDL 通訊協定作者可以將超出限制的內容移至盒子中。

不過,如果一次啟用所有靜態訊息大小強制執行機制,可能會導致現有程式碼發生錯誤,並妨礙遷移作業。

我們建議以以下方式解決這個問題:

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

成效

(該部分未完成。)

安全性

將現有的臨時機制,換成由 Fidl 語言繫結所支援的官方解決方案,我們就能改善整體安全規範。

舉例來說,FIDL 語言繫結可確保包含封裝資料的 VMO 在嘗試存取內容前,只會有一位擁有者。這可解決常見的共用記憶體威脅,例如:

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

反之,導入這項功能可能會導致使用大型訊息的情況增加,進而提高遭受其他威脅的可能性,例如:

  • VMO 供應器會向用戶端傳送大量回應,導致用戶端在序列化時分配大量堆積。

測試

(該部分未完成。)

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

(該部分未完成。)

既有技術與參考資料

(該部分未完成。)

編輯者附註:已拒絕 RFC-0062:Method Impossible,該提案建議禁止所有可能超出通訊協定限制的方法。由於靜態分析的限制,這項做法過於受限,無法擷取執行階段行為,以便正確分頁訊息,或手動「封裝」訊息。



  1. 假設 FIDL 可透過其他管道傳送,而這些管道可能會採用不同的封裝方式。這項提案並未涵蓋實作方式。 

  2. 編輯者附註:由於此 RFC 是在 2018 年撰寫,因此值類型和資源之間的差異正式成為 FIDL 語言的一部分,請參閱 RFC-0057。