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

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

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

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

摘要

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

设计初衷

此 RFC 的设计初衷是放宽目前规定绑定必须在编码期间进行验证的设计限制。这并不意味着绑定必须更改其行为,只是它们受到的限制更少。事实上,在大多数情况下,它们不太可能因本 RFC 而发生更改。

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

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

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

利益相关方

教员: pascallouis

审核人: pascallouis、yifeit、mkember

咨询对象

azaslavsky

共同化

此 RFC 已在 FEC 讨论邮件列表中分发。

设计

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

  • 绑定必须确保在编码后将填充字节归零。 不过,编码器本身不需要将填充归零,例如,可以通过编程语言的保证将其归零。

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

实现

此 RFC 没有直接的可交付内容,绑定目前会验证 FIDL 对象,并且可能会继续这样做。不过,将来绑定将能够放宽验证检查。

性能

性能影响高度依赖于用例和绑定。以下是一些验证对性能的影响示例:

  • 在 Rust 中,包含 15 个 uint8 和 1 个布尔值的结构体的编码速度比不进行布尔值验证时慢 3.2 倍 (52ns)。如果结构体包含 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 倍 (400ns),而对消息标头进行编码时,编码速度会慢 1.3 倍 (37ns)。

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

用于这些基准的 CL:

工效学设计

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

向后兼容性

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

安全注意事项

填充字节在历史上一直被归零而不是验证,并且将继续被归零。这一点很重要,因为 FIDL 对象可以分配在旧分配之上,如果内存被复制到网络上,则会导致泄漏。对于大多数其他类型的验证,泄漏的风险不太显著。

正在执行的验证主要有两类。

值限制

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

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

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

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

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

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

州无效

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

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

  • 向量不存在但计数不为零 - 这在 编码期间绝不应发生。

如果这些问题在到达解码器之前未被捕获,则不会产生显著的负面影响。它们也往往是 FIDL 实现中的内部问题,可以通过其他方式保证正确性。

总结

虽然存在需要考虑的安全问题,但由于信息泄漏的风险较低,因此通常可以在解码端而不是编码端捕获这些问题。

隐私注意事项

对隐私没有影响。

测试

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

文档

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

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

限制越多,灵活性就越低,反之亦然。 对于验证,限制越多,据称安全性就越高,而灵活性则可以提高性能并减小代码大小。此 RFC 认为,安全性优势并不那么显著,FIDL 应倾向于移除此要求。

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

在先技术和参考文档