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

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

需要进行更改以适应 RISC-V 指针遮盖扩展程序。

问题
Gerrit 更改
  • 817252
作者
审核人
提交日期(年-月-日)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: Userspace Top-Byte-Ignore 扩展。

Address - 地址是一个 64 位整数,表示用户地址空间边界内的某个位置。系统从未标记地址。

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

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

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

设计

符合带标记的指针 ABI

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

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

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

  3. 当内核接受标记的指针时(无论是通过系统调用还是故障),都会尝试保留该标记,但达到的程度是用户代码以后可以观察到的。

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

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

此外,Zjpm 将由内核启动选项控制。与 ARM TBI 类似,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 的实现非常相似。可以在 U 模式下启用 Zjpm,方法是在 upm CSR 中设置 uenable 位。标记大小可通过 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 寄存器的说明。

性能

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

测试

用于测试 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 能够正式支持它。本文档将进行更新,以适应规范中的所有重大变更。