RFC-0034:空值終止字串

RFC-0034:空值終止字串
狀態已遭拒
區域
  • FIDL
說明

我們提議終止 FIDL 中的字串 (無論如何都保留字串的大小,但做法與現況一致)。此外,請將 C 繫結變更為使用 uint8_t* 處理字串資料

作者
提交日期 (年/月)2019-02-08
審查日期 (年/月)2019-02-21

拒絕原因

摘要

我們建議:

  1. FIDL 中的字串將終止為空值 (但仍保留字串大小,但方式與本例相同);

  2. 變更 C 繫結,以使用 uint8_t* 處理字串資料。

提振精神

目前的 FIDL 字串編碼可讓您輕鬆意外編寫不安全的程式碼。在 Fuchsia 系統中,某些權限層級最特殊的程式碼會使用 C 和低階 C++ 繫結,因此針對這些層級曝露的風險,應給予額外的權重。

我們不小心遇到了這類錯誤,但 Fuchsia 樹狀結構中的程式碼進行系統性稽核並不容易,也不會防止程式碼在樹狀結構或第三方程式碼中重複發生。

設計

這會變更 FIDL 字串的編碼,加入值為零的單一終止器位元組。這不會讓 FIDL 字串與 C 字串相容,但是將 FIDL 字串用做 C 字串後,系統會將該字串視為較短且而非預期,因此較為安全。

有線格式

線編碼字串的新定義會是 (醒目顯示的變更項目):

  • 代表文字的變數長度序列 (採用 UTF-8 編碼字元)。
  • 可為空值;空值字串和空白字串不同。
  • 可指定大小上限,舉例來說,您可以使用 string:40 取代 40 個位元組字串 (不含空值結束器)。
  • 字串內容具有空值結束器。
  • 編碼器和解碼器必須驗證字串的最後一個位元組 (以長度表示) 後存在空值位元組。
  • 會儲存為 16 位元組記錄,其中包含:
    • size:64 位元無正負號的程式碼單位 (位元組),不含空值結束器
    • data:64 位元狀態指標或指向外行字串資料的指標
  • 編碼以進行傳輸時,data 表示內容存在:
    • 0:字串為空值
    • UINTPTR_MAX:字串不是空值,資料是下一個外部物件
  • 解碼以供使用時,data 是內容的指標:
    • 0:字串為空值
    • <valid pointer>:字串不是空值,data 位於指定的記憶體位址中

字串表示如下:

  • string:不可為空值的字串 (出現空值資料時,會發生驗證錯誤)
  • string?:可為空值字串
  • string:Nstring:N?:長度上限為 N 個代碼單位的字串

這將導致破壞性傳輸格式變更,明確地說明長度為 8 整除的字串長度將延長 8 個位元組 (將對齊 8 個位元組並終止,導致另一個 7 個位元組再次新增為對齊 8 的邊框間距)。

編碼器必須更新,在編碼的字串中加入空值終止,並驗證字串內容內沒有空值字元。您必須更新解碼器,確認字串內容中沒有任何空值字元,但長度代表存在空值結束字元。

C 繫結

目前 C 繫結會以 char*size_t 的形式代表字串。如果將 char* 傳遞至預期 C 字串的函式,可能會以錯誤的方式解譯。繫結將變更為使用 uint8_t*,因此將字串資料指標傳遞至 strchr()printf("%s") 會導致無法編譯。

導入策略

這是一項破壞性的有線格式變更。就所有 FIDL 的使用方式而言,部署作業必須審慎協調。

您必須在某些建構時間旗標後方更新下列程式碼:

  • //zircon/system/ulib/fidl/walker.h (以便正確驗證字串)
  • //zircon/system/host/fidl/lib/flat_ast.cpp (更新 StringType::Shape)
  • //zircon/system/host/fidl/lib/c_generator.cpp (更新 EmitLinerarizeMessageProduceInterfaceClientImplementation 等)
  • //sdk/lib/fidl/cpp/string.cc
  • //garnet/public/lib/fidl/rust/fidl/src/encoding.rs
  • //third_party/go/src/syscall/zx/fidl/encoding.go (更新 marshalString, unmarshalString)
  • //third_party/go/src/syscall/zx/fidl/encoding_new.go (更新 mString)
  • //sdk/dart/fidl/lib/src/types.dart (更新 StringType)
  • llcpp 繫結
  • 其他語言的樹狀結構外繫結

