RFC-0033:未知字段处理和严格处理

RFC-0033:处理未知字段和严格程度
状态已接受
领域
  • FIDL
说明

此 FTP 修正并阐明了 FIDL 解码器在遇到类型未知的表、可扩展联合体、枚举和位(可扩展消息)时的行为。

作者
提交日期(年-月-日)2019-02-07
审核日期(年-月-日)2019-03-07

摘要

此 FTP 修正并阐明了 FIDL 解码器 遇到表格、可扩展联合体、枚举和位 可扩展消息[^1] - 包含 类型未知。

具体而言,我们建议:

  • 为可扩展消息定义严格且灵活的行为; 指定解码器如何遇到未知字段(包括句柄) 行为
  • 可为可扩展消息添加前缀的 strict 关键字 声明。 这可保证收到的邮件中不会包含未知字段 拒绝它们。
  • 默认为具有灵活性的可扩展消息,即 允许未知值并通过绑定公开;
  • 定义并推荐绑定提供给客户端的 API 检查包含未知字段的消息

与其他 RFC 的关系

此 RFC 已由以下人员修改:

设计初衷

可扩展消息是实现数据交换的重要机制 在不破坏线缆格式(二进制)兼容性的情况下不断改进。 不过,更改架构会为 FIDL 解码器制定设计决策, 因为在如何验证、解析这些字段以及如何向 最终用户

虽然每种语言的数据结构机制和规范不同 为解码器及其 API 指定行为有助于提高安全性 通过强制执行验证行为,并提升整体人体工学 以提高各种类型和语言之间的一致性。

我们还希望为受限环境启用绑定,其中 正确操作可能并不需要解析未知字段, 性能负担过重。 这同样适用于已经成熟且 预计不会进一步发展。

设计

未知字段是指其顺序(表)、标记(可扩展并集)、 值(枚举)或特定位(位)对读取器是未知的。 在这里,我们将使用“tag”来指代未知的 序数/标记/值/特定位(为简洁起见)。

  • 必须验证和解析包含未知标记的消息 成功。
    • 不过,请参阅下文,了解针对 strict 消息。
  • 解码器必须处理消息中的未知句柄。
    • 默认处理行为必须为关闭所有手柄。
    • 绑定可以提供一种可让客户端处理未知的机制 进行特殊处理。
  • 绑定必须提供一种机制,用于检测未知标记是否被 。
  • 绑定应提供一种机制来检测包含 指定的标记存在于收到的消息中。
  • 绑定可以提供一种机制来读取标记、原始数据和 未知字段中的(未类型化)句柄。
  • 如果目标语言提供了一种详尽检查标记的机制 (例如,C/C++ 中的 switch()、Rust 中的 match):

    • 该语言绑定应提供特殊的“未知”标记,标记, 可纳入详尽检查中,确保通用分析规则 (例如,C/C++ 中的 default、Rust 中的 _)可以省略。
    • 此建议的目的是 因为如果需要进行编译,则标记 不会引发编译器警告。
    • 此 FTP 没有定义上传方法 因为不同语言之间的实现策略可能有所不同。
    • 示例:

      // Bindings SHOULD NOT offer this API:
      switch(union.Which()) {
        case Tag1: ...
        case Tag2: ...
        case Tag3: ...
        default: ...
        // no unknown tag in bindings forces handling using default case
      }
      
      // Bindings SHOULD offer this API:
      switch(union.Which()) {
        case Tag1: ...
        case Tag2: ...
        case Tag3: ...
        case Tag_Unknown: ...
        // no default case: new tags cause a non-exhaustiveness warning
      }
      

