RFC-0120:單獨使用 FIDL 傳輸格式

RFC-0120:單獨使用 FIDL 線段
狀態已接受
領域
  • FIDL
說明

此 RFC 會將在沒有傳輸的情況下使用 FIDL 線段的需求條件 (即編碼和解碼) 進行正式化。也會指定繫結應如何公開這項功能的評分量表。

問題
毛皮變化
作者
審查人員
提交日期 (年-月-日)2021-07-02
審查日期 (年-月-日)2021-08-04

摘要

此 RFC 將使用 FIDL 的需求 (即編碼及解碼) 進行正式化 沒有傳輸方式。也會指定繫結方式的評分量表 都應該公開這項功能我們介紹線路格式的概念 中繼資料,說明傳輸格式的修訂版本和功能,以及 需要在編碼和解碼 API 中使用,因此:

  • 使用 FIDL 線段時正式支援繫結,無需進行傳輸。
  • 使用者必須傳輸傳輸格式的中繼資料與經過編碼的訊息。
  • 繫結可能支援持續慣例,訊息前置字元為 中繼資料

提振精神

Fuchsia 的核心原則是可更新。我們在 ABI 上投入大量心力 在處理序間通訊 (IPC) 情境中使用 FIDL 時相容 (例如兩個對等互連項目 Zircon 管道上的 FIDL 通訊協定。FIDL 線的獨立用途 另一方面,由於格式相對稀少 請留意相容性例如,系統有時會誤以為 光是傳送 FIDL 訊息的編碼位元組,就會導致 可進化的 ABI。

驅動程式中繼資料 RFCRFC-0109:Fast datagram sockets 呼叫,可透過位元組導向的介面傳送 FIDL。 請把握這個好時機 將 FIDL 線格式獨立用於 提供革命性和互通性保證

設計

繫結「必須」支援在沒有 這種 API 的傳輸需求詳見下文請注意,許多繫結 您已取得某種形式的公開編碼/解碼 API (例如,位於fidl::Encode 高階 C++ 繫結)。請據此進行調整 RFC。因此 RFC 的這部分可視為核心的正式化 釐清 FIDL 層的層次

FIDL 線格式

FIDL 電匯格式的重點是二進位檔相容性:一組保證 改良結構定義演變,支援讀取以不同 該架構的版本例如,具有 struct{uint8;uint8;} 版面配置的類型 可能會轉變為版面配置 struct{uint16;}。FIDL 則是提供可擴充的資料 但這類結構不支援電線格式本身的演變,例如 例如改用更有效率的呈現方式有兩項 交易標頭中的資訊,有助於 FIDL 的二進位檔相容性 透過通訊協定和傳輸使用時的格式:

  • 魔法號碼:可指出線路格式的修訂版本。如果接收器 不支援這個修訂版本,因此可以確定性拒絕解碼,例如 而不是將錯誤解釋視為混亂的電線格式。
  • 旗標:表示在這則訊息中啟用的任何軟體轉換。適用對象 舉例來說,在聯集到 xunion 的遷移作業期間,旗標 用於表示聯集已使用可延伸 這種表示法

單獨使用 FIDL 線格式時,則缺少這項資訊 相關結果建議將部分資訊納入 編碼和解碼作業具體違規事項如下:

  • Encoding 會將繫結/語言特定的網域物件轉換為 FIDL 編碼格式線形格式的不透明 blob 中繼資料,說明傳輸格式的修訂版本和功能
  • 「解碼」則會使用編碼形式的 FIDL 訊息,以及相應的 傳輸格式中繼資料,產生繫結/語言專屬網域物件。

在虛擬程式碼中,這些函式具有下列函式簽章:

function Encode<T>(object: T) -> (EncodedMessage, WireFormatMetadata);
function Decode<T>(message: EncodedMessage, metadata: WireFormatMetadata) -> T;

EncodedMessage 包含編碼的位元組,以及 撰寫新的電子郵件訊息大多數的繫結都會定義具有類似或相等用途的類型。

傳輸格式中繼資料本身會有與 64 位元相容的 ABI 整數值。在虛擬 C 標記法中,這個檔案的版面配置如下:

struct fidl_wire_format_metadata_t {
    uint8_t disambiguator;
    uint8_t magic_number;
    uint8_t at_rest_flags[2];
    uint8_t reserved[4];
};

RFC-0138:處理不明互動提議 將交易標頭中的旗標細分為 dynamic_flags - 通訊協定的要求/回應互動模型,以及 at_rest_flags - 與線路格式相關的問題。此 RFC 假設 但還是可以輕鬆適應,同時不會遺失 資源。

線路格式中繼資料應對齊 8 位元組, 就地將郵件解碼繫結「必須」可代表外部中繼資料 為 8 個位元組的不透明結構,對齊方式為 8 個位元組 (例如包含單一 uint64 欄位的結構)。讓中繼資料本身 藉此避免使用者 中繼資料。

繫結「必須」檢查 reserved 個位元組是否為零。繫結「不得」依附 at_rest_flags 位元組,表示具有任何特定值。繫結必須 驗證 magic_number 代表的傳輸格式修訂版本 支援

繫結「必須」檢查 disambiguator 位元組是否為零。其中有 0 個位元組 中繼資料的前方才能避免程式將 FIDL 訊息視為 訊息持續以檔案形式保留時,就會傳送文字 (請參閱 資料持續性的慣例)。

請注意,FIDL 交易標頭包含的資訊是超集 透過電匯格式中繼資料at_rest_flags 欄位的語意 交易標頭和 magic_number 欄位的值相同 傳輸格式中繼資料。

每則訊息「必須」與對應的中繼資料搭配使用。於 換句話說,這類檔案不得分享中繼資料 (例如,將中繼資料 A 用於 將訊息 A 和訊息 B 解碼) 或交換中繼資料 (例如使用中繼資料 A) 以解碼訊息 B,並使用中繼資料 B 解碼訊息 A)。這樣一來, 訊息傳輸格式修訂版本在執行期間 (例如傳送線) 修改 格式軟體遷移

