RFC-0149:非强制性 FIDL 编码验证

RFC-0149:FIDL 编码验证不是强制性的
状态已接受
区域
  • FIDL
说明

不再需要在 FIDL 编码期间进行验证。不过,解码期间仍会进行验证,并且在编码期间必须将填充值设为零。

Gerrit 更改
作者
审核人
提交日期(年-月-日)2021-11-19
审核日期(年-月-日)2022-01-22

摘要

不再需要在 FIDL 编码期间进行验证。不过,解码期间仍会进行验证,并且编码期间必须将填充值设为零。

设计初衷

制定此 RFC 的目的是放宽目前要求绑定必须在编码期间进行验证的设计约束条件。这并不意味着绑定一定会更改其行为,而只是表明它们受到的约束更少。事实上,在大多数情况下,这些值不太可能因此 RFC 而发生变化。

不过,不再强制要求在编码期间进行验证的原因有以下几点:

  • 编码期间的性能:验证会产生额外的开销。例如,有一个结构体编码的快速路径,可简化为 memcpy,但在许多绑定中,如果结构体中存在需要验证的枚举字段,则无法采用该路径。对于 HLCPP 绑定,性能影响尤为显著,因为它会对要编码的对象进行两次遍历 - 一次进行编码,一次进行验证(不过,可以通过重新设计绑定来避免这种情况)。

  • 代码大小:验证逻辑会增加代码大小。对于生成代码或使用宏的绑定,这种代码大小增加最为显著,因为验证逻辑会在输出中重复多次。

利益相关方

主持人:pascallouis

审核者:pascallouis、yifeit、mkember

咨询了

azaslavsky

社交

此 RFC 已通过 FEC 讨论邮寄名单分发。

设计

  • 绑定在编码期间可以选择是否验证 FIDL 对象。更确切地说,不再要求以保证对象在解码期间始终能通过验证的方式对其进行编码。

  • 绑定必须确保在编码后填充字节设为零。不过,编码器本身无需将填充设置为零 - 例如,它可以通过编程语言的保证将填充设置为零。

  • 绑定必须在解码期间验证 FIDL 对象。

实现

此 RFC 没有任何即时可交付成果 - 绑定目前会验证 FIDL 对象,并且可能会继续这样做。不过,未来绑定将能够放宽验证检查。

性能

性能影响在很大程度上取决于应用场景和绑定。下面是验证对性能产生的一些影响的示例:

  • 在 Rust 中,如果进行布尔值验证,则包含 15 个 uint8 和 1 个布尔值的结构体的编码速度会比不进行验证时慢 3.2 倍(52ns)。如果结构包含 254 个 uint8 和 1 个布尔值,则速度会慢 21 倍(844 纳秒)。不过,在 LLCPP 中,使用相同结构的性能开销微不足道。之所以选择此测试用例,是因为在许多绑定中,结构体、数组或矢量正文中的单个布尔值或枚举都会阻止 memcpy 优化。

  • 在有枚举验证的情况下,LLCPP 对 256 个枚举的数组进行编码的速度会变慢 2.2 倍(2.6us),Rust 会变慢 1.2 倍(192ns),Go 会变慢 9 倍(1.1us)。

  • 启用验证后,HLCPP 对象的编码速度会降低 1-5 倍。16 个元素的表编码速度会变慢 1.7 倍(400 纳秒),而消息标头的编码速度会变慢 1.3 倍(37 纳秒)。

这些测量结果来自搭载 2.60GHz Intel Core i5-7300U CPU 的机器。 请注意,这些是微基准测试,实际性能可能会有所不同。

用于这些基准测试的 CL:

工效学设计

对绑定工效学应该没有影响。 不过,对于可能会影响用户的某些失败模式,在编码期间停用验证的绑定将不再“快速失败”。

向后兼容性

这应该不会对向后兼容性产生影响。

安全注意事项

填充字节过去一直设为零,而不是经过验证,今后也将继续设为零。这一点很重要,因为 FIDL 对象可以在旧分配之上分配,如果将内存复制到线路上,就会导致内存泄露。对于大多数其他类型的验证,泄露风险较小。

系统会执行两类主要验证。

值限制

  • 布尔值、枚举、位 - 验证可确保这些类型的支持整数在预期范围内。

  • 浮点数 - 浮点数验证目前不在规范的范围内,但绑定可能仍会执行一些验证,尤其是为了防止 NaN 值。

  • UTF-8 - FIDL 字符串是载荷中包含 UTF-8 数据的矢量。验证可确保它们采用 UTF-8 编码。

这类字段通常由用户通过绑定 API 分配。验证可确保用户通过 API 提供的输入有效,并且没有任何其他形式的意外 bug 更改了值,例如内存损坏。请注意,内存损坏 bug 可能会在处理对象的任何阶段发生,因此希望通过布尔值验证等方法捕获它有些不切实际。

  • 大小限制 - 某些对象(例如表格)的大小存在限制。

传输层始终必须遵循某些大小限制,例如信道传输层上的 64k 消息大小限制,以防止消息大小无限膨胀。因此,通常不需要在到达解码器之前解决此问题。

州无效

  • 将非必需类型视为可选类型 - 本质上是缺少载荷的非必需类型。

  • 信封内嵌位 - 信封包含字段,用于指明是否应内嵌存储数据。封装容器的大小可能小于等于 4 字节,应内嵌存储,但缺少内嵌位标记。

  • 矢量不存在,但计数不为零 - 编码期间绝不应出现这种情况。

如果未在到达解码器之前捕获这些错误,它们都不会产生严重的负面影响。它们通常是 FIDL 实现中的内部问题,可以通过其他方式保证正确无误。

总结

虽然需要考虑一些安全问题,但这些问题通常可以在解码端(而非编码端)发现,因为它们的信息泄露风险较低。

隐私注意事项

对隐私没有影响。

测试

受此 RFC 影响,测试要求通常会减少。

文档

需要更新 FIDL 绑定规范才能解决此问题。

缺点、替代方案和未知情况

限制越多,灵活性就越低,这是自然而然的。在验证方面,更多限制会带来相应的安全优势,而灵活性则有助于提升性能和代码大小。此 RFC 认为,安全优势并不明显,FIDL 应支持移除此要求。

移除此要求后,绑定将可以选择保留或移除现有的编码端验证。在实践中,绑定最终应该会尽可能移除此验证,但某些绑定可能会继续在调试模式下进行验证,以便更早发现问题。

在先技术和参考文档