RFC-0137:舍弃 FIDL 中的未知数据 | |
---|---|
状态 | 已接受 |
领域 |
|
说明 | 使 FIDL 绑定舍弃未知数据,而不是保留和代理数据。 |
问题 | |
Gerrit 更改 | |
作者 | |
审核人 | |
提交日期(年-月-日) | 2021-08-25 |
审核日期(年-月-日) | 2021-10-13 |
摘要
大多数 FIDL 绑定都会保留未知的表字段和联合变体,从而允许 用户代码来检查并重新编码原始字节和句柄。此行为 会给 FIDL 带来极大的复杂性, 传输格式迁移很困难,并且无法在所有绑定中实现。周三 建议改用绑定舍弃未知数据, 如表 1 所示。
表 1:flexible类型(包含未知数据)的更改
类型 | 是否可以访问未知内容? | 可以重新编码吗? | 代理未知? |
---|---|---|---|
位 | 是 | 是 | 是 |
枚举 | 是 | 是 | 是 |
表 | 是 → 否 | 是 | 是 → 否 |
并集 | 是 → 仅限序数 | 是 → 否 | 是 → 否 |
背景
灵活类型是 FIDL 中用于编写可发展 API 的一项重要功能。 已在 RFC-0033:处理未知字段和严格程度中引入, 自 2020 年底以来,所有绑定都已引入这两个属性。借助灵活的类型 即使有未知成员,解码也会成功。FIDL 表始终 灵活,而位、枚举和联合可以标记为严格或灵活。 对于灵活的位和枚举,未知值只是一个整数。不过, 对于未知表字段或联合变体,该值由原始字节组成 和句柄(我们称之为“未知数据”)。
目前,大多数绑定会在网域对象中保留未知数据。例外情况 是 LLCPP,其设计限制使得这一点很难支持。对于 而保留未知数据会产生以下行为。假设 进程 A、B 和 C 通过 FIDL 进行通信。如果 A 和 C 知道一个新表 而 B 则不如此,而该字段会从 A 发送到 B 再发送到 C,则 C 将 尽管 B 对它一无所知,但也能接收并理解它。也就是说, 代理未知数据。应用还可以解读未知数据 例如,前四个字节为 始终是一个标识符。然而,此类情况是人为的,并且能够更好地根据模型估算 FIDL 类型。代理是唯一现实可行的应用场景, 未知数据。
设计初衷
在设计 FIDL 时,我们致力于解决实际问题,使用 这些功能。保留未知数据的功能 这些原则。自从实施以来,它在 紫红色的购买行为,一再出现复杂的问题 其他 FIDL 工作的考虑因素。
这就引出了我们质疑代理未知数据的优点。好主意吗 该怎么办呢?我们认为这不是,至少不是 行为对于 也就是“代理”然而,针对这些情况, FIDL 中提供了专门的代理支持, 而不仅仅是未知数据。
即使我们假设默认情况下需要代理,
直接对 FIDL 域对象进行重新编码。不过,这很常见(并且
建议)将这些对象转换为内容更丰富、特定于应用的类型
然后才能进行进一步处理。这种做法与
代理的情况下,可按原样传递已编码的消息,
只需极少的处理工作,即可直接对已解码的消息进行重新编码。例如,
Rust crate fidl_table_validation
在转换
将 FIDL 域对象转换为应用域对象。因此,发送
复杂系统中跨多个跃点的表不能依赖于所有字段
如有任何参与者使用此模式,也就不会到达最终目的地。
无论是否需要代理,保留未知数据都有几个问题 缺点这会增加传输格式的迁移难度。在迁移过程中 这时所有同行都可以阅读新旧格式,这意味着 就可以放心地开始编写新格式了由于这种更改 因此不可避免地会遇到这样的情况 同时接收新格式和新格式的消息。假设它收到 这两个表都具有未知的表字段,然后尝试在 单条消息在这种情况下,保留未知数据的唯一方法是 有线格式元数据,但这会增加不可接受的 降低复杂性和开销
另一个缺点是 FIDL 绑定之间的功能对等性。在 支持就地解码(例如 LLCPP),很难选择 可以拥有句柄和表示未知句柄的对象表示法。 对于已知数据,解码器通过覆盖来在域对象中插入句柄 其在线状态指示标志。对于未知数据,解码器只知道 要跳过的标识名,而不是其存在的位置 指标。因此不可能返回短于 同时返回域对象和重新打包的句柄表。相反,这些 绑定直接不支持保留未知数据。这可能会导致 这会使在其他绑定中依赖它的用户感到惊讶, 需要进行两次 GIDL 测试,以测试所有涉及未知数据的情况。
一般来说,由于需要保留未知数据, FIDL。这种复杂性并不仅限于实现,还会影响到用户, 因为它会与其他特征交互。例如,区别在于 旨在仅影响 API 兼容性, 而非 ABI。不过,后来发现它不可避免地 收到灵活值的未知句柄时对 ABI 的影响 类型。这种极端情况的存在只是由于需要保留 网域对象中包含未知数据。
利益相关方
谁对是否接受此 RFC 有影响?(此部分为可选内容, encouraged.)
教员:pascallouis@google.com
审核者:abarth@google.com、yifeit@google.com、ianloic@google.com
已咨询:bryanhenry@google.com
社交化:此 RFC 的草稿已发送给 FIDL 团队以供评论。
设计
灵活位和枚举的未知值的处理方式保持不变。
解码表和灵活联合时:
绑定不得在网域对象中存储未知字节和句柄,除非 这些绑定是专为代理设计的
绑定必须关闭所有未知句柄。
对之前解码的表和灵活联合进行重新编码时:
绑定必须成功对表的已知字段进行重新编码,且不得 包含未知字段(这意味着存储它们)。
使用 未知变体。
关于表和灵活联合的域对象:
绑定不应提供任何机制来区分不含 表中舍弃了未知字段的未知字段。它们应该是 如果绑定提供深度相等函数,则视为相等。
绑定必须提供一种机制来确定灵活联合是否具有 未知变体,并且应提供对未知序数(即 域对象的未知变体应仅存储序数)。未知 如果绑定提供深度等式函数,则变体的行为应该类似于 NaN, 比较不等于,即使序数相同也是如此。
在 Rust 中,后一点意味着从灵活联合中移除 Eq
特征
和包含传递变量的类型和类型,就像对浮点数所做的那样。
实现
该实现主要是删除负责 保留所有绑定中的未知项。我们认为没有任何 未知数据访问器的生产环境使用。如果确实存在,我们不得不 了解应用场景并尝试找到前进方式。
目前,LLPP 无法对包含未知字段的表进行重新编码。这将 需要根据设计进行更改,以便成功仅对已知字段进行编码。
安全注意事项
此方案可提高安全性,因为它可以减少信息和 功能是隐式传递的。保留未知数据时, 很容易通过不可疑的组件传递任意字节和句柄。 被舍弃后,FIDL 便会对数据边界进行准确编码 使系统更易于审核。
隐私注意事项
此提案可以增强隐私保护,因为它限制了未知 其中可能包括敏感信息。
测试
测试主要在 GIDL 中进行。涉及未知数据的 success
测试
可拆分为两部分:decode_success
和 encode_success
(编码
只有已知的表字段)或 encode_failure
(联合无法编码)。通过
包含未知数据的值的表示法也会发生变化。GIDL 不应
更长的解析未知字节和句柄,因此改为使用语法 123:
unknown
指示序号 123 处的未知信包。
可以移除拆分 LLCPP 和非 LLCPP 的许可名单和拒绝名单。全部 对于未知数据,绑定将具有相同的编码/解码行为。 数据。此外,fxrev.dev/428410 中添加的 LLCPP 专用单元测试可以 取而代之的是 GIDL 测试。
测试是否采用了严格/灵活和价值/资源的所有组合 虽然使用灵活值类型的句柄对未知数据进行解码,但仍保留 就不会再失败
文档
以下文档需要更新:
缺点、替代方案和未知问题
替代方案:视需要保留未知项
我们不是完全取消对保留未知数据的支持, 继续为其提供支持,而不仅仅是默认支持。例如,可以是 类型上有一个属性,可能限于值类型来缓解 关于代理未知句柄的问题不过,这种方法使得 将复杂性降低为很少使用的功能,并且无法解决电线格式 迁移问题
缺点:灵活的类型不一致
这种方案的缺点是,它会减少灵活类型的行为, 保持一致,可能不太直观。解释这一点有助于将分类 沿两个轴的位、枚举、表和联合,如表 2 所示: 代数类型(乘积或总和)和载荷(带有或不带载荷)。
表 2:灵活类型的分类
产品类型 | 总和类型 | |
---|---|---|
无载荷 | 位 | 枚举 |
带载荷 | 表 | 并集 |
目前,所有灵活类型都会代理未知信息。此提案将破坏 沿两个轴的对称性例如,请考虑以下 FIDL 类型:
// Product types (multiple fields set)
type Bits = bits { A = 1; B = 2; };
type Table = table { 1: a struct{}; 2: b struct{}; };
// Sum types (one variant selected)
type Enum = enum { A = 1; B = 2; };
type Union = union { 1: a struct{}; 2: b struct{}; };
首先,我们失去了各个有效负载轴的一致性。目前,出发地:Bits
Table
或从 Enum
增加到 Union
增加功能,允许
以承载有效负载在此方案中,该功能附带
不再保留未知的成本。
其次,我们失去了整个代数类型轴的一致性。目前,
Table
和 Union
允许在对数据未知的对象进行解码后重新编码。
在此提案中,Table
可以重新编码,但 Union
无法重新编码。
我们认为,这种在实用主义与一致性之间进行权衡是值得的, 动机中所述的复杂性。不过, 以保持更一致的风格。
替代方案:舍弃所有未知信息
为了提高一致性,我们可以舍弃所有未知信息,即使是未知信息 以及易于存储的整数也就是说,如果一个集群中存在单个未知状态, 位和枚举,以及舍弃载荷以及未知序数 代表联合体。表 3 显示了产生的行为。
表 3:表 1 的调整:舍弃所有未知信息
类型 | 是否可以访问未知内容? | 可以重新编码吗? | 代理未知? |
---|---|---|---|
位 | 是 → 否 | 是 | 是 → 否 |
枚举 | 是 → 否 | 是 → 否 | 是 → 否 |
表 | 是 → 否 | 是 | 是 → 否 |
并集 | 是 → 否 | 是 → 否 | 是 → 否 |
替代方案:可选的灵活联合
为了提高一致性,我们可以要求灵活联合始终是 可选,然后将未知变体解码为缺失并集。这样, 可以对联合进行重新编码,使其与表保持一致。表格 4 显示了生成的行为。
表 4:表 1 的调整:可选的灵活联合
类型 | 是否可以访问未知内容? | 可以重新编码吗? | 代理未知? |
---|---|---|---|
位 | 是 | 是 | 是 |
枚举 | 是 | 是 | 是 |
表 | 是 → 否 | 是 | 是 → 否 |
并集 | 是 → 否 | 是 | 是 → 否 |
替代方案:记住未知字段是否被舍弃
在所提议的设计中,绑定用户无法判断是否未知
字段在解码表时被舍弃。另一种方法是
布尔值或一组未知序数的值。用户可以
然后通过 has_unknown_fields()
等函数进行查询。例如,
在这种情况下,存储服务可能会失败,以避免数据丢失。
这种替代方案的缺点是,它会向表添加额外的隐藏状态
域对象。它们不再是简单的值类型,也就是各个字段的总和。
例如,这会引发一个问题,即 ==
运算符是否应将
这样的布尔选项
替代方案:重要字段
检查 had_unknown_fields()
的唯一实际用例,如所述
如果返回 true,则会失败。而非
在绑定中提供该访问器后,我们可以接受表中的属性,
灵活的联合成员选择接受该行为:
type Data = table {
1: foo string;
@important
2: bar string;
};
此属性的作用是,在
信封标头。当解码器遇到未知字段时,
重要位设置,就必须失败。换句话说,@important
属性会停用向前兼容性,像动态版本一样运作
静态 strict
修饰符,用于位、枚举和联合。
此替代方案可能需要有自己的 RFC,而非此替代方案。
确认:此创意源自 yifeit@google.com。
替代方案:保留值/资源对 ABI 的影响
此方案消除了 RFC-0057 对 ABI 的影响,并且 认为可以通过舍弃未知数据进行改进。 不过,也有人认为 ABI 影响是可取的,应予以保留。
降低 ABI 影响的优势(此方案):
- 它使特征更加严格/灵活,价值/资源则更加独立。那里 它们的交集不再是特殊情况。鉴于 直到撰写各自的 RFC 很长时间才会注意到这种情况, 让用户感到意外
- 您可以更轻松地将类型从值转换为资源,因为它仅 会破坏 API,而不是 ABI。在没有代码中断时(请求和 响应类型(无法在某些绑定中直接引用),此 转换不再以静默方式更改行为。
保持 ABI 影响的优势(此替代方案):
- 它能更准确地对界面的意图进行建模。如果您表明 您不需要处理句柄(通过使用值类型),并且会在以下位置收到句柄: 这表明存在差距,失败是没有问题的。
- 如果我们改变了主意,以后可以减少对 ABI 的影响。切换到另一个 更有可能造成中断。
先验技术和参考资料
协议缓冲区
协议缓冲区的设计 点。在 proto2 中,系统会保留和代理未知字段 (例如如今的 FIDL)。在 proto3 中,行为已更改为舍弃未知字段 (如本方案所述)。不过,后来又改回了这一决定 因此在 3.5 版及更高版本的 proto3 中,proto3 同样保留了未知字段。
这就引出了一个问题:如果我们接受 FIDL, 提案?我们认为答案是否定的,因为 FIDL 和 Protobuf 分别占用了 不同设计空间。Protobuf 必须还原到旧的保留行为 因为它有两个使用场景:中间服务器和读取-修改-写入模式。 这两种模式在紫红色不太普遍。不使用中间代理 Fuchsia 的安全和隐私原则鼓励 通信。FIDL API 评分准则不同于读取-修改-写入模式, 建议采用部分更新模式。
Thrift
Apache Thrift 舍弃未知字段。