繫結「必須」支援下列頂層類型的獨立使用:

  • 結構體
  • 表格
  • Union

如果使用其他資料類型,編碼和解碼函式「必須」執行失敗。失敗 應盡可能在編譯期間發生。

FIDL 語言不會規定傳輸格式中繼資料的傳輸方式。 或與編碼訊息建立關聯舉例來說,中繼資料可以是 在實際工作環境中使用 FIDL 時,衍生自交易郵件標頭 處理序間通訊 (IPC) 背景資訊。

資料持續使用的慣例

為了更有效支援激勵的應用實例,我們要指定 將中繼資料附加到已編碼訊息的慣例,其中位元組 訊息內容前方會加上中繼資料。繫結應支援這項功能 使用獨立線路格式的用法,稱為 持續性

涵蓋以下持續性用途:

  • 將單一 FIDL 物件寫入網路、磁碟,或其他位元組/封包導向 介面不支援傳輸 Zircon 控制代碼,且未選擇 轉換為要求/回應模式換句話說,資料為「靜態」。
  • 支援超過 64 KiB 的訊息。64 KiB 訊息大小上限為 Zircon 管道傳輸的資源。將訊息保留至位元組時 使用這種方式,就沒有這類限制。現有的 Rust 持續性 API 支援 且可用於減緩管道訊息大小限制 待處理的內建 FIDL 支援,手動將大型郵件保存下來 並將值轉換成 VMO

以下用途超出範圍:

  • 內建支援將相同類型訊息編碼的序列。 應用程式可以定義自訂串流方法, 他們的特定用途

使用這個加上前置字串的 API 變種版本,可提升多種方面的人體工學和安全性 方式:

  • 使用者不需要手動追蹤資料之間的關聯 和中繼資料資料會直接追蹤中繼資料,而且可以做為單一資料傳送 。相較之下,在架構外傳遞中繼資料會增加 不相符的版本。接收器需要處理 多種線路格式的多種版本,自動編入同一個持續性媒介中:
    • 當串流 API 中的寄件者變更身分時,新的寄件者可能 所說的通訊格式與原寄件者不同。
    • 假設 Proxy 會從多個元件接收持續性訊息 並使用不同傳輸格式修訂版本,然後將內容儲存至資料庫。 Proxy 就必須將頻外變種版本改回有前置字串 才能保留不同線路格式的修訂內容。
  • 緩衝管理能夠簡化且效能提升, 適合在熱線中使用獨立電線格式。例如: 繫結可以分配一個緩衝區,以保存中繼資料和 酬載,或使用第一個元素指向單一向量化寫入 到中繼資料
  • 使用者不必重新導入將中繼資料傳入的相同邏輯 並支援多種程式語言及用戶端程式庫,因為 FIDL 已提供 。

