RFC-0224:用户空间 J-Extension 指针遮盖

RFC-0224:用户空间 J 扩展指针遮盖
状态已接受
区域
  • 内核
说明

为适应 RISC-V 指针屏蔽扩展而需要的更改。

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

摘要

本文档提出了在 Fuchsia 用户空间中支持 RISC-V J 扩展指针屏蔽功能所需的更改。

设计初衷

RISC-V J 扩展旨在使 RISC-V 成为传统上被解释或 JIT 编译的语言,或需要大型运行时库或语言级虚拟机的语言的理想目标。示例包括(但不限于)C#、Go、Haskell、Java、JavaScript、OCaml、PHP、Python、R、Ruby、Scala、Smalltalk 或 WebAssembly。J 扩展的一项值得注意的功能是指针屏蔽 (PM)。这是一项硬件功能,启用后,MMU 在内存访问时会忽略有效地址的最高 N 位。这与 ARMv8.0 CPU 上的 Top-Byte-Ignore (TBI) 功能非常相似。PM 的一个直接用途是在用户空间中启用硬件辅助 AddressSanitizer (HWASan),其中标记存储在最高字节中以进行内存跟踪。

术语

本文档中使用的许多术语都可从 RFC-0143:用户空间最高字节忽略中扩展而来。

地址 - 地址是一个 64 位整数,表示用户地址空间范围内的某个位置。地址永远不会被标记。

指针 - 可解引用的内存位置,可能带有标记,也可能不带标记。这相当于 RISC-V 基本 ISA 中定义的有效地址。

标记 - 指针的高位,通常用于元数据。RISC-V 指针屏蔽支持不同的标记大小。

Zjpm - 这是 J 扩展中指针遮盖功能的正式标识符。

设计

符合已加标记的指针 ABI

Zjpm 可启用已标记的用户空间指针。内核对这些指针的处理遵循 RFC-0143 中规定的相同规则。即:

  1. 内核将忽略从系统调用接收到的用户指针上的标记。

  2. 在接受地址的系统调用中传递带标记的指针是错误的。

  3. 当内核通过系统调用或故障接受带标记的指针时,会尝试保留该标记,以便用户代码稍后可以观察到该标记。

  4. 内核本身永远不会生成标记指针。

  5. 比较用户空间指针时,内核会忽略可能存在的任何标记。

此外,Zjpm 将由内核启动选项控制。与 ARM TBI 类似,启用 Zjpm 后,所有用户空间进程都会启用 Zjpm。

RISC-V 不会为写入调试寄存器的值提供任何语义含义,因此调试器可以自由地将标记值写入调试寄存器。对于监视点等功能,指针比较通过 mcontext6 寄存器的 match 字段进行控制,该字段可控制为匹配确切的标记值或忽略标记,就像使用指针掩码一样。

将标记设置为 8 位(对于 Sv39 和 Sv48)

Zjpm 最直接的用例是在 RISC-V 上启用内存错误检测工具(例如 HWASan),这取决于将元数据存储到指针的最高位。ARM TBI 仅支持 8 位标记大小。不过,Zjpm 更加灵活,允许忽略内存访问的可变数量的最高位。在不同模式下,可以通过 CSR 寄存器控制此位数。

最简单的方法是仅遵循我们现有的规则。ARM TBI 和 HWASan 已经使用 8 位标记,但目前或可预见的未来,对大于(或小于)8 位的标记并没有任何需求。

仅针对 Sv39 和 Sv48 支持 8 位标记大小。这是因为 Sv57 仅支持遮盖前 7 位。如果未来支持 Sv57,则需要重新考虑标记大小。

实现

大部分实现将与 ARM TBI 的实现非常相似。通过在 upm CSR 中设置 uenable 位,可以在 U 模式下启用 Zjpm。可以通过 upm 中的 ubits 位字段设置标记大小。

现有的系统调用基础架构应已设置为接受具有固定标记大小的标记用户空间指针。

ZX_FEATURE_KIND_ADDRESS_TAGGING 功能将添加一个额外的标志(类似于 ZX_RISCV64_FEATURE_ADDRESS_TAGGING_ZJPM_8BIT),用于指示 Zjpm 是否已启用以及标记大小。ZX_RISCV64_FEATURE_ADDRESS_TAGGING_ZJPM_8BIT 表示前 8 位肯定会被屏蔽,但未来我们可能会添加其他标志,以表示更多的高位会被屏蔽。例如,我们可以添加类似 ZX_RISCV64_FEATURE_ADDRESS_TAGGING_ZJPM_16BIT 的内容来指示前 16 位被屏蔽,但 ZX_FEATURE_KIND_ADDRESS_TAGGING 也会设置 8 位等效标志,以确保检查 8 位标志的兼容代码适用于未来的系统。

Zjpm 还依赖于已启用的 Zicsr 扩展,该扩展提供用于修改 CSR 寄存器的指令。

性能

性能影响应该可以忽略不计,并且将使用现有的微基准进行验证。

测试

用于测试 ARM TBI 的同一套测试也应应用于 Zjpm。这些测试应在很大程度上与启用了哪种地址标记模式无关。

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

Zjpm 比 ARM TBI 灵活得多,因此我们可以支持更多遮盖选项。

目前,似乎没有任何真实硬件支持最新版本的 Zjpm,因此内核如何发现硬件中可用的支持尚不明确。在我们了解硬件的实际限制之前,这里提出的保守的单功能始终开启模式将是最佳支持的模式(如果有)。QEMU 应该支持指针屏蔽,并且可以通过“x-j”CPU 属性启用。

将标记始终设置为某个其他静态值

标记 ABI 允许支持不同的标记大小(也就是说,我们不受 8 位限制)。实际上,我们很少使用虚拟地址的最高位。在 x86 上,我们实际上只使用最低的 48 位,但这只是我们目前针对的目标所做的假设,未来可能会发生变化。目前,指示标记大小的位字段长度为 5 位,这意味着只能忽略指针的最高 31 位。此字段之上的其余位是 WPRI,因此将来可以扩展此字段以支持更大的值。

对于 HWASan 等工具,内存标记算法不一定依赖于 8 位标记大小。将标记大小增加到 16 位左右可以显著降低标记比较中出现假正例的几率,但这会意味着需要将更大的标记存储到影子内存中,这并不是很理想。目前,使用 8 位时的假正例概率已经非常小。

将标记作为可修改的值公开给用户空间

此选项意味着向用户公开指针遮盖功能的方法。 也就是说,用户可以 (1) 在运行时启用/停用指针遮盖,并且 (2) 可以更改标记大小。这可能不太理想,因为没有立即执行此操作的必要性,并且需要添加更多系统调用来切换这些值。不过,标记 ABI 为将来支持此功能留出了空间。

指令提取时的指针屏蔽

Zjpm 的一项强大功能是能够在指令提取时启用 PM,包括因直线执行、控制转移(例如,分支和直接/间接跳转以及 uret/sret/mret)而导致单调 PC 增加的指令提取。此提案仅概述了数据指针的指针屏蔽规则,但为未来探索此选项留出了空间。

与其他理想的硬件功能互动

Zjpm 仅引入了指针遮盖功能。其他实用功能(例如标记检查或沙盒强制执行)可以在软件或需要 Zjpm 的未来硬件扩展程序中实现。一个类似的示例功能是依赖于 TBI 的 ARM MTE。

草稿的更改

Zjpm 目前仍是一项提案草案,但人们希望它能获得 RVA23 批准,以便 HWASan 能正式支持它。如果规范发生任何重大变化,我们会更新本文档以适应这些变化。