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

RFC-0149:FIDL 编码验证不是必需的
状态已接受
领域
  • FIDL
说明

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

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

总结

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

设计初衷

我们制定此 RFC 的目的在于放宽目前要求绑定必须在编码过程中进行验证的设计限制。这并不意味着绑定一定会改变其行为,只表示它们受到的限制更少。事实上,在大多数情况下,它们不太可能因此 RFC 而发生更改。

也就是说,在编码期间不再强制执行验证有以下几个原因:

  • 编码期间的性能,验证会产生额外的开销。例如,结构体编码有一条快速路径,可以缩减为 memcpy,如果结构体中有需要验证的枚举字段,则在许多绑定中都无法使用此路径。对 HLCPP 绑定来说,性能影响尤为显著,这些绑定会对已编码的对象进行两次编码,一次编码,一次进行验证(但重新设计绑定可以避免这种情况)。

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

利益相关方

教员:pascallouis

审核者:pascallouis、yifeit、mkember

咨询人员

阿萨斯拉夫斯基

社交

此 RFC 已在 FEC 讨论邮寄名单中分发。

设计

  • 绑定可以也可能不会在编码期间验证 FIDL 对象。 更确切地说,不再要求对象的编码方式可确保对象在解码过程中始终通过验证。

  • 绑定必须确保填充字节在编码后为零。 不过,编码器本身并不需要将内边距设为零,例如,可以通过编程语言保证将内边距设为零。

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

实现

此 RFC 没有可立即交付项 - 绑定目前会验证 FIDL 对象,并且可能会继续这样做。但是,将来的绑定可以放宽验证检查。

性能

性能影响高度特定于用例和绑定。下面列举了一些示例来说明验证对性能的影响:

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

  • 包含 256 个枚举的数组的编码速度在 LLCPP 中速度是慢 2.2 倍 (2.6us),在 Rust 中速度是慢 1.2 倍 (192ns),在进行枚举验证时速度是 Go 速度的 9 倍 (1.1us)。

  • 通过验证后,HLCPP 对象的编码速度会慢 1-5 倍。包含 16 个元素的表的编码速度减慢了 1.7 倍(400ns),而消息标头的编码速度则慢了 1.3 倍(37ns)。

这些测量值来自配备 Intel Core i5-7300U CPU @ 2.60GHz 的机器。 请注意,它们属于微基准,实际中的性能可能会有所不同。

用于以下基准的 CL:

工效学设计

应该不会对绑定工效学设计产生任何影响。但是,对于某些可能影响用户的失败模式,在编码期间停用验证的绑定不会再“快速失败”。

向后兼容性

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

安全注意事项

过去,填充字节数为零,而非经过验证,并将继续归零。这一点很重要,因为 FIDL 对象可能会在旧分配之上进行分配,从而导致在将内存复制到连接线上时导致泄露。对于大多数其他验证类型,泄露的风险不太重要。

系统主要执行两类验证。

价值限制

  • Bool、Enum、位 - 验证可确保支持这些类型的整数在预期范围内。

  • 浮点数 - 浮点数验证目前不在规范范围内,但绑定可能仍会执行一些验证,特别是防止出现 NaN 值。

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

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

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

进行传输时,始终需要遵循特定的大小限制,例如信道传输的 64k 消息大小限制可防止消息大小无限增大。因此,在到达解码器之前,问题通常不会立即得到解决。

州无效

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

  • 信封内嵌位 - 信封具有指示是否应以内嵌方式存储数据的字段。可能会有大小 <= 4 个字节的信封应以内嵌方式存储,但缺少内嵌位标记。

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

如果在到达解码器之前未捕获到这些信号,则它们不会产生明显的不良后果。它们往往也是 FIDL 实现中的内部问题,可以通过其他方式保证正确无误。

总结

虽然存在需要考虑的安全问题,但通常可以在解码端(而不是编码端)捕获到它们,因为它们的信息泄露风险很低。

隐私注意事项

这不会对隐私权造成任何影响。

测试

由于此 RFC,测试要求通常会降低。

文档

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

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

在更多限制和更高的灵活性之间进行自然的权衡。在验证中,更多的限制会带来按比例提升的安全优势,而灵活性则可以提升性能和代码大小。该 RFC 认为,安全优势不太明显,因此 FIDL 应建议移除这项要求。

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

早期技术和参考资料