持續性 API 「必須」支援下列頂層類型:

  • 非資源結構
  • 非資源資料表
  • 非資源工會

若使用其他資料類型,持續性「必須」失敗。失敗「應該」發生於 盡可能減少編譯時間

在虛擬程式碼中,持續性 API 具有下列函式簽章:

function Persist<T>(object: T) -> vector<uint8>;
function Unpersist<T>(bytes: vector<uint8>) -> T;

繫結可能會使用替代命名/方法簽名, 以目標語言顯示適當內容,只要遵循 API 的架構即可 從資料流程的角度來看

繫結可能支援向量化的 Persist 變化版本,且該變化版本支援向量化 例如產生 zx_channel_iovec_tzx_iovec_t 來連結至 緩衝區數量,或整合目標的慣用作者介面 語言。繫結應提供向量變化版本 (如果已經使用) 顯示在 IPC 程式碼路徑中

繫結可以支援接收向量化的 Unpersist 變體 輸入內容,例如使用 zx_iovec_t 連結至多個緩衝區 與目標語言的慣用讀者介面整合。

請注意,持續性會產生位元組, 可能會產生處理

繫結「必須」支援持續保留大型值,導致編碼訊息 超過 64 KiB 的大小

請更新 FIDL 樣式指南和 API 評分量表,納入持續性 注意事項:

  • 清楚指出二進位 blob 使用持續性慣例還是 透過自訂/頻外機制傳遞中繼資料。

FIDL 原文語言

這個 RFC 不會變更 FIDL 來源語言。

實作

繫結應調整其獨立編碼/解碼 API,以符合 相關的設計設計,涉及中繼資料他們不一定要遵循 函式簽章,只要函式與提案一致 從資料依附元件的角度來看例如解碼器 必須透過部分方法設定,才能由中繼資料設定。

建議使用相同的獨立編碼/解碼 API 來導入訊息功能 例如透過 Zircon 管道傳送交易訊息。

可單獨新增持續性 API 變種版本的繫結支援。

目前已有實作持續性 API 在 Rust 繫結中,但資料格式和 API 與 墨西哥的 RFC。Rust 實作項目會依照已接受的 設計。

Rust 異動

目前,Rust 繫結提供下列函式:

fn create_persistent_header() -> PersistentHeader;
fn encode_persistent_header(header: &mut PersistentHeader) -> Result<Vec<u8>>;
fn encode_persistent<T: Persistable>(body: &mut T) -> Result<Vec<u8>>;
fn encode_persistent_body<T: Persistable>(body: &mut T, header: &PersistentHeader) -> Result<Vec<u8>>;
fn decode_persistent<T: Persistable>(bytes: &[u8]) -> Result<T>;
fn decode_persistent_header(bytes: &[u8]) -> Result<PersistentHeader>;
fn decode_persistent_body<T: Persistable>(bytes: &[u8], header: &PersistentHeader) -> Result<T>;

請替換成以下內容 (確切的簽名可能因為 借用與終身潛能):

fn persist<T: Persistable, W: std::io::Write>(body: &mut T, writer: W) -> Result<()>;
fn unpersist<T: Persistable, R: std::io::Read>(reader: R) -> Result<T>;

fn standalone_encode<T: TopLevel, W: std::io::Write, H: core::iter::Extend<HandleDisposition>>(body: &mut T, writer: W, out_handles: &mut H) -> Result<WireMetadata>;
fn standalone_decode<T: TopLevel, R: std::io::Read>(reader: R, handles: &mut [HandleInfo], metadata: &WireMetadata) -> Result<T>;

struct WireMetadata { /* private fields */ }

已針對結構體、聯集和資料表實作 TopLevel 特徵。

尤其是,使用者無法再以無任何內容建立永久標頭 並在編碼多則郵件時重複使用相同的標頭。

此外,繫結也「必須」提供序列化/去序列化方法 支援在頻外傳遞中繼資料的 WireMetadata 對位元組/從位元組傳遞。

成效

獨立編碼和解碼是 FIDL 交易用途的一部分 而持續性 API 則應共用大部分的程式碼路徑因此,我們 因此可重複使用相同的標準和效能基準。

人體工學

應設計具約束力的人體工學,以提高持久性 命名慣例舉例來說,繫結可以使用更簡短、更慣用的 表示永久變種版本的函式名稱 (例如 fidl::persist),並在 較長且更明確的函式名稱,代表獨立的公開函式 編碼/解碼 API (例如 fidl::standalone::encode)。

