RFC-0034:空的终止字符串

RFC-0034:Null 终止字符串
状态已拒绝
领域
  • FIDL
说明

我们提议 FIDL 中的字符串以 null 结尾(一如既往,保留字符串的大小)。此外,将 C 绑定更改为针对字符串数据使用 uint8_t*

作者
提交日期(年-月-日)2019-02-08
审核日期(年-月-日)2019-02-21

遭拒原因

摘要

我们建议:

  1. FIDL 中的字符串将以 null 终止(始终保持 字符串);

  2. 更改 C 绑定以将 uint8_t* 用于字符串数据。

设计初衷

当前的 FIDL 字符串编码很容易导致意外写入不安全的内容 代码。C 和低级别 C++ 绑定用于一些具有最高特权的 代码,因此应额外重视暴露风险 这些层。

我们不小心遇到了这样的情况 错误,但存在 对 Fuchsia 树中的代码进行系统性审核非常困难,不会阻止它们 出现在我们的代码树或第三方代码中。

设计

这提议更改 FIDL 字符串的编码,以包含单个 终止符字节,且值为零。这不会使 FIDL 字符串与 C 字符串,但 FIDL 字符串用作 C 字符串,系统会将其解释为 比预期短要比预期长,这更安全。

电汇格式

新的 的定义 线路编码的字符串如下(突出显示的更改):

  • 表示文本的可变长度的 UTF-8 编码字符序列。
  • 可为 null;null 字符串和空字符串不同。
  • 可以指定大小上限,例如string:40,最多 40 个字节的字符串 (不包括 null 终止符)。
  • 字符串内容含有 null 终止符。
  • 编码器和解码器必须验证最后一个 字符串的字节,如长度指示。
  • 存储为一条 16 字节的记录,其中包括: <ph type="x-smartling-placeholder">
      </ph>
    • size:64 位无符号代码单元(字节),不包括 null 终止符
    • data:64 位存在状态指示或指向外行字符串数据的指针
  • 编码以进行传输时,data 表示内容存在: <ph type="x-smartling-placeholder">
      </ph>
    • 0:字符串为 null
    • UINTPTR_MAX:字符串为非 null,数据是下一个外行对象
  • 在解码以供使用时,data 是一个指向内容的指针: <ph type="x-smartling-placeholder">
      </ph>
    • 0:字符串为 null
    • <valid pointer>:字符串为非 null,data 在指示的内存中 地址

字符串的表示方式如下:

  • string:不可为 null 的字符串(如果 null 数据为 null,会发生验证错误) )
  • string?:可为 null 的字符串
  • string:Nstring:N?:最大长度为 N 个代码单元的字符串

这将构成重大的传输格式更改,尤其是 长度可被 8 整除,则长度为 8 个字节(将对齐到 8 个字节,为 以 null 终止,导致额外添加 7 个字节作为填充,以与 8 对齐 )。

编码器需要更新,以便为经过编码的字符串添加 null 终止,并且 以验证字符串内容中是否有 null 字符。解码器 需要进行更新,以检查字符串中是否有 null 字符 但存在长度指示的 null 终止符。

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/hlcpp/string.cc
  • //garnet/public/lib/fidl/rust/fidl/src/encoding.rs
  • //third_party/go/src/syscall/zx/fidl/encoding.go(更新 marshalStringunmarshalString)
  • //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 不兼容。

性能

这对 build 性能没有影响,但会发生以下不严重的问题 效果影响:

  • 每个字符串平均需要一个额外的字节进行传输

安全

这项变更专门用于修复 使用 FIDL 编写内存不安全语言的代码。

测试

您将使用兼容性测试套件对此进行测试。该套件应 进行了扩展,以确保处理长度为 7 字节、8 字节和 9 字节的字符串等边缘情况 正确。

缺点、替代方案和未知之处

这会使 FIDL 消息的大小平均增加每个字符串一个字节。 对于长度不能被 8 整除的字符串,没有变化。对于 长度可被 8 整除的字符串将增加 8 个字节。

替代方案

取消应用启动

我们可以保持一切原样,并依靠代码审核和文档来 确保用户不会编写错误的代码该问题也会在一定程度上得到缓解 因为采用了新的低级别 C++ 绑定。迁移过程中 这种细微问题导致的、非常严重的 bug, 实际应用情况

Null 终止,但禁止嵌入的 null 字节 / 使用修改后的 UTF-8

(这是原始建议)

'\0' 是有效的 UTF-8 字符,存在于 FIDL 用户的 UTF-8 字符串中 广告资源禁止使用 null 字节会导致 FIDL 不适合多种用途 而使用经过修改的 UTF-8 将产生额外的开销, 从可能为 null 字节的 UTF-8 转换为修改后的 UTF-8。

只需使用 uint8_t,而不是 char

如果 FIDL 字符串数据指针为 uint8_t 而不是 char,则它们无法 无需类型转换即可传递给标准字符串函数或 printf。这会 有助于揭示这类错误,但不能阻止它们

在调试 build 中使用有效的 ASCII 填充内边距

8 个字符串中有 7 个后跟零填充字节。调试中 我们可以将这些零更改为有效的 ASCII 字符, 输出或解析后的错误这些东西很容易掉下来 穿越裂缝

始终将字符串移出一行

C/C++ 绑定可以为字符串和 null 终止符分配空间, 复制和终止收到的所有字符串。这会对 C 和低级别 C++ 绑定的性能损失是不可接受的。

将字符串移出 ASan build

地址排错程序将检查代码是否不会溢出堆分配。对于 ASan 构建,我们可以为堆上的字符串分配空间,复制字节 并将该指针返回给调用方。集成起来并非易事 绑定至 C 绑定,可能意味着在行为方面存在隐藏 bug 的显著差异 发布 build,并且不会帮助开发者 Fuchsia 构建系统。

停止使用 C 和 C++

如果 Google 在 2019 年无法编写安全的 C++ 代码,那么 C++ 就不安全了。很遗憾 C 和 C++ 是许多设备驱动程序作者的首选语言, 供应商可以选择将现有 C/C++ 驱动程序移植到我们的平台。

不要向 C 公开原始字符

我们可以向 C 公开一个不透明结构,并需要访问 C 中的任何字符串 从已解码的消息缓冲区中复制字符串。

早期艺术作品和参考资料

DBusCapt'n Proto 和 CORBA1 发送长度和 null 终止,protobuf 终止。


  1. CORBA 规范 3.3、 第 2 部分, 第 9.3.2.7 节。