RFC-0199:保护子 VMAR

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

允许 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 仅具有单一的权限概念,并且无法更改。VMAR 是使用 zx_vmar_allocate 创建的,它将以下内容作为输入:

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

在这里,请求的权限必须(如预期)等于或小于父 VMAR 的句柄的权限。

生成的子 VMAR 句柄的句柄权限设置为与创建的 VMAR 的权限匹配,而创建的 VMAR 的权限与请求的权限匹配。

这会产生两个结果:

  1. 在遍历 VMAR 层次结构时,权限只能降低,而不能增加。
  2. VMAR 句柄的句柄权限永远不会超过 VMAR 权限,但可以降低权限。

影响

zx_vmar_protect 运算目前对请求的权限施加了三个要求:

  1. 调用的 VMAR 句柄必须具有请求的权限。
  2. 由覆盖该范围的最内层 VMAR 调用的 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 句柄对用户空间来说是不合理的负担。