严格处理消息

  • 我们引入了一个可以为可扩展消息添加前缀的 strict 关键字 声明,例如strict table T { ... }strict enum T { ... }
  • 包含未知字段的严格消息必须被视为无效。
  • 绑定不得提供特殊的“未知”参数,标记用于详尽标记 检查严格的消息(如果它们支持这种灵活的机制) 消息。
  • 将严格消息转换为灵活消息,反之亦然。 必须作为非破坏性源代码级 (API) 更改加以支持,可能的话 使用 [Transitional] 属性过渡到软过渡。
    • 这种转换不得更改传输格式 (ABI)。
  • 严格消息具有传递性。 如果某封邮件被标记为“严格”,则只有该邮件被标记为“严格”。 此消息中包含的子消息并不严格。

  • 语法示例:

    // One simply doesn't walk into Mordor and add a new file mode, so this is
    // reasonable to be strict.
    strict bits UnixFilePermission : uint16 {
        ...
    };
    
    // It's too dangerous for clients to ignore data in this table if we
    // extend it later, but we wish to keep the wire format compatible if we
    // do change it, so it's not a struct.
    strict table SecurityPolicy {
        ...
    };
    

实施策略

  1. 更新 FIDL 兼容性测试,以验证现有语言 绑定遵循此规范。
    1. 为 (1) 仅包含 (2) 已知字段的消息添加测试用例 和 (3) 至少一个已知字段和一个未知字段。
  2. 确保 FIDL 兼容性测试包含针对 所有适当的类型。
  3. 添加了对 fidlc 中严格消息的支持。
  4. 更新了语言绑定,以支持严格的消息。
  5. 在 FIDL 兼容性测试中添加了严格消息的测试用例。

展望未来:使用网站修饰符

在设计阶段,我们还考虑过允许严格的关键字 放置在使用声明网站的 声明网站展示位置。

示例语法如下:

protocol Important {
    SomeMethod(...) -> (strict other.library.Message response);
}

此处,other.library.Message 可能未定义 strict,但 我们希望在需要严格验证的情况下一直使用它。

这会增加绑定作者的设计复杂性,因为 在严格模式和灵活模式下都可能需要other.library.Message 模式。

在编码/验证/解码方面,将严格和灵活 与上下文不同, 字符串或矢量的处理。 它们具有相同的布局,但可以有不同的边界, 使用它们。 这也类似于在可为 null 或 不可为 null 的上下文。 通常,绑定会选择一个类型架构,并用某种方式指示 例如边界、可为 null 性,或者我们在这里探讨的严格模式。

第二个问题是,为同一个应用同时显示严格模式和灵活模式 也就是处理消息汇编 消息。

例如,假设一个具有三个成员(ABC)的枚举。 为了公开灵活模式,我们需要一个特殊的枚举成员 “unknown”。 因此,现在可以组建不传递 严格验证,以便在此枚举 在严格的上下文中,编码期间将会失败。 在这里,再次强调,使用字符串和向量的并行非常重要: 高度专业化的 API,利用绑定创建字符串和向量, 过长,则将无法进行编码。

面对同时支持严格和灵活选项时要遵循的策略 为灵活模式生成所有额外元素 必要时,在编码、解码、 和验证。

工效学设计

此 FTP 从以下几个方面改善了人体工学:

  • 最好设置用户的对不同语言的 FIDL 行为的预期。
  • 严格消息可让用户避免向 处理未知字段。

文档和示例

  • 语法和需要更新语言规范 字段。
  • 应更新 FIDL 样式指南,以提供关于何时使用 将消息声明为严格。

向后兼容性

  • 此变更不会影响 ABI 兼容性。
  • 如果需要对解码器或绑定进行更改,使其符合此 FTP, 这些更改可能会导致源代码级 (API) 中断, 并逐一解决。

性能

  • 强制解码器和绑定遵循此 FTP 可能会要求 (可能没有显著的)性能损失,因为强制它们处理所有 未知字段并关闭所有句柄。
  • 绑定可能需要额外级别的间接性(因此使用 额外的内存/二进制文件大小)来提供“未知”标记以获取详尽的 代码检查。

安全