回溯相容性

這項變更本身俱有回溯相容性,因為 但 Rust FIDL 持續性實作除外。據我們所知 Rust FIDL 持續性的現有讀取者、寫入者和儲存的資料一律會改變 。

新增傳輸格式中繼資料,可改善日後的回溯相容性 預估即將進行的 FIDL 電匯格式遷移。

傳輸格式中繼資料含有 5 個保留的位元組。這些位元組可能是 重新剪輯成日後的意義。舉例來說,我們 用一個位元組描述持續性特定疑慮

安全性考量

FIDL 電匯格式的驗證規定在此。 並持有相同的安全屬性

請注意,FIDL 不是自我描述格式。已成功將 使用單一訊息類型的保留訊息,並不保證會 使用相同的訊息類型序列化:

  • 程式可能會不清楚 FIDL 訊息是否包含前置字串 中繼資料標頭,或者如果中繼資料在頻外傳遞, 輸入內容剖析不正確我們認為這類錯誤通常很容易被發現 測試階段提供清楚的說明文件, 有點混淆。

  • 惡意行為人可能會誘騙程式覆寫保留的 FIDL 含有 Foo 類型的訊息,以及 Bar 類型的另一則訊息, 藉由在路徑處理中利用漏洞。這個 可讓惡意行為人間接影響 Foo 的內容 撰寫新的電子郵件訊息

另一節將提出互動性較高的做法, 風險,可以透過擴充中繼資料標頭來提供訊息類型的相關資訊。

隱私權注意事項

FIDL 線格式的邊框間距位元組必須設為零, 有助於避免機密資訊外洩

與臨時性相比,持續性資料往往會產生更大的隱私權問題 「IPC 資料」可迅速取用,但也會將處理序間通訊 (IPC) 資料傳送至 元件,持續保存或透過網路傳送。因此, 隱私疑慮、處理序間 API 和永久性 API 差不多。

值得一提的是,開發人員可以隨時透過 其他方式,例如 JSON 或 XML。平時 日後設計時如果提及涉及 保留使用者或其他機密資料,無論方法是透過 FIDL 持續性。

FIDL API 元素上的隱私權註解將簡化隱私權審查流程, 提供成效更佳的下游工具 (例如自動遮蓋);外部 範圍,後者著重於傳送 FIDL 的特定方法 訊息。

測試

我們將擴充 GIDL (FIDL 合規測試套件) 來測試編碼 永久格式解碼

說明文件

  • 強化繫結規格,納入加入的 要求 (例如 LLCPP) 的要求。

  • 建立 FIDL 的參考頁面,用於獨立編碼/解碼及 持續性,以及兩個 API 之間的關係。

    • 已取得 RustLLCPP 相關的說明文件現有的說明文件將會更新。
  • 使用獨立編碼的所有語言新增至 //examples/fidl/ 以及新增對應的教學課程

缺點、替代方案和未知

替代方法 1:僅支援持續性 API

我們可以更進一步地說明慣例,並將其假設為標準: 所有中繼資料都必須在訊息酬載前方。足夠使用 而當今我們觀察到的案例,這樣有可能會成為 提供無人看作的獨立編碼/解碼功能 這個 API 和主要的持續性 API 可讓使用者選擇 配合自家設計的風格

替代方法 2:允許分享電匯格式中繼資料

我們可以允許為工作階段中的所有訊息共用相同的中繼資料, 這樣一來,系統就能在開頭傳送一次中繼資料,之後則不會 兩個對等點之間的通訊內容這可以用來 多個 FIDL 物件。例如: Fast datagram 通訊端 RFC 可避免將 8 個位元組增加到 每個 UDP 資料元,方法是先透過通訊端傳送中繼資料,然後 網址。

而中繼資料必須獨立於 並且僅根據 FIDL 的版本進行編碼 執行階段編譯為訊息的生產端這表示 FIDL 編碼器不得在執行階段任意切換線路格式表示法 可支援多種線路格式表示法

中繼資料額外收取的 8 位元組稅金似乎沒有明顯限制 。一律在訊息中附上這類訊息,能有效緩解大量中繼資料 跨越複雜的資料結尾。

