RFC-0138:處理未知互動

RFC-0138:處理不明互動
狀態已接受
領域
  • FIDL
說明

我們擴充 FIDL 語意,讓對等點能處理未知的互動。

更小鳥
作者
審查人員
提交日期 (年月分)2021-05-25
審查日期 (年-月-日)2021-10-27

摘要

我們會擴充 FIDL 語意,讓對等點處理不明互動,例如接收不明事件或接收不明方法呼叫。為達成此目標:

  • 我們在 FIDL 語言中推出了彈性互動嚴格互動功能。這種彈性的互動方式,甚至可以由對等點妥善處理。嚴格互動會導致終止。

  • 我們引入三種通訊協定作業模式,封閉通訊協定一律禁止不明互動。相反地,開放式通訊協定是允許任何種類的未知互動。最後,ajar 通訊協定僅支援一種不明互動。

FIDL 支持演進的全方位觀點

在深入探討本提案的具體細節之前,建議您先瞭解 FIDL 的目標是如何解決演進的疑慮。

該問題有兩個面向:來源相容性 (API) 和二進位檔相容性 (ABI)。

API 相容性的目標是確保在變更發生前,根據產生的程式碼編寫的使用者程式碼仍可在變更後針對產生的程式碼進行編譯。舉例來說,如果在 FIDL 程式庫中新增宣告 (例如定義新的 type MyNewTable = table {};),就不會導致使用這個程式庫的現有程式碼無法編譯。

有三種積極做法可以解決來源相容性問題:

  1. 請盡可能讓更多變更來源相容 (例如 RFC-0057:預設無控制代碼);
  2. 提供明確的保證 (例如 RFC-0024:強制來源相容性);
  3. 提供版本管理 (例如 RFC-0083: FIDL 版本管理)。

除此之外,ABI 相容性旨在為針對不同程式庫版本建構的程式,提供互通性。例如,兩個程式對資料表結構定義具有不同瞭解,但能夠成功進行通訊。

達成 ABI 相容性可分為三個部分:

  1. 靜態相容性涉及到在資料層級達成互通性,也就是當兩個具有不同結構定義的同個資料表可以互通的情況下,何時可以互通?
  2. 動態相容性假設所有資料類型都相容,並會著重於在對等體有不同版本的通訊協定 (例如不同方法) 時,達成互通性。
  3. 最後,在某些情況下,您無法選擇有不同的通訊協定,也就是解決方案是改為瞭解每個對等點的功能 (交涉),然後據此調整通訊 (通訊協定朗讀內容)。

當要求「本機彈性」時,動態相容性尤其適合,例如在最不具變更的運作模式中新增小型項目。在其他情況下,例如相對於 fuchsia.io2 的 fuchsia.io1,就必須轉換網域模型。我們需要「全球靈活性」,且解決方案屬於通訊協定協商類別。

我們在此 RFC 中特別討論的機制 (嚴格且靈活的互動) 可以提高動態相容性 (2) 的現狀。

術語

關於通訊協定的組合模型

兩名同事之間的通訊是一種「互動」。互動會從「要求」開始,可能需要回應

要求和回應都是「交易訊息」,以標頭 (「交易標頭」) 表示,後面會選擇性加上「酬載」1

互動為引導,我們分別命名為「用戶端」和「伺服器」用戶端對伺服器互動是指從用戶端傳送至伺服器的要求開始,如果反向方向出現,回應就會變成回應。同樣地,我們會談到伺服器與用戶端互動

我們通常會使用「射後」或「單向」一詞,來代表用戶端發起的無回應互動,而「呼叫」或「雙向」一詞則用於需要回應的互動 (一律在目前模型中啟動)。當伺服器啟動無回應互動的對等點時,通常稱為事件2

「通訊協定」是一組互動。我們使用通訊協定 (即用戶端和伺服器之間的一系列互動),將工作階段定義為用戶端與伺服器之間特定通訊的執行個體。

應用程式錯誤是指符合錯誤語法的條件。傳輸錯誤是指因核心錯誤 (例如寫入已關閉的管道) 或 FIDL 中發生錯誤而發生錯誤。

提振精神

Fuchsia 的核心原則是可更新:套件的設計宗旨為獨立更新。即使驅動程式可提供二進位檔穩定度,也能讓裝置順利更新至新版 Fuchsia,同時保留現有的驅動程式。FIDL 是達成這項可更新性的核心環境,主要用於定義應用程式二進位檔介面 (ABI),進而奠定了穩固的前向與回溯相容性基礎。

具體而言,我們想讓兩組對彼此的通訊協定稍有不同的瞭解,以安全地互通。更棒的是,我們想要確保兩個對等體的強力靜態保證是「相容」。

許多工作已證實為 FIDL 類型編碼和解碼作業提供了彈性和保證,這稱為「靜態相容性」。我們推出了 table 版面配置,也就是 union 版面配置,選擇明確的 union 版面配置,導入 strictflexible版面配置修飾符,導入 protocol 序數雜湊減少 protocol 或第 61} 或第 61}第 61 類第 條雜湊的衝突。

