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 倍(52 纳秒)。如果结构包含 254 个 uint8 和 1 个布尔值,则速度会慢 21 倍 (844ns)。不过,在 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 纳秒)。

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

用于这些基准的 CL:

工效学设计

不应影响绑定的人体工学。 不过,在编码期间停用验证的绑定将不再针对某些可能会影响用户的故障模式“快速失败”。

向后兼容性

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

安全注意事项

填充字节过去一直被置零,而不是经过验证,并且未来也会继续被置零。这一点很重要,因为 FIDL 对象可以分配在旧分配之上,如果内存被复制到网络上,则会导致泄漏。对于大多数其他类型的验证,泄露风险并不那么严重。

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

值限制

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

  • 浮点数 - 浮点数验证目前不属于规范的一部分,但绑定可能仍会执行一些验证,尤其是为了防止出现 NaN 值。

  • UTF-8 - FIDL 字符串是有效负载中包含 UTF-8 数据的向量。 验证可确保它们是 UTF-8 格式。

这些类型的字段通常由用户通过绑定 API 进行分配。验证可确保用户通过 API 提供有效输入,并且没有其他形式的意外 bug 更改了这些值,例如内存损坏。请注意,内存损坏 bug 可能会在处理对象的任何阶段发生,并且期望通过(例如)布尔值验证来捕获它在某种程度上是任意的。

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

对于传输,始终必须遵循某些大小限制,例如渠道传输上的 64k 消息大小限制,以防止消息大小无限增大。因此,在到达解码器之前,这通常不是需要立即解决的问题。

州无效

  • 将非可选类型视为可选类型 - 实际上是一种缺少载荷的非可选类型。

  • 信封内嵌位 - 信封具有用于指示数据是否应内嵌存储的字段。可能会出现这种情况:信封的大小 <= 4 字节,应内联存储,但缺少内联位标记。

  • 矢量缺失但数量不为零 - 这在编码期间绝不应发生。

如果这些问题在到达解码器之前未被发现,则不会造成严重的负面后果。它们往往是 FIDL 实现中的内部问题,可以通过其他方式保证其正确性。

总结

虽然需要考虑安全问题,但由于信息泄露的风险较低,这些问题通常可以在解码端而非编码端被发现。

隐私注意事项

不会影响隐私权。

测试

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

文档

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

缺点、替代方案和未知因素

限制越多,灵活性就越低,这是自然而然的。对于验证,更多限制会带来预期的安全优势,而灵活性则可以改进性能和代码大小。此 RFC 认为,安全优势并不那么显著,FIDL 应倾向于移除此要求。

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

在先技术和参考资料