針對真正想要的 中繼資料,我們可以考慮在 FIDL 中原生新增串流功能。適用對象 比如說,您可以想成如何在通訊端 單一類型的值實作繫結即可略過 中繼資料,以及禁止變更傳送者/接收者身分 ( 例如,每當有新對等連線 。

@stream
protocol UdpSocketPayload over zx.socket {
    Send(SendMsgPayload);
    -> Recv(RecvMsgPayload);
};

擁有訊息一律專屬的中繼資料也可讓使用者對 每則訊息都適用例如,我們可能會使用 1 標記來表示 已經過壓縮這也可察覺我們設計出更有力的電線 表示較不受限於有效率限制的表示法 記憶體內處理序間通訊 (IPC) (例如,不需要為指標或對齊保留空間)。 可用在保留區域的預留區域中,以其他位元表示 中繼資料

替代做法 3:使用交易郵件標頭

利用 交易郵件標頭

因此我們以傳輸形式建構持續性做為傳輸方式,方法為 寫入物件:

@persistence
type Metadata = table {
    1: foo int32;
    2: bar vector<int64>;
};

// desugars to
protocol MetadataSink over persistence {
    // Ordinal is the hash of `mylib/MetadataSink.Metadata`.
    Metadata(Metadata);
};

這個方法有一些優點:

  • 重複使用現有的 FIDL 功能。保留與透過 反而是將位元組寫入某種接收器 (vmo、 檔案、通訊端)。我們稍後也可以新增串流或多個訊息支援 開啟,方法是新增用來告知訊息大小的控制封包 (控制項) 等等
  • 重複使用序數雜湊檢查,減少交叉通訊並提升安全性: 攻擊者無法藉由在網站的 資料層 (酬載)這項策略與 FIDL 方法的屬性。

這看起來像是一段很常見的交通運輸方式,不過結果會是 只有一個單向和單向的通訊協定,因此很奇怪:用戶端可能只會 只會傳送一種值接收器沒有機會 作出回應。搭配我們不斷新增的演進功能,網格效果不佳 和封閉式互動

替代方法 4:使用訊息類型資訊擴充傳輸格式中繼資料

這比替代做法 3 還不重要,因此我們可以將完整的 保存訊息的類型名稱,並將該名稱新增至傳輸格式中繼資料 指出持續保留的訊息類型,但不會說明想法 我們才知道

這麼做可減少交叉溝通,但仍有其他細微的複雜性:

  • 如何處理類型重新命名:現存類型的名稱 一部分,因為這會影響中繼資料中的雜湊。
  • 如何利用 FIDL 的交易處理序間通訊 (IPC) 實現這一點:主要提案 將獨立編碼/解碼 API 制定為較低層級的核心功能 可用於建立交易處理序間通訊 (IPC) 功能的 FIDL 功能 上方。此替代方法會產生兩個獨立功能。具體而言 無法透過交易訊息衍生傳輸格式中繼資料 標頭,因為後者使用的序數雜湊與這兩者皆使用 請求和回應類型

整體而言,我們認為這項替代方案能防範的安全性風險並未 非常值得一試

替代方法 5:將獨立 API 限制為非資源類型

主要提案建議可處理 FIDL 線的兩種公用 API 格式:

  • 獨立的編碼/解碼:可對資源類型和產生控制代碼進行編碼。 由 FIDL 交易訊息實作實作 (用戶端和伺服器) 共用 繫結)。
  • 持續性:不允許資源類型;而資料來源為純粹傳輸格式 中繼資料一律會在酬載前面加上一個單位。

這源自於持續持久慣例的觀察 ,瞭解現行的 FIDL 獨立使用案例。

日後的應用情況可能更適合單獨傳輸或儲存 傳輸格式中繼資料和酬載。獨立開發 反而無法使用 API 處理控制的缺點 以免出現不必要的威脅

替代方法是提供三種 API:

  • 繫結-內部獨立編碼/解碼:可對資源類型進行編碼。二手車 直接處理交易型訊息導入作業
  • 公開獨立編碼/解碼:不允許資源類型。
  • 持續性:不允許資源類型。一律傳輸格式中繼資料 放在酬載之前,當做一個單位。

這可在需要個別用途時,改善非資源保證 傳輸或儲存電匯格式中繼資料,但會產生令人混淆的 API 這是因為我們最後得到了兩種編碼/解碼 API 支援多種資源類型再加上目標語言 有時難以公開隱藏 API 途徑。

主要提案採用簡化途徑,並結合前兩種 API 架構

既有藝術品和參考資料