此 FTP 可提高安全性。

  • 我们为包含未知内容的消息指定验证行为。
  • 严格消息使解码器能够验证和舍弃未知消息 检查内容,从而降低出现错误的可能性。

测试

请参阅实现策略部分( 计划使用 FIDL 兼容性测试)。 此外,每种语言绑定都应有自己的测试来断言 正确行为。

缺点、替代方案和未知问题

此 FTP 主要阐明了其行为,并提供了相关的实现 以确保语言绑定符合其建议。

替代方案:默认采用严格模式或混合模式

严格程度应与矢量的大小边界相似 或字符串;它是一种独立于消息布局的约束条件, 并且可以在不破坏 ABI 的情况下进行更改。

我们希望 FIDL 作者做出明确的选择来限制(约束) 信息。

此外,我们不希望出现混合模式,在该模式下某些消息(例如枚举) 具有严格的限制,而其他(如表)则不然。

替代方案:使用 [严格] 属性,而不是新关键字

这个创意足够重要,值得为之指定关键字。 对于其他语言中类似功能的先例, 很好地翻译成 FIDL。

备选:其他关键字

在设计阶段,提出了几种不同的替代方案。 最有可能的竞争者是 final:它表示“最后 主题”在 C++、Java、C#(以及其他语言)中优先级较高。

不过,由于我们可能需要使用关键字“final”关于 这表明不能在组合中使用它(即传统的 为“final”),我们选择了另一个关键字以表示严格验证。

这为引入如下语法留出了机会:

final strict protocol Important {
    MyMethod(SomeTable arg);
};

这表明 Important 协议无法组合,并且 必须严格执行所有验证。

其他探索关键字包括:sealedrigidfixedclosedknownstandardized

替代方案:仅严格

我们可以将所有可扩展消息定义为始终严格。 目前,枚举和位只有严格的限制, 并扩展到表和可扩展联合体。

在这种情况下,如果更改可扩展结构(例如,添加 新字段)将要求读者在写入作者之前更新 已更新。 这严重限制了这些可扩展数据结构的使用, 对于更高级别的应用场景来说过于严格。

此外,如果这是设计选择,我们无需使用 用于表和可扩展联合的信封(即, 字节或句柄数量)。 事实上,根据严格的仅解释,未知字段将是 否则架构将确定字节数和 标识名的使用方式与其余消息中 FIDL 进程。

替代方案:仅灵活

我们可以将所有可扩展消息定义为始终具有灵活性。

这对于枚举(和位)来说会非常惊讶,这与 预期。 这就导致了两种不良的备选替代方法:

  • 为枚举(和位)设置例外,使其更严格,例如 如上文所述,这会造成混淆,并增加语言规则的 理解。
  • 确保广告内容灵活多变,这违背了 预期、向错误敞开大门(例如读取无效值),以及 肯定会导致 与绑定提供的选择。

继续探索其他可扩展消息(表和 可扩展的工会)存在空间,并且需要严格。

例如,考虑使用定义的安全日志记录协议 LogEntry 表格形式。 该协议的实现可能需要保证 客户端不会发送服务器无法识别的字段,因为 这些客户可能对这些新字段可能会如何控制 日志条目的处理。 例如,较新版本可能会添加字段“pii ranges”提供 包含 PII 且必须明确记录的日志条目的范围 (例如,替换为唯一 ID,以该 ID 下保管的原始数据 唯一 ID)。 为了防止旧服务器接受此类有效负载,以及可能导致错误处理 这些日志条目,作者就会为自己的数据 LogEntry,从而保护自身免遭后续可能的滥用。

先验技术和参考资料

其中一些理由是 go/proto3-unknown-fields, 说明了 proto3 为什么不再支持保留未知字段,然后 后来推翻了该决定。

  • FTP-037:事务性邮件标头 v3(尚未发布)

Footnote1

枚举和位包含在可扩展消息中,因为新成员可以 无论是在定义消息之后添加还是移除