我們現在要轉化動態彈性和保證,這稱為動態相容性。假設兩個對等點都具有靜態相容性,亦即所有用於互動的類型都處於靜態相容性,動態相容性就是這兩個對等點都能夠成功互通,而且兩者都沒有因非預期互動而取消通訊。

相關人員

  • 講師:jamesr@google.com。
  • 審查者:
    • abarth@google.com (美國聯邦通信委員會)
    • bprosnitz@google.com (FIDL)
    • ianloic@google.com (FIDL)
    • yifeit@google.com (FIDL)
  • 諮詢時間:
    • jamesr@google.com
    • jeremymanson@google.com
    • jsankey@google.com
    • tombergan@google.com
  • 社會化:RFC 草稿與 FIDL 團隊共用,並與 Fuchsia 團隊的許多成員進行討論。它在 Eng Council Discuss 郵寄清單 (eng-council-discuss@fuchsia.dev) 上廣泛分享。

設計

我們推出「彈性互動」和「嚴格互動」的概念。巧妙地,即使不明,也能由對等點妥善處理彈性互動。反之,如果接收端未知,則嚴格互動會導致對等點突然終止工作階段。我們將互動的「嚴格程度」稱為互動,分為彈性或嚴格的互動方式。請參閱彈性和嚴格互動的語意

在沒有防護機制的情況下,我們可能會在無意間使用彈性互動,導致隱私權受到危害:

  • 例如設計能不斷改進的轉譯引擎。新版本將 flexible SetAlphaBlending(...); 與意圖加入單向互動,因此針對以舊版轉譯器為目標的較新用戶端,系統只會忽略其設定 (但大部分的轉譯程序仍會正常運作)。現在,如果該新方法與特殊的 PII 轉譯模式相關,StartPIIRendering(); 就必須為舊版轉譯器停止處理 (而不是忽略),因此適合使用 strict 互動。
  • 另一個例子是惡意的對等點傳送各種訊息,嘗試以反射方式探索暴露表面,看看哪一則被理解。一般來說,反射功能會耗用額外的效能成本,並開啟隱私權問題 (您可能會比實際感受到更多)。根據原則,FIDL 會選擇禁止反射,或要求明確選擇加入。

因此,我們另外引入三種能夠執行通訊協定的模式:

  • 「封閉通訊協定」是指允許或預期沒有彈性互動,且接收彈性互動異常的情形。
  • 開放式通訊協定是允許任何彈性互動的方式 (單向或雙向)。這類通訊協定提供最大的彈性。
  • ajar 通訊協定是允許彈性的單向互動 (射後不理呼叫和事件),但不允許彈性的雙向互動 (如果對等點不知道這個方法,則無法進行方法呼叫)。

詳情請參閱「通訊協定語意」。

嚴格靈活的互動語義

嚴格互動的語意很簡單:當收到未知的要求時 (亦即,如果接收者不知道其序數),對等端會突然終止工作階段 (也就是關閉管道)。

彈性互動的目標,是讓收件者妥善處理不明互動。這會對設計產生影響。

彈性互動的傳送者必須知道接收者可能會忽略其要求 (因為接收者無法理解)。

接收者必須能夠表明這項要求很有彈性 (而非嚴格),並據此採取行動。

由於雙向互動需要收件者回應傳送者,因此不明要求的接收者必須在沒有其他詳細資料的情況下,才能建立回應。接收者必須告知傳送者,說明要求無法理解。為滿足這項要求,彈性雙向互動的回應是結果聯集 (請參閱詳細資料)。

定義依據為語意,如果是單向互動,傳送者無法分辨接收者是否已知或不知道其要求。使用彈性的單向互動時,FIDL 作者應謹慎瞭解其整體通訊協定的語意。

值得注意的是,單向互動算是「最理想」,因為傳送者無法判斷對等點是否收到互動。然而,管道提供排序保證,讓互動的排序具有確定性且已知。嚴格的單向互動使我們得以確保只有在前一個互動能夠理解時,才會發生某些互動。舉例來說,記錄通訊協定可能具有 StartPii()StopPii() 嚴格互動,以確保沒有任何對等互連項目忽略這些項目。

如需進一步討論選擇嚴格與靈活互動時應考量的優缺點,請參閱:

開放式、封閉式和 ajar 通訊協定的語意

closed 通訊協定的語意設有限制,只有嚴格互動,沒有彈性的互動。closed 通訊協定含有任何 flexible 互動,此為編譯時間錯誤。

ajar 通訊協定的語意允許嚴格互動,以及一種靈活的互動。ajar 通訊協定含有任何 flexible 雙向互動,此為編譯時間錯誤。

open 通訊協定沒有限制,嚴格和靈活,允許單向和雙向互動。

如要進一步瞭解選擇封閉、ajar 或開放式通訊協定時應考量的優缺點,請參閱:

語言變更

我們推出 strictflexible 修飾符,將互動標示為嚴格或彈性:


