| RFC-0028:处理权限 | |
|---|---|
| 状态 | 已接受 |
| 区域 |
|
| 说明 | 在 FIDL 中注释必需或排除的句柄权限。 |
| 作者 | |
| 提交日期(年-月-日) | 2019-04-01 |
| 审核日期(年-月-日) | 2019-04-18 |
“The Right Stuff”
摘要
在 FIDL 中注释必需或排除的句柄权限,具体如下:
具有一种机制,用于描述所有句柄类型(例如
Protocol、request<Protocol>、handle)的权限在序列化时,强制执行权限过滤,即权限说明决定了一个掩码,该掩码指示必须拥有的一组权限。
在反序列化时,强制执行权限验证和提供的权限集。
设计初衷
句柄权限是用于减弱 Fuchsia 内权限的工具。在此 FTP 之前,FIDL 无法描述句柄权限。目前,在对句柄权限有重要限制或要求的几个位置,此类说明会降级为方法注释。
理想的权限限制示例包括只读 IPC 或 VMO 句柄,以及不可执行的 VMO 句柄。
理想的权限要求示例包括可写入的 IPC 句柄。
设计
权利限制
此提案为 FIDL 新增了一种类型的限制:权限注释。这可以应用于 FIDL 数据结构和方法参数中的所有句柄类型:句柄、协议和协议请求。
可以在类型化句柄声明中指定权限限制:handle<*subtype*, *RC*>。它们无法在纯句柄上指定,因为权限仅在特定句柄子类型的上下文中才有意义。如果句柄声明中未指定权限限制,则会转发具有相同权限的权限(现有行为不会发生变化)。
示例:
handle<vmo, zx.rights.READ | zx.rights.WRITE> 需要读取和写入权限,发送给客户端时会移除所有其他权限
handle<vmo> 未指定任何权利,转发现有权利
必须为每个权利限制指定至少一项权利(例如,handle<vmo, > 是无效的)。
无法为服务器和客户端协议端点(myprotocol 和 request<myprotocol>)指定权限限制。这些端点始终具有 zx.TRANSFER | zx.WAIT | zx.INSPECT | zx.WRITE | zx.READ | zx.SIGNAL | zx.SIGNAL_PEER 权限,并且子类型设置为渠道。如果需要自定义权限(例如,对于不可移动的协议),可以使用 handle<*channel*, *RRC, ORC*>。(使用模板可能会进一步优化此流程,我们将在未来的 FTP 中讨论此问题)。
版权限制必须遵循“默认拒绝”政策。必须明确列出所有权利。
语法
句柄权限限制是可以使用按位运算符指定的位类型表达式。在上面的示例中,| 是按位或运算符。此语法旨在具有通用性,也适用于 FIDL 语言中其他位置的位类型值。在 zx 库中,将有一个包含所有权限的位定义 zx.rights。
虽然重复 zx.rights.[*right*] 的权利规范很冗长,但有提案建议在未来的 FTP 中缩短它,因此该规范被认为不在本 FTP 的范围内。
绑定
绑定必须通过销毁消息并关闭具有墓志铭 ZX_ERR_ACCESS_DENIED 的通道来响应对具有错误所需权限的句柄的接收和反序列化。
绑定必须通过以下方式来响应具有错误必需权限的句柄的序列化:无法序列化、销毁消息,以及禁止发送消息。绑定还必须使用 ZX_ERR_BAD_STATE 关闭渠道。
默认值
不会有句柄权限的默认值。此处的推理如下:
默认值的行为不明确,并且在不同对象类型之间可能不一致。 用户无法直观地了解给定对象的默认权限。
默认值不鼓励使用精细的句柄权限,这使得在编写 FIDL 定义时很容易不考虑权限。
如果存在默认值,对于许多对象类型,最合适的候选值将是“无权限”或“最大权限”。如果将默认右侧边距设置为最大值,则会限制此 FTP 中更改的有效性;但如果将默认右侧边距设置为无,则效果与不设置默认值相同。但这两者都不是很有帮助。
重复使用句柄声明
没有默认值的一个后果是,指定权限可能会很冗长。为了改进这一点,我们将允许使用别名功能(即 using 关键字)为整个句柄声明命名。
using readable_vmo = handle<vmo, zx.READ>
一种替代方案可能是允许为权利限制设置别名(例如,为 zx.READ | zx.WRITE 设置“io”别名),但这会提供一层间接层,从而模糊权利,尤其是在广泛使用这些权利的情况下。通过允许在对象级进行别名化,可将使用范围限制为类型相同的位置。
可参数化性
我们希望创建包含以下渠道的通用消息:其权利限制可以参数化。
例如,考虑包含 handle<vmo> 的 fuchsia.mem.Buffer。应该可以实现以下效果:通过约束 C 流向 handle<vmo> 来约束 fuchsia.mem.Buffer:C。
广义类型别名是实现此目标的第一步,而模板的引入将进一步满足此需求。虽然这不在本提案的范围之内,但必须在相关工作中考虑此要求。
实施策略
消息接收应依赖于 zx_channel_read_etc 系统调用,以便在调用时提供权限信息。绑定将使用权限信息来验证是否具有所需的权限,并过滤掉超出所需权限范围的任何其他接收到的权限。
消息的发送应依赖于 zx_channel_write_etc,该方法会将发送权限减少到指定权限的集合,并验证所有必需的权限是否都存在,如果验证失败,则返回 ACCESS_DENIED。绑定将负责在收到此响应后关闭渠道。为了与现有行为保持一致,此系统调用将使用 ZX_HANDLE_OP_MOVE,这相当于先调用 zx_handle_replace,然后再调用 zx_channel_write。如果未指定任何权限,系统将使用 ZX_RIGHT_SAME_RIGHTS 代替权限。
我们正在制定一份详细描述实现过程的实现意向文档。
工效学设计
从长远来看,这有助于提升人体工程学效果。权限文档和检查以标准方式在整个 FIDL 中完成,而不是通过临时注释和检查完成。
文档和示例
这需要更改文档,以:
向后兼容性
ABI 兼容性
权限更改不得破坏 ABI 兼容性。
来源兼容性
权限更改可能会破坏源代码兼容性。打破源代码兼容性是意料之外的,但如果绑定作者选择此路径,则应明确记录。
添加所需权限
在消息接收者方面,它向后兼容,具有额外的必需权限,因为它只会提供更多功能。不过,在消息发送方,添加必需的权限是一项向后不兼容的更改,因为现在需要先拥有该权限才能发送消息。
移除必需的权利
在发送者端,它向后兼容,可用于移除必需的权限;但在接收者端,它向后不兼容。接收方现在将无法获得其预期中的权利。
添加可选权利
添加可选权限是向后兼容的。
移除可选权利
在发件人端,它向后兼容,可用于移除可选权限;但在收件人端,它向后不兼容。相应收件人现在将无法获得之前可能已获得的权利。
环境假设
此模型的部署可能会打破一些环境假设。例如,客户端可能会假设通过连接接收到的所有 VMO 都是可映射的,即使服务器并不打算提供此保证也是如此。
可以将此状态视为此 FTP 的全部动机:移除这种普遍存在的隐式合约。
性能
微基准测试表明,zx_channel_write_etc 和 zx_channel_read_etc 的性能非常相似。对于包含 1 个句柄的 64 字节消息,zx_channel_write/zx_channel_read 需要 962 纳秒,而 zx_channel_write_etc/zx_channel_read_etc 需要 1000 纳秒。
读取或写入句柄时所需的句柄数组将从 256 字节 (ZX_CHANNEL_MAX_MSG_HANDLES * sizeof(zx_handle_t)) 增加到 1024 字节 (ZX_CHANNEL_MAX_MSG_HANDLES * sizeof(zx_handle_info_t))。同样,写入句柄时堆栈分配的数组将从 256 字节增加到 1280 字节 (ZX_CHANNEL_MAX_MSG_HANDLES * sizeof(zx_handle_disposition))。
为了将堆栈分配保持在 256 字节的最大值,如果句柄表过大(读取时超过 16 个句柄或写入时超过 12 个句柄),我们将需要进行堆分配。在此次变更中,我们将考虑消息大小和句柄表的组合堆栈分配要求(目前我们只考虑消息大小)。
安全
这是一项安全改进。这样可以更准确地审核我们的 API 表面。它将权限检查移至绑定中,这样可以更好地审核,而不是在所有调用位置进行相同的检查。
例如,一旦权限被完全使用,就可以轻松审核所有转移可执行 VMO 所有权的地方。这在今天很难实现。
测试
每个绑定实现都应进行单元测试。
此功能的推出还应确保修改为使用此功能的 FIDL 协议的实现会测试新功能。
缺点、替代方案和未知因素
缺点:公共 API 将非常冗长
从某种意义上来说,此特征是“有噪声的”。在系统的典型执行过程中,可能永远不会采用缺少权限的路径。但它们仍然占据公共系统 API 中的实际空间。
我们认为,这种成本换来的清晰度和精确度是值得的,并且合理使用别名可以满足大多数需求。
例如,考虑以下两类可以避免或减少的问题:
因权限不兼容而导致远处发生中断:故障可能发生在距离来源很远的地方(如果句柄再次转移,甚至可能发生在下游的其他进程中)。
未记录的假设会导致兼容性问题:某些客户端/服务器可能会传递权限比严格需要的更多的句柄,导致其对等方假设它们可以依赖这些权限,如果这些假设被证明是错误的,则会导致兼容性问题。
我们知道,详细说明权限很烦人,但它相当于为功能输入信息。
替代方案:权利的下限和上限
初始设计要求对权限设置下限和上限(例如“无执行权限”或“有写入权限”)。从安全角度来看,Fuchsia 应该对所有功能(和权限)采用“默认拒绝”政策。
因此,如果某项功能(或权限)未在任何地方明确提及,则不应授予该功能(或权限)。组件清单已针对沙盒化采取这种行为,理想情况下,FIDL API 也应如此。
我们可能还希望允许明确表达“可选权利”,即可能提供也可能不提供的权利。
因此,从限制条件语法的角度来看(即人们在编写 FIDL API 时输入的内容),我们正朝着列出权限的方向发展,并将一些权限标记为可选。
请注意,从约束语义的角度来看(即需要在 JSON IR 中表达的内容以及由绑定实现的内容),此语法更改继续表达下限和上限检查。
强制性源代码兼容性
在 Fuchsia 中,权限通常表示为 uint32,并且权限的值可以随时更改。因此,在 FIDL 中更改权限值似乎不会对生成的源代码造成实质性更改。不过,在某些使用情形下,可能需要打破源代码兼容性,例如,如果存在给定的权限(在本例中为 zx.rights.WRITE),则生成特定方法(例如 write())。因此,我们并未规定权限更改不得打破源代码兼容性。