您應使用 fidl_compatibility_test 進行測試,執行測試,並確認預期的系統穩定性。

事實上,變換變更需要與發布團隊和 Chromium 等外部團隊協調。

人體工學

這可讓 C 和低層級 C++ 繫結更容易使用,也不會影響其他繫結。

說明文件與範例

請務必更新傳輸格式說明文件 (如上所述)。

回溯相容性

這項變更與 API 相容,但與 ABI 不相容。

效能

這不會影響建構效能,但會有下列輕微影響:

  • 每個字串平均需要額外位元組

安全性

這項變更專門用於使用 FIDL 修正記憶體不安全語言潛在的安全漏洞。

測試

系統會使用相容性測試套件進行測試。該套件應擴充,確保能正確處理 7、8 和 9 個位元組字串等極端案件。

缺點、替代方案和未知

這會讓 FIDL 訊息大小平均增加每個字串一個位元組。 對於長度不能以 8 整除的字串不會有任何變化。如果字串的長度可以被 8 整除,長度會增加 8 個位元組。

替代選項

什麼也不做

我們可以保留所有內容,並依賴程式碼審查和說明文件,確保使用者不會編寫錯誤的程式碼。採用新的低層級 C++ 繫結也能減輕這個問題的影響。這種細微問題造成的錯誤類別潛在的危險,過於嚴肅,不要故意留下來。

空值會終止,但禁止內嵌的空值位元組 / 使用已修改的 UTF-8

(這是原始提案)

'\0' 是有效的 UTF-8 字元,存在於 FIDL 使用者想交換的 UTF-8 字串中。將空值設為禁用,會導致 FIDL 對許多用途不適當,而使用修改後的 UTF-8 會產生額外的負擔,將一般的 UTF-8 轉換為可能為空值的位元組,以修改 UTF-8。

只使用 uint8_t 而非 char

如果 FIDL 字串資料指標是 uint8_t,而不是 char,就無法在不轉換的情況下傳遞至標準字串函式或 printf。這有助於找出這類錯誤,但無法防止問題發生。

在偵錯版本中用有效 ASCII 填入邊框間距

在 8 個字串中,有 7 個後面接著有零填充位元組。在偵錯版本中,我們可以將這些零變更為有效的 ASCII 字元,以便在列印或剖析錯誤時顯示這些錯誤。很容易弄丟這些裂縫

一律將字串移出該行

C/C++ 繫結可以分配字串和空值結束字元的空間,並複製及終止收到的所有字串。這會導致 C 和低階 C++ 繫結產生無法接受的效能成本。

針對 ASan 建構將字串移出行

位址掃毒程式會檢查程式碼不會超過堆積分配量。針對 ASan 建構,我們可以為堆積上的字串分配空間,請複製其中的位元組,然後將該指標傳回呼叫端。要整合到 C 繫結並不容易,這可能意味著 ASan 和發布子版本在行為方面出現了重大錯誤,而且無法協助開發人員在 Fuuchsia 建構系統以外工作。

停止使用 C 和 C++

如果 Google 無法在 2019 年編寫安全 C++,就會導致 C++ 不安全。但很遺憾地,C 和 C++ 是許多裝置驅動程式庫作者的首選語言,許多廠商可能會選擇將現有的 C/C++ 驅動程式移植到我們的平台。

不要向 C 公開原始字元

我們可以向 C 公開不透明的結構,並需要在 C 中存取任何字串,才能從已解碼的訊息緩衝區中複製字串。

既有圖片與參考資料

DBusCaptn Proto 和 CORBA1 傳送長度和空值終止,protobuf 不會終止。


  1. CORBA 規格 3.3,第 2 部分,第 9.3.2.7 節。