protocol Example {
    strict Shutdown();
    flexible Update(value int32) -> () error UpdateError;
    flexible -> OnShutdown(...);
};

根據預設,互動方式具有彈性。

審慎設定樣式後,建議您一律明確指出互動的嚴格程度,也就是說,應為每個互動設定此樣式3

我們導入了 closedajaropen 修飾符,將通訊協定標示為已關閉、jar (部分為開放式) 或開放式:


closed protocol OnlyStrictInteractions { ...
ajar protocol StrictAndOneWayFlexibleInteractions { ...
open protocol AnyInteractions { ...

在封閉的通訊協定中,沒有定義任何彈性的互動。封閉的通訊協定只能組合其他封閉的通訊協定。

在 ajar 通訊協定中,無法定義兩種彈性互動。ajar 通訊協定只能組合封閉通訊協定或 ajar 通訊協定。

(開放式通訊協定沒有限制)。

通訊協定預設為開啟。

這個提案的先前版本指定了 ajar 做為預設值。然而,在未明確修飾詞宣告的雙向方法的情況下,開放性修飾符的預設值 ajar 與嚴格度修飾符的預設值彈性發生衝突,就會產生衝突。這表示包含兩種方法的通訊協定在編譯時,至少要對通訊協定或方法都設有修飾符才能編譯。如下所示:開放性的預設值以粗體顯示,而嚴格度的預設值以斜體顯示。

視覺化:以嚴格/彈性方式顯示開啟/ajar/封閉式編譯組合的格線。

為解決此問題,我們將開放性從 ajar 變更為開放式,讓通訊協定可以在不具有修飾符的情況下,在通訊協定或方法上不含修飾符的兩種方法編譯。

妥善設定樣式指南,建議您一律明確指定通訊協定的模式,即應針對每個通訊協定設定此模式。[^default-debate]

變更傳輸格式:交易郵件標頭旗標

我們將交易訊息標頭修改為:

  • 交易 ID (uint32)
  • 靜態標記 (array<uint8>:2,即 2 個位元組)
  • 動態旗標 (uint8)
  • 魔法數字 (uint8)
  • 序數 (uint64)

即標記位元組分成兩個部分,靜態標記為兩個位元組,而動態標記 1 個位元組。

動態旗標位元組的結構如下:

  • 位元 7,第一個 MSB「嚴格度位元」:嚴格方法 0,靈活方法 1。
  • 位元 6 到 0,未使用,已設為 0。

歡迎進一步瞭解「動態旗標」的使用方式:

  1. 我們在第三版交易訊息標頭中新增了標記。這些標記是「暫時用於軟性遷移作業」。例如,在嚴格到可擴充聯集遷移作業期間,使用了一個位元。不過,目前沒有需要使用這些標記的方案才能一次使用。因此,我們可以變更這些旗標的意圖,避免僅僅暫時用於暫時用,並當做傳輸格式的一部分使用。

  2. 傳送者必須使用嚴格位元,才能向接收方指出 strict 互動,在接收方不知道互動的情況下。本案例預期的語意是要突然終止的通訊。如果沒有此嚴格程度,傳送方與接收者之間的偏差可能會遭到忽略。舉例來說,如果是含有新新增 strict StopSomethingImportant(); 單向互動的 ajar (或開放式) 通訊協定,如果沒有嚴謹的位元,接收器只能猜測未知的互動是嚴格還是彈性,以便根據此 RFC 所追求的可進化性改進措施選擇彈性。因此,FIDL 作者在擴充通訊協定時將強制仰賴雙向的嚴格互動。

另請參閱「在交易 ID 中設置嚴格程度」一文,瞭解替代表示法;如要瞭解未來可能需要使用的替代表示法,請參閱互動模式位元

線路格式變更:結果聯集

結果聯集目前有兩個變化版本 (一般1成功回應的變化版本,第2錯誤回應的序數) 已擴展為第第三個變化版本,一般為 3,還會帶有新的列舉 fidl.TransportError 表示「傳輸層級錯誤」。

舉例來說,互動如下:

open protocol AreYouHere {
    flexible Ping() -> (struct { pong Pong; }) error uint32;
};

具有回應酬載:

type result = union {
    1: response struct { pong Pong; };
    2: err uint32;
    3: transport_err fidl.TransportError;
};

具體來說,如果彈性方法使用 error 語法,則會據此設定成功類型和錯誤類型 (分別為一般 1 和 2)。否則,如果彈性方法未使用 error 語法,結果聯集 (一般 2) 的錯誤變體會標示為 reserved4

部分精確度:5

  • 我們是從應用程式的角度選擇 transport_err 名稱,而該錯誤的來源應該無法區分。會發生應用程式錯誤,其後則是「傳輸錯誤」,也就是因 FIDL 編碼/解碼、FIDL 通訊協定錯誤、核心錯誤等因素而發生的錯誤數量。基本上,「傳輸錯誤」是指架構中可能發生的所有錯誤類型 (包含許多層的軟體)。

  • 我們將類型 fidl.TransportErr 定義為嚴格的 int32 列舉,其中包含單一變數 UNKNOWN_METHOD。此變化版本的值與 ZX_ERR_NOT_SUPPORTED 相同,也就是 -2:

    type TransportErr = strict enum : int32 {
      UNKNOWN_METHOD = -2;
    };
    

    向用戶端呈現傳輸錯誤時,如果繫結提供了為不明互動 transport_err 取得 zx.status 的方法,則繫結必須使用 ZX_ERR_NOT_SUPPORTED。不過,如果不明互動 transport_err 不符合 zx.status 向用戶端呈現的方式,則不需要繫結。

    另一種方法是只使用 zx.status,並一律使用 ZX_ERR_NOT_SUPPORTED 做為值來表示不明方法,但這有兩個重要的缺點:

    • 這需要一個程式庫 zx 的依附元件,許多程式庫可能不會直接使用這類依附元件。這會導致很難在 IR 中定義結果聯集,因為我們需要在 zx 中自動插入依附元件,或將類型降級至 IR 中的 int32,但系統產生的繫結會將類型視為 zx.status

    • 但不會定義繫結應如何處理非 ZX_ERR_NOT_SUPPORTEDtransport_err 值。透過指定類型為嚴格列舉,我們會明確定義繫結的語意,該繫結接收系統無法辨識的 transport_err 值;系統會將該類型視為解碼錯誤。

  • 如果我們描述的是共用通用結構的聯集類型類別 (即三個一般、第一個變化版本不受限制 (成功類型可以是任何內容),第二個變體必須為 int32uint32 或列舉,且第三個變化版本必須是 fidl.transport_err),就是為了簡單起見,我們稱之為「結果聯集」。

JSON IR 的變更

我們會在 JSON IR 中公開互動的嚴格程度。實際上,我們會更新 #/definitions/interface-method 類型,並新增 strict 布林值做為 ordinalnameis_composed 等的同層。

我們會在 JSON IR 中公開通訊協定的模式。實務上,我們會更新 #/definitions/interface 類型,並新增含有成員 closedajaropenmode 列舉,做為 composed_protocolsmethods 等的同層。

繫結的變更

我們希望在系統提供自動處理要求的繫結時,顯示繫結資訊。舉例來說,雖然繫結能夠自動建構表示要求不明的要求,但重要的是,兩者都提出了收到的未知要求 (可能包括與要求相關的中繼資料),且選擇回應「要求不明」或突然終止通訊。

靜態疑慮。

  • 如果是彈性互動,繫結應透過與顯示其他傳輸層級錯誤 (例如 zx_channel_write 的錯誤) 或在解碼期間發生錯誤等的機制,向用戶端顯示結果聯集的 transport_err 變體。結果聯集的 errresponse 變化版本應與用戶端呈現的方式相同,也就是在宣告為嚴格方法時,繫結會顯示這些類型的方式。

    • 舉例來說,在 Rust 繫結中,Result<T, fidl::Error> 是用來表示呼叫的其他傳輸層級錯誤,因此 transport_err 應合併至 fidl::Error。同樣地,在低階 C++ 繫結中,fit::result<fidl::Error> 可用來傳送傳輸層級錯誤,因此 transport_err 應合併為 fidl::Errorresponseerr 變數的傳送方式與嚴格方法相同。在 Rust 中,這意味著 Result<Result<T, ApplicationError>, fidl::Error> 代表方法含有錯誤語法,Result<T, fidl::Error> 適用於沒有錯誤語法的方法,response 值為 Terr 值則為 ApplicationError

    • 如果是折疊錯誤為 zx.status 的繫結,必須將 transport_errUNKNOWN_METHOD 轉換為 ZX_ERR_NOT_SUPPORTED

動態疑慮。

  • 使用 zx_channel_writezx_channel_call 或其同層傳送要求時,必須依照下列方式設定動態標記:
    • 對於嚴格互動,嚴格位元 (bit 7) 必須設為 0,且必須設為 1 才能採用彈性互動。
    • 接下來 6 位元必須設定為 0。
  • 收到已知互動時:
    • 目前的繫結機制沒有任何改變。
    • 具體來說,繫結不應驗證嚴格程度,以簡化從嚴格遷移至彈性互動的作業 (反之亦然)。
  • 收到不明互動 (例如不明序數) 時:
    • 如果互動為嚴格 (如收到嚴格度標記表示):
    • 繫結必須關閉通訊 (即關閉管道)。
    • 如果互動很有彈性 (如收到的嚴格度標記所示):
    • 如果是已關閉的通訊協定,繫結必須關閉管道。
    • 如果互動為單向互動 (交易 ID 為零):
      • 繫結必須向應用程式引發這項未知的互動 (詳情請見下文)。
    • 如果互動為雙向互動 (交易 ID 非零):
      • 如果是 ajar 通訊協定,繫結必須關閉管道。
      • 針對開放式通訊協定,繫結必須向應用程式引發這種未知的互動 (詳情請見下方)。
    • 進一步瞭解如何引發不明互動:
      • 如果互動是雙向的,繫結必須傳送結果聯集回應要求,方法是使用所選的第三個變化版本與 UNKNOWN_METHODfidl.TransportErr 傳送結果聯集。這項程序必須在向使用者程式碼產生不明互動前完成。
      • 繫結應透過叫用先前註冊的處理常式 (或類似) 的方式,向應用程式引發未知互動。
      • 建議您針對繫結要求註冊不明的互動處理常式,以避免建構可能遭到誤解的「預設行為」。繫結可提供「免人工管理處理常式」或類似功能,但建議您讓繫結作業明確。
      • 繫結「可」選擇在處理不明互動時,為應用程式提供關閉管道的選項。

不明訊息包含帳號代碼時,伺服器必須關閉傳入訊息中的控點。伺服器必須先關閉傳入訊息中的所有控點,接著才能:

  • 關閉通道;在嚴格方法的情況下,會用一個在封閉通訊協定上的彈性方法,或對 ajar 通訊協定的彈性雙向方法關閉管道
  • 回覆訊息 (在開放式通訊協定上使用彈性雙向方法時)
  • 在開放式通訊協定或 ajar 通訊協定上有彈性的單向方法呼叫時,通知使用者程式碼出現不明的方法呼叫。

同樣地,如果用戶端收到包含帳號代碼的不明事件,用戶端就必須關閉傳入訊息中的控點。用戶端必須在以下前關閉傳入訊息中的所有帳號代碼:

  • 關閉管道。發生嚴格事件或基於封閉通訊協定的彈性事件時,
  • 在開放式或 ajar 通訊協定有彈性事件的情況下,通知使用者程式碼不明事件。

一般來說,處理不明互動時,作業順序如下。

  1. 關閉傳入訊息中的控點。
  2. 可以的話,請關閉該管道或傳送 UNKNOWN_METHOD 回覆。
  3. 將不明互動提高至不明互動處理常式,或回報錯誤。

在非同步環境中,多個執行緒可能會同時嘗試在管道上傳送/接收訊息,可能無法在回報不明方法錯誤前,確保管道已關閉。因此,如果互動嚴重且出現不明方法或事件的錯誤,則不需要先關閉管道。不過,如要按照此 RFC 中指定的可復原不明互動,您必須先關閉控點和回覆 (如適用),然後才能分派不明的互動處理常式。

此 RFC 的先前版本並未指定傳入訊息的關閉控制代碼順序、回應未知的雙向方法,並且會向使用者引發未知互動。

相容性影響

ABI 相容性

將互動從 strict 變更為 flexible,或將 flexible 變更為 strict 並不相容。

變更通訊協定模式 (例如從 closed 變更為 ajar) 並無法與 ABI 相容。雖然從更嚴格的模式變更為限制較少的模式或許也可以與 ABI 相容,但實際上並非是由於通訊協定同時定義傳送者和接收端 (射後不去和事件)。

所有變更都可虛轉移。如有需要,修飾符可以版本化

來源相容性

將互動從 strict 變更為 flexible,或者將 flexible 變更為 strict 可能與來源相容。無論互動的嚴格程度為何,都鼓勵繫結提供相同的 API,也就是折疊現有的傳輸錯誤 API。

變更通訊協定模式 (例如從 closed 變更為 ajar) 並不相容。根據通訊協定模式,建議繫結功能專門提供其提供的 API。舉例來說,封閉的通訊協定不需要提供「不明方法」處理常式,因此建議不要提供未使用的處理常式。

與平台版本管理的關係

RFC-0002 演變一節所述,「只要平台對 Fuchsia 系統介面的語意進行回溯不相容時,就會變更 ABI 修訂版本」。

我們達成「可更新」目標的成效之一,就是剖析新的 ABI 修訂版本的速度。由於您可以以回溯相容的方式新增或移除彈性互動,因此這項功能將有助於改善小雪的可更新性。

實作

  • 我們可以想像,繫結只會實作規格中嚴格部分的情況,確保在通訊中能及早停止,如同同業遇到其他錯誤或錯誤一樣。
  • 由於 FIDL 的重要性在於 FIDL (即第 1 名) 的重要性,因此這並非必要,因此我們需要繫結來遵循這個規格。
  • 為符合繫結規範,繫結「必須」實作嚴格靈活的互動語意,以及通訊協定的三種模式。
  • 因此,我們詳細變更了繫結規格。這是 ABI 破壞性,是傳輸格式的主要演變 (涵蓋「靜態」和「動態」問題)。

此 RFC 的前一個版本呼叫了新的神奇數字背後的未知互動。不過如已指定,不明互動可回溯相容現有的通訊協定,因為先前用於表示嚴格程度的標頭位元之前未使用/保留,而且傳輸格式僅針對彈性兩種方法進行變更,但這些方法只能存在於開放式通訊協定中。我們不會變更魔法數字,而是使用兩個階段推出來啟用不明互動支援功能,但將預設修飾符設為 closedstrict,然後將這些修飾符明確新增至現有的 FIDL 檔案,然後將預設值變更為 openflexible

效能注意事項

不會影響 closed 通訊協定。封閉式通訊協定不需要檢查嚴格位元,如「繫結的變更」一節所述。

ajaropen 通訊協定的小影響:

  • 處理不明互動的情形類似於處理已知互動、叫用預先註冊處理常式,以及執行應用程式程式碼。
  • 此外,如果遇到雙向不明互動 (僅限 open 通訊協定) 的情況,繫結將建構並傳送回應。

我們預期效能考量很少的問題,而且在選擇通訊協定模式時,主要取決於安全性考量

人體工學

這使得 FIDL 變得更加難以理解,但能解決對演變性的重要需求。然而,一直以來都是尖銳的邊緣。

回溯相容性

此功能無法回溯相容,並且需要將所有 FIDL 用戶端和伺服器進行軟遷移。

安全性考量

新增對對等點傳送不明要求的功能 (即在彈性互動的情況下),可以減少安全疑慮。

針對特別敏感的通訊協定,系統可能需要以高度嚴謹的互動方式來排除演化問題,因此建議使用 closed 通訊協定。Fuchsia 的大多數內側則是使用 closed 通訊協定 (例如 fuchsia.ldsvc)。

當您考慮使用 ajaropen 通訊協定時,FIDL 作者需要考慮兩個疑慮:

  • 惡意的對等點傳送具有大型承載的未知要求。(這與使用 flexible 類型時,必須一併處理大量不明酬載的問題類似)。如大小會影響 ABI 影響,則需要使用更多功能才能為 FIDL 作者提供控制權,且將在日後的工作中加以處理。
  • 開啟通訊協定探查大門,讓同業在不知情的情況下嘗試瞭解要實作的方法,接著努力製作訊息,以運用發現的方法。如果實作顯示的方法超出預期,就可能發生問題。舉例來說,如要公開父項通訊協定,而是改為繫結可組成父項的子項通訊協定。請注意,雖然靈活的互動沒有改變攻擊向量,但因為對等點能夠連續嘗試多個序數,而不需要重新連線 (在某些情況下,這可能會非常昂貴),因此更容易遭利用。
  • 在選擇採用 ajaropen 通訊協定之間取得平衡時,請考慮對等點無法判斷某種互動是否被處理或忽略,但在 open 通訊協定允許兩種未知互動的情況下,處理對等互連會揭露其無法理解互動,而這可能會向惡意對等點透露寶貴資訊。

隱私權注意事項

如果開通通訊協定探查,可能會導致隱私權疑慮。如安全性考量一節所述,這個威脅模型並非由 RFC 變更,但可能更容易遭利用。

測試

如要開發此 RFC 中描述的新功能集,關鍵在於確保所有繫結都遵循相同的規範,所有行為也都以類似的方式運作。因此,需要能夠在測試中表達規格,例如「傳送這個要求,以正確的交易 ID 回應,但一般而言,預期傳送者管道要關閉」。根據我們的經驗,多半將重點放在高規格表現規格後,測試會隨之增加,因此,該規格的所有繫結都會提高法規遵循程度,同時提高迴歸的防護能力。

我們將採用與編碼和解碼相同的方法 (開發 GIDL):一開始先以手動方式撰寫測試,盡可能執行更多繫結,並將可能受到宣告式測試方法的部分一般化。雖然我們希望能為動態問題建構與 GIDL 類似的工具,但追求目標,但這並非以最終結果為主,更可能偏好以手作表達的測試。

說明文件

我們會針對這項功能提供詳盡的說明文件。在規格端:

我們會新增 FIDL API Rubric 中的額外項目,以說明通訊協定演變。

在特定目標語言中具體使用這項功能時,我們希望每個繫結都會更新說明文件,並提供實際範例。

缺點、替代項目和未知

缺點:訊息大小上限為 ABI 影響

處理不明問題 (例如使用 flexible 類型或本文所述的不明互動可能會遇到的不明酬載) 是指對等點預期讀取的訊息大小上限受到 ABI 影響,因為沒有明確描述這項限制,並非靜態驗證。

目前,管道沒有向量化讀取,也無法進行部分讀取。因此,可將訊息傳送到符合所有要求 (例如彈性互動、對等的預期時) 的對等點,而會導致通訊失敗,進而破壞 ABI。如果有問題的訊息因同業預期訊息小於 1 KiB 而無法讀取,則系統一律不會讀取超出限制的新訊息,且會關閉管道,雙方的通訊也會取消。

因為採用 flexible 類型,彈性互動的加入會使此類問題更加嚴重。

以下是日後的指引方向:

  • 向量化管道讀取,允許接收者例如僅讀取訊息的標頭,然後決定要讀取其餘的酬載或捨棄該訊息 (這也會需要新的系統呼叫)。
  • 將訊息大小上限設為通訊協定的明確屬性,可能使用預先定義的大小類別,例如 smallmediumlargeunbounded

替代方法:與指令模式比較

指令模式有助於用戶端批次處理伺服器要處理的許多要求。您也可以使用指令模式來達成此 RFC 中描述的易變性。

例如:

open protocol AnOpenProtocol {
    flexible FirstMethod(FirstMethodRequest) -> (FirstMethodResponse);
    flexible SecondMethod(SecondMethodRequest) -> (SecondMethodResponse);
};

這可透過以下的封閉通訊協定進行近似,也就是說,如果現在採用現在的 FIDL 功能,必須採用這種功能才能達到相同程度的演化:

closed protocol SimulateAnOpenProtocol {
    strict Call(Request) -> (Response);
};

type Request = flexible union {
    1: first FirstMethodRequest;
    2: second SecondMethodRequest;
    ...
};

type Response = flexible union {
    1: first FirstMethodResponse;
    2: second SecondMethodResponse;
    ...
    n: transport_err zx.status;
};

不難想見的是,指令模式的方法無法滿足他們的需求。

由於我們必須比對每項要求與聯集中的回應,因此我們失去了「比對組合」的語法執行,而這也會導致缺少語法位置。

由於不真實的伺服器可能會以 SecondMethodResponse 回應 FirstMethodRequest,因此我們也會失去類型安全。比如說,智慧型繫結可能會注意到這個模式 (也許透過 @command 屬性的輔助),並且提供與目前方法相同的人體工學。

在線路層級中,指令模式會強制決定排序的「two method discriminators」。交易訊息標頭中有序數 (識別 Call 為互動),還有聯集序數 (識別選取的是聯集的變化版本,例如 1 代表 FirstMethodRequest,2 代表 SecondMethodRequest)。

這裡再次強調,如果所有方法都遵循指令模式,也就是所有方法的要求和回應都是聯集,我們就不需要交易訊息標頭中的一般。基本上,上述彈性通訊協定會使用指令模式「編譯為」封閉的通訊協定。聯集傳輸格式需要計算變化版本的位元組和控點,且這些計數需要由相容的解碼器驗證。這個問題在兩個面向之間存在問題:

  • 交易訊息標頭允許的彈性 (不描述酬載,如果可以進行解碼) 是與聯集傳輸格式 (實際上根據設計) 不相符的程度。這種靈活度與簡便性特別適合用於低階使用 (這種 FIDL 會旋轉)。

  • 組合模型沒有「通訊協定分組」的概念。這個功能非常強大,因為我們可以在同一個管道中 (而且可以執行) 多個通訊協定。我們會盡可能使用結構化組合 (例如 compose 標記),以及採用動態組合 (例如服務探索)。如果我們呈現的檢視畫面是「所有編譯為聯集」,就會實施嚴謹的群組。

最後,有些 FIDL 作者希望能「自動批次處理要求」。舉例來說,fuchsia.ui.scenic 程式庫因使用 fuchsia.ui.scenic/Session.Enqueue 方法中的指令模式而廣為人知。不過,提供「自動批次處理要求」是需要考量的危險功能,因為在一個單位中處理多個指令的語意,主要會因應用程式彼此大不相同。我們應如何處理未知的指令?該如何處理失敗的指令?是否應忽略指令、停止執行、導致取消和復原?即使是以「批次工作單元」(交易) 概念設計的 RDBMs 系統,通常也會提供許多批次處理模式 ([隔離層級)(https://en.wikipedia.org/wiki/Isolation_(database_systems))。總結來說,FIDL 沒有支援「自動批次處理要求」的計畫。

總而言之,雖然表面上看來,嚴格靈活互動的語意可能與指令模式相同,但特殊語意卻明顯不同。

替代做法:通訊協定協商

什麼是通訊協定交涉

通訊協定協商是廣泛的詞彙,描述同業彼此互動的技術,逐步建立彼此相關的背景資訊,因此可讓通訊對象正確、更快且更有效率。

舉例來說,假設我隨機打電話來。也許對等點會從「所以,是,是嗎?」開始。您已經從沒有對等點的背景資訊進入了某些身分識別。我們可以繼續說「噢,是這樣。對嗎?」,由於行銷電話的興起,您現在可能會遇到 「你是誰?」。諸如此類的資料都有幫助。這對同類應用程式的開發人員幾乎都不太瞭解對方的身分,也不太清楚擁有哪些功能。

  • 哪些資料元素可以理解?就像指出所需的資料表欄位對等互連,也請務必避免對等點產生大量複雜資料,只有在收到時才會忽略。
  • 同類應用程式支援哪些方法?在轉譯引擎中,您可以想像看看 Alpha 混合功能是否可用為功能,如果不可用,就會調整與轉譯器的互動 (可能是傳送不同內容)。
  • 應使用哪些效能特性?通常需要協商緩衝區的大小,或協議可發出呼叫的頻率 (思考配額)。

每種類型通常都需要稍微不同的解決方案,但基本上都會將互動模型的抽象說明 (例如「同業可理解的方法組合」) 轉換成可交換的資料。

如要有效解決通訊協定交涉問題,第一步是提供概念的說明 (「通訊協定」、「方法 foo 的回應類型」)。此外,由於同儕一開始的情境很低,也就是他們並不瞭解彼此,而且必須假設彼此對世界的定義不同,因此概念的說明往往仰賴結構屬性。舉例來說,說「回應類型為 MyCoolType」表示沒有意義,且不必解釋,但說「回應類型為 struct { bool; }」表示有其自身,因此可以免除上下文的解釋。

通訊協定協商與嚴格靈活互動之間的關係

這個 RFC 中提出嚴謹且靈活的互動要求,提供了不斷演進的通訊協定所需要的彈性空間。現在您可以新增或移除方法。或許還不夠多。但濫用演進技術之後,通訊協定就會變得異常,導致網域難以理解。這與一段時間的資料表類似,因為這類資料表現在代表一種「匯總結構」,其中包含一組隨時間變化的多組要求。

在合約通訊協定交涉中,如果運用得當,您可以有效隔離版本管理負擔,並在某些動態選擇 (協商) 之後,建立更簡潔且嚴格的通訊協定 (可能是 closed 通訊協定)。

這兩種演化技巧都有其進步空間,而且在演化工具盒中都是必備的。

替代方法:在交易 ID 中加入嚴格位元

使用交易 ID 來傳達嚴格靈活互動所需的位元時,有一項重要缺點。部分交易 ID 是由核心產生,也就是說,zx_channel_call 會將訊息的前四個位元組視為 zx_txid_t 類型的交易 ID。將更多資訊封裝至交易 ID 會強制在核心與 FIDL 之間建立更強的耦合,這是不適當的做法。改用交易標頭標記後,使用 zx_channel_call 的 FIDL 程式碼將能繼續建構標頭中的所有項目,除了 ID 以外。

替代選項:互動模式位元

此 RFC 的較舊版本稱為「互動模式」位元,以新增「互動模式」位元,以區分來自雙向互動的單向互動,並預期擴展為更複雜的互動,例如終端機互動

如果互動模式位元與交易 ID 中提供的資訊重複,主要的缺點:一種是具有零交易 ID 的其中一種互動方式,而雙向互動會含有非零的交易 ID。由於資訊備援功能,這會開啟使用不同部分備援的實作項目 (例如繫結) 來確定如何處理訊息。進而惡意撰寫訊息,避免系統不同部分以不同的方式解讀訊息。

雖然我們希望能同時將交易 ID 指派給所有互動,並擴展互動模式,但這兩個變更都必須在互動模式中需要額外資料,但我們還是希望針對這些功能設計時間,呈現設計討論的表格。

替代方法:命名

隨著此 RFC 疊代,有很多關於如何正確命名新概念的討論。我們在此摘要說明其中幾項討論。

如何區分「不明」與需要「已知」的互動:

  • 已選擇 openclosed 個原始名稱。
  • (none)required (前提是對等點必須實作此方法),否則通訊協定將會終止。
  • 入圍者:RFC-0033:處理不明欄位與嚴格度中藉的 flexiblestrict

如何區分永遠不會接收不明互動的通訊協定,來自可以接收單向未知互動的通訊協定,這些通訊協定來自可以同時接收單向和雙向互動的通訊協定:

  • staticstandard,選擇了 dynamic 個原始名稱。「靜態」和「動態」的差別在於我們一直使用「靜態」和「動態」這兩個詞彙來是指 FIDL 的線路格式和訊息。舉例來說,這個 RFC 中一部分的「動態疑慮」與「動態通訊協定」不同,兩者的涵義與「動態」不同。
  • strict(none)flexible 再次從 RFC-0033 借用。
  • 取代 static,使用 sealed 強調通訊協定無法輕鬆展開。
  • 使用 hybridmixed 取代 standard
  • 入圍者: closedajaropen。由於開啟和關閉功能不會用於互動,因此可以將其用於通訊協定修飾符。罐子的定義字面上其實「部分開口」,正好就是我們所說的概念。是的,大家都擔心自己有點詭異的感覺。

優先藝術與參考資料

(如文字所述)。


  1. 簡單來說,訊息 (相對於交易訊息) 是指 FIDL 值的編碼形式

  2. 請注意,對於 fidlc 和 JSON IR 愛好者,請注意,編譯器的內部代表事件maybe_request_payload等於nullptrmaybe_response_payload等於 present。然而,從模型的角度來看,我們稱此酬載為要求,但採用伺服器對用戶端的方向。我們應與組合模型保持一致,變更 fidlc 和 JSON IR。這不在 RFC 的範圍內,但為確保完整性。

  3. 我們偏好一體適用的文法,並遵循程式碼檢查作業強制執行的風格指南。我們之所以設計選擇,是因為希望同時為新用戶提供較易近人的語言,同時針對 Fuchsia 平台採用非常明確 (且相反) 的標準。 

  4. 值得注意的是,在 flexible 互動中新增 error 可當做與軟體式 ABI 相容的變更。

  5. 我們後來將 transport_errTransportErr 分別重新命名為 framework_errFrameworkErr。詳情請參閱 https://fxbug.dev/42061151。