RFC-0033:处理未知字段和严格程度 | |
---|---|
状态 | 已接受 |
领域 |
|
说明 | 此 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 }
- 该语言绑定应提供特殊的“未知”标记,标记,
可纳入详尽检查中,确保通用分析规则
(例如,C/C++ 中的
严格处理消息
- 我们引入了一个可以为可扩展消息添加前缀的
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 { ... };
实施策略
- 更新 FIDL 兼容性测试,以验证现有语言
绑定遵循此规范。
- 为 (1) 仅包含 (2) 已知字段的消息添加测试用例 和 (3) 至少一个已知字段和一个未知字段。
- 确保 FIDL 兼容性测试包含针对 所有适当的类型。
- 添加了对
fidlc
中严格消息的支持。 - 更新了语言绑定,以支持严格的消息。
- 在 FIDL 兼容性测试中添加了严格消息的测试用例。
展望未来:使用网站修饰符
在设计阶段,我们还考虑过允许严格的关键字 放置在使用声明网站的 声明网站展示位置。
示例语法如下:
protocol Important {
SomeMethod(...) -> (strict other.library.Message response);
}
此处,other.library.Message
可能未定义 strict
,但
我们希望在需要严格验证的情况下一直使用它。
这会增加绑定作者的设计复杂性,因为
在严格模式和灵活模式下都可能需要other.library.Message
模式。
在编码/验证/解码方面,将严格和灵活 与上下文不同, 字符串或矢量的处理。 它们具有相同的布局,但可以有不同的边界, 使用它们。 这也类似于在可为 null 或 不可为 null 的上下文。 通常,绑定会选择一个类型架构,并用某种方式指示 例如边界、可为 null 性,或者我们在这里探讨的严格模式。
第二个问题是,为同一个应用同时显示严格模式和灵活模式 也就是处理消息汇编 消息。
例如,假设一个具有三个成员(A
、B
和 C
)的枚举。
为了公开灵活模式,我们需要一个特殊的枚举成员
“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
协议无法组合,并且
必须严格执行所有验证。
其他探索关键字包括:sealed
、rigid
、fixed
、closed
、
known
和 standardized
。
替代方案:仅严格
我们可以将所有可扩展消息定义为始终严格。 目前,枚举和位只有严格的限制, 并扩展到表和可扩展联合体。
在这种情况下,如果更改可扩展结构(例如,添加 新字段)将要求读者在写入作者之前更新 已更新。 这严重限制了这些可扩展数据结构的使用, 对于更高级别的应用场景来说过于严格。
此外,如果这是设计选择,我们无需使用 用于表和可扩展联合的信封(即, 字节或句柄数量)。 事实上,根据严格的仅解释,未知字段将是 否则架构将确定字节数和 标识名的使用方式与其余消息中 FIDL 进程。
替代方案:仅灵活
我们可以将所有可扩展消息定义为始终具有灵活性。
这对于枚举(和位)来说会非常惊讶,这与 预期。 这就导致了两种不良的备选替代方法:
- 为枚举(和位)设置例外,使其更严格,例如 如上文所述,这会造成混淆,并增加语言规则的 理解。
- 确保广告内容灵活多变,这违背了 预期、向错误敞开大门(例如读取无效值),以及 肯定会导致 与绑定提供的选择。
继续探索其他可扩展消息(表和 可扩展的工会)存在空间,并且需要严格。
例如,考虑使用定义的安全日志记录协议 LogEntry
表格形式。
该协议的实现可能需要保证
客户端不会发送服务器无法识别的字段,因为
这些客户可能对这些新字段可能会如何控制
日志条目的处理。
例如,较新版本可能会添加字段“pii ranges
”提供
包含 PII 且必须明确记录的日志条目的范围
(例如,替换为唯一 ID,以该 ID 下保管的原始数据
唯一 ID)。
为了防止旧服务器接受此类有效负载,以及可能导致错误处理
这些日志条目,作者就会为自己的数据
LogEntry
,从而保护自身免遭后续可能的滥用。
先验技术和参考资料
其中一些理由是 go/proto3-unknown-fields, 说明了 proto3 为什么不再支持保留未知字段,然后 后来推翻了该决定。
- FTP-037:事务性邮件标头 v3(尚未发布)
Footnote1
枚举和位包含在可扩展消息中,因为新成员可以 无论是在定义消息之后添加还是移除