RFC-0199:保护子 VMAR

RFC-0199:保护子 VMAR
状态已接受
区域
  • 内核
说明

允许 zx_vmar_protect 应用于子 VMAR 中的映射。

问题
Gerrit 更改
作者
审核人
提交日期(年-月-日)2022-09-13
审核日期(年-月-日)2022-11-03

摘要

允许 zx_vmar_protect 减少范围内的子 VMAR 的权限。

设计初衷

在 Fuchsia 上,软件(例如组件加载器)将映射放置在子 VMAR 中,但不会保留这些子 VMAR 的句柄并将其传递给其他软件,这种情况并不少见。在这种情况下,即使使用名义上是该范围的父级授权机构的根 VMAR,也无法再对这些映射执行 zx_vmar_protect 等操作。

protect 无法将权限提升到用于创建映射的句柄所允许的范围之外,通常用于在应用中提供额外的错误检查。例如,应用可能会移除对未使用堆部分的访问权限,从而捕获任何释放后使用或未经授权的访问。

在某些使用情形下,无需提升权限,protect 仅用于降低权限。在这些场景中,如果映射位于子 VMAR 中,无法使用 protect 来减少权限,这会减少应用可执行的错误检测。

利益相关方

辅导员

cpu@google.com

审核者

travisg@google.com, rashaeqbal@google.com, nickcano@google.com

已咨询

wez@google.com, palmer@google.com, mvanotti@google.com

共同化

此提案之前已与 Zircon 团队讨论过。

设计

引入一项新权限 ZX_RIGHT_OP_CHILDREN,用于控制对对象的运算是否允许递归到子对象。此提案仅赋予 VMAR 此权利,不允许其他对象使用此权利。右侧将成为 ZX_DEFAULT_VMAR_RIGHTS 的一部分。

现有操作的定义将更改为以下方式,以便了解此权限:

  • zx_vmar_protect:能够以递归方式应用于子区域中的映射,但子区域映射只能降低权限。下一部分将对此进行进一步讨论。
  • zx_vmar_unmapzx_vmar_op_range:已是递归,并将遵循限制此行为的新权限。
  • zx_vmar_destroy:在逻辑上是递归的,需要操作权限。

对于所有操作,其 API 说明都将定义为:当不存在 ZX_RIGHT_OP_CHILDREN 权限时,返回 ZX_ERR_ACCESS_DENIED,其中指定的范围包含子区域。

内核 VMAR 代码在处理某个范围时已能够递归到子 VMAR 中,无需为此设计新方法,因为可以使用现有的枚举工具。

保护机制

zx_vmar_protect 是在存在 ZX_RIGHT_OP_CHILDREN 时唯一获得功能的运算,本部分将探讨映射权限的工作方式,以及当应用于子映射时,这会如何应用于 protect

映射权限

映射有两种权限概念。

  1. 用于创建映射的 VMAR 和 VMO 句柄的访问权限的交集。这表示映射的最大权限。
  2. 映射的当前操作权限,所有访问权限都将根据此权限进行检查。这些权限始终等于或小于最大权限。

首次构建时,系统仅从句柄权限推断出最大权限,用户无法限制此权限。初始操作权限在选项中设置为 zx_vmar_map,并且必须小于或等于句柄的最大权限。

构建完成后,可以使用 zx_vmar_protect 操作随时更改操作权限,前提是所请求的权限不超过原始最大权限。

这样做的结果是,即使映射的最大权限允许读取,如果当前权限不允许读取,则访问会被拒绝并生成异常。其他 VMAR 操作(例如 zx_vmar_op_range)与 protect 类似,都受最大权限(而非当前权限)的限制。

层次结构权限

与映射不同,VMAR 只有一个权限概念,并且无法更改。使用 zx_vmar_allocate 创建 VMAR,该命令将以下内容作为输入:

  • 父 VMAR 的句柄
  • 新 VMAR 的权限

在此,所请求的权限必须(正如预期的那样)等于或小于父 VMAR 的句柄上的权限。

生成的子 VMAR 句柄的句柄权限设置为与创建的 VMAR 的权限(与请求的权限一致)相匹配。

这会带来以下两个后果:

  1. 在遍历 VMAR 层次结构时,权限只能降低,而绝不能提高。
  2. VMAR 句柄的句柄权限绝不会超过 VMAR 权限,但可以减少权限。

影响

zx_vmar_protect 操作目前对所请求的权限有三项要求:

  1. 调用的 VMAR 句柄必须具有所请求的权限。
  2. 由涵盖该范围的最内层 VMAR 调用,不会递归到子 VMAR 中。
  3. 范围内的每个映射都必须具有足够的最大权限。

做出此更改后,第二个要求更改为:子 VMAR 中相应范围内的每个映射都必须具有等于或高于所请求权限的当前操作权限。

实现

这可以通过几个小型 CL 完全实现,这些 CL:

  • ZX_RIGHT_OP_CHILDREN 引入内核 API。
  • 为每个 VMAR 操作添加了相应权限,并根据需要更新了文档和添加了测试。

性能

预计不会对效果产生任何影响。更改实现以支持子 VMAR 迭代可能会使现有情形变差,我们将使用 Zircon 微基准来检查这一点。

向后兼容性

ZX_RIGHT_OP_CHILDREN 将是默认权限,对于已递归的现有操作,行为将保持不变。protect 将默认获得递归能力,但在测试之外,用户不应依赖于在这种情况下生成错误。因此,让 protect 成功而不是失败不应影响任何现有用户。

安全注意事项

目前,通过在 VMAR 中放置映射、保护该映射,然后舍弃 VMAR 的句柄,可以创建无法更改其权限的映射。即使映射具有更高的最大权限,由于没有 VMAR 句柄,当前权限也永远无法更改。如果可以增加这些映射的权限,则会违反安全属性。

虽然此提案允许更改子 VMAR 中的权限,但仅允许减少权限,而不允许增加权限。举个类似的例子,使用 zx_vmar_unmap 操作可以有效地将子区域的权限减少到零。能够有选择性地将权限降低到介于无权限和当前权限之间的某个级别,并不会提供任何其他类型的权限。

注重安全的组件还可以从其拥有的任何句柄中移除 ZX_RIGHT_OP_CHILDREN

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

传递所有 VMAR 句柄

主要替代方案是保持内核不变,并更改用户空间以跟踪和传递所有子 VMAR 句柄。虽然从概念上讲很简单,但鉴于此信息交换需要在组件创建边界上进行,因此定义或扩展用于传输此信息的 API 将是一项不小的更改。

除了这种方法的实现复杂性更高之外,人们普遍认为,为了执行操作而需要保留和跟踪所有子 VMAR 或 VMO 句柄,对于用户空间来说是一种不合理的负担。