| 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_unmap和zx_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。
映射权限
映射具有两种权限概念。
- 用于创建映射的 VMAR 和 VMO 句柄的访问权限的交集。这表示映射的最大权限。
- 映射的当前运算权限,任何访问都将根据此权限进行检查。这些权限始终等于或小于最大权限。
首次构建时,最大权限仅从句柄权限推断,用户没有机制来约束此权限。初始运算权限在 zx_vmar_map 的选项中设置,并且必须小于或等于句柄的最大权限。
构建后,可以使用 zx_vmar_protect 运算随时更改运算权限,前提是请求的权限不超过原始最大权限。
这样做的结果是,即使映射的最大权限允许读取,如果当前权限不允许读取,则访问将被拒绝并生成异常。其他 VMAR 运算(例如 zx_vmar_op_range)的行为与 protect 类似,受最大权限(而非当前权限)的限制。
层次结构权限
与映射不同,VMAR 仅具有单一的权限概念,并且无法更改。VMAR 是使用 zx_vmar_allocate 创建的,它将以下内容作为输入:
- 父 VMAR 的句柄
- 新 VMAR 的权限
在这里,请求的权限必须(如预期)等于或小于父 VMAR 的句柄的权限。
生成的子 VMAR 句柄的句柄权限设置为与创建的 VMAR 的权限匹配,而创建的 VMAR 的权限与请求的权限匹配。
这会产生两个结果:
- 在遍历 VMAR 层次结构时,权限只能降低,而不能增加。
- VMAR 句柄的句柄权限永远不会超过 VMAR 权限,但可以降低权限。
影响
zx_vmar_protect 运算目前对请求的权限施加了三个要求:
- 调用的 VMAR 句柄必须具有请求的权限。
- 由覆盖该范围的最内层 VMAR 调用的 VMAR,子 VMAR 将不会递归到其中。
- 范围内的每个映射都必须具有足够的最大权限。
在此更改中,第二个要求更改为:范围内的子 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 句柄对用户空间来说是不合理的负担。