diff --git a/articles/20230626-rvsec-intro-part1.md b/articles/20230626-rvsec-intro-part1.md new file mode 100644 index 0000000000000000000000000000000000000000..7d28b86ac17d0af51b2944f1866f9ef5f16ab78d --- /dev/null +++ b/articles/20230626-rvsec-intro-part1.md @@ -0,0 +1,148 @@ +> Author: Mingde Ren
+> Date: 2023/06/26
+> Revisor: Falcon
+> Project: [RISC-V Linux 内核剖析](https://gitee.com/tinylab/riscv-linux)
+> Proposal: [RISC-V Security 技术调研](https://gitee.com/tinylab/riscv-linux/issues/I5WEQX)
+> Sponsor: PLCT Lab, ISCAS + +# RISC-V 安全拓展调研(Part 1) + +## 前言 + +RISC-V 架构因其开源、低成本、高度可定制等特点,近年来受到众多大小厂商青睐,已经在物联网等领域投入生产使用,可预见的未来内还可能会被部署在云计算、移动端等场景中。在生产环境中,设备除了要提供足够的功能之外,还需要提供信息安全等保障。x86、Arm 等平台下已经有相对完善的可信计算框架(Intel SGX、Intel TDX、AMD SEV、Arm Trustzone、Arm CCA 等)。而 RISC-V 的 specification 目前只规定了一些安全特性(如 PMP),暂时还没有提供一套完整公认的可信计算框架。 + +针对 RISC-V 下的安全特性设计,学术界和工业界都进行了广泛的探索。但是由于 RISC-V 的高度可定制性,这些设计思路大多彼此独立,导致暂时并没有资料完整地总结介绍整个 RISC-V 生态下的安全特性设计。因此,本系列文章将从硬件特性、软件支持角度总结现存的学术界以及工业界中的 RISC-V 安全特性设计。 + +本文为系列文章的第一部分,将先介绍 RISC-V 官方 specification 中规定的硬件安全特性,并介绍基于 PMP 规范实现的可信计算框架 Keystone。 + +## RISC-V Specification 中的安全拓展 + +RISC-V 的各种拓展还在非常活跃的开发中,因此除了已经发布的 specification 外,还有许多值得参考的官方文档。可以在[这里](https://wiki.riscv.org/display/HOME/Recently+Ratified+Extensions)查看近期已经得到批准、但尚未合并到官方 specification 中的拓展。还在开发中,尚未完全确定并得到批准的拓展可以在[这里](https://wiki.riscv.org/display/HOME/Specification+Status)查看。其中与安全相关的有:已获得批准的 SMEPMP、SMSTATEEN,尚未获得批准的 SPMP、IOPMP、IOMMU 等。本文将主要介绍已经得到批准的安全拓展。 + +## PMP(物理内存保护,Physical Memory Protection) + +### PMP 介绍 + +PMP 是 RISC-V 架构 specification 中规定的一种硬件安全特性,用于对物理内存进行访问控制。使用 PMP 可以将物理内存划分为多个区域,并对每个区域分别设置读、写、可执行权限。处理器中每个核都有一个独立的 PMP 单元,用于限制核对物理内存的访问。此外,如果处理器支持虚拟地址,那么 PMP 同样会作用在 MMU 对物理内存的访问上,如果该核对任意一级页表所在的物理地址没有访问权限,那么这次地址翻译将会失败,并触发访问错误(access fault),陷入到 M-mode 中。 + +使用 PMP 可以提供深度防御(defend-in-depth),即使有漏洞的操作系统被攻破,攻击者所能造成的损害也会受限。例如,OpenSBI 默认使用一个 PMP 条目来禁止 S-mode 和 U-mode 的软件对自己所在物理内存的访问,从而保障自身安全。理论上我们也可以在内核启动后对代码段和只读数据段用 PMP 禁止写入操作来保证内核代码和只读数据的完整性。 + +PMP 规定了一系列 CSR(控制与状态寄存器,Control and Status Register)来划分物理内存区域和配置权限,这些 CSR 只能由 M-mode 的特权软件访问。这些 CSR 包括用于划分物理地址的 `pmpaddr` 寄存器和用于配置权限的 `pmpcfg` 寄存器。每个 `pmpaddr` 寄存器标注了一个物理地址,用于匹配一段 PMP 区域。每个 PMP 区域对应一个 8 比特的 `pmpcfg` 条目。在 RV32 中,每四个 `pmpcfg` 条目打包在一个 `pmpcfg` CSR 中(如下图),依次命名为 `pmpcfgN`,其中 N 为该寄存器的序数。 + +![RV32 PMPCFG](images/20230626-rvsec-intro-part1/RV32-pmpcfg-layout.png) + +而在 RV64 中,每八个 `pmpcfg` 条目被打包在一个 `pmpcfg` CSR 中。为了和 RV32 兼容,这些 CSR 命名会跳过奇数,仅使用偶数(如下图)。 + +![RV64 PMPCFG](images/20230626-rvsec-intro-part1/RV64-pmpcfg-layout.png) + +在 20211203 版的 specification 中,RISC-V 核的实现可以提供 0、16 或 64 个 PMP 寄存器。注意:PMP 寄存器的数目可以是 0,意味着标准并没有要求所有的 RISC-V 核都实现 PMP 功能。此外,尽管 specification 里 PMP 寄存器可以至多有 64 个,但在实际电路中很难实现这么多的数量,现有的 RISC-V 开发板大多只支持 8 个 PMP 寄存器。 + +PMP 提供了三种方式来使用 `pmpaddr` 寄存器:NAPOT,NA4 和 TOR,其中 NA4 可以看成 NAPOT 的一种特例。三种方法中,TOR 的使用相对简单,它使用两个连续的 `pmpaddr` 寄存器分别标注一段物理内存的起始地址和终止地址。如下图所示,当 `pmpcfg[i]` 被设置为 TOR 模式时,其对应的 PMP 区域将由 `pmpaddr[i-1]` 和 `pmpaddr[i]` 标注,即图中的 `0x8080_0000` 到 `0x80C0_0000` 区域。注意这里的 i 并不限定奇偶数,并且当 i 为 0 时,将自动匹配 `0x0` 到 `pmpaddr[0]` 的区域。 + +![TOR 配对模式](images/20230626-rvsec-intro-part1/pmpaddr.png) + +为了节约 PMP 寄存器的使用数量,specification 中还约定了 NAPOT 和 NA4 两种地址匹配模式。这两种模式要求 PMP 区域的起始地址是按照区域大小对齐的,并且区域大小必须为 2 的幂次。我们可以继续使用上图所示的区域(`0x8080_0000` 到 `0x80C0_0000`)作为例子。这段区域大小为 `0x40_0000`,即 4M,并且起始地址 `0x8080_0000` 是 4M 对齐的,因此可以使用 NAPOT 模式进行地址匹配。配置方式为将 `pmpcfg[i]` 设置为 NAPOT 模式,并将 `pmpaddr[i]` 的高位设置为地址右移两比特(地址要求是 4 字节对齐的),低位设置为区域大小减一:`0x205F_FFFF`(`(0x8080_0000 >> 2) | (0x40_0000 - 1)`)。更一般的配置方式如下图: + +![NAPOT 地址编码](images/20230626-rvsec-intro-part1/NAPOT-encoding.png) + +需要注意的是,尽管我们说 NA4 是 NAPOT 的特例,但是给定一个 `pmpaddr` 寄存器,我们无法判断这个寄存器的值是 NA4 模式下的一个地址,还是 NAPOT 模式中一个对齐的地址加上末尾的标识。因此 NA4 和 NAPOT 在 `pmpcfg` 寄存器的配置中要区分开来。此外,specification 中提到,RISC-V 设备不需要对上图中每个粒度都提供支持,粒度可以由厂商自行决定。如 D1 Nezha 开发板中,PMP 的最小粒度为 4K 内存页,小于此粒度的配置会对齐到此粒度。 + +刚刚提到每个 `pmpcfg` 包含八个比特,它们提供了对应的 PMP 区域的地址匹配模式、访问权限、配置锁定的功能,如下图: + +![PMPCFG 寄存器](images/20230626-rvsec-intro-part1/pmpcfg.png) + +其中 A 代表开关和刚刚介绍的三种地址匹配模式,对应关系如下: + +![PMPCFG A 比特控制三种地址匹配模式](images/20230626-rvsec-intro-part1/pmpcfg-a.png) + +其他位中,X、W、R 分别代表可执行、写、如权限,L 代表锁定。当 L 设置为 1 后,直到下次系统重置(如重启)之前,PMP 配置都无法被更改。并且当 L 未被设定时,PMP 只对 U/S-mode 生效;而设定 L 后,PMP 也会对 M-mode 生效,即此时无论机器处于哪种特权状态,违反 PMP 设定的物理内存访问都会触发访问错误。 + +此外,PMP 区域之间可以存在重叠,PMP 寄存器的序数越低则优先级越高,因此重叠区域的配置将以低序数 PMP 寄存器为准。 + +### 基于 PMP 实现的可信计算框架 + +Keystone 是一个软件 TEE(可信执行环境,Trusted Execution Environment)框架,RISC-V PMP 是 Keystone 唯一依赖的硬件安全特性。TEE 通常提供与操作系统相隔离的区域用来执行需要隐私保护的应用程序,这些隔离区域被称为飞地(Enclave)。TEE 可以在操作系统内核被攻破的情况下保障飞地内程序的完整性和隐私性。Keystone 在 M-mode 实现了一个基于 OpenSBI 的安全监视器(Security Monitor),安全监视器向操作系统提供了用于创建、执行、终止、验证飞地等功能的 ABI: + +![Keystone: sm/src/sm-sbi.h](images/20230626-rvsec-intro-part1/keystone-sm-api.png) + +Keystone: sm/src/sm-sbi.h + +操作系统可以为飞地分配初始资源,以及初始配置,当配置完成后,安全监视器将会审查配置并生成签名,用户可以通过签名确认飞地初始状态的完整性。飞地进入执行状态之前,安全监视器会更新 PMP 配置,使得除了飞地所在核以外的所有的核都无法访问飞地访问区域;同时,飞地本身所在核将被配置为只能访问自己所拥有的 PMP 区域,如下图所示: + +![图源:Keystone, EuroSys’20](images/20230626-rvsec-intro-part1/keystone-pmp.png) + +图源:Keystone, EuroSys’20 + +图中可以看到飞地中除了需要隔离保护的机密区域外,还预留了一段不可信空间(U1),用于与操作系统通信(如将计算结果发送至操作系统等)。 + +为了简化可信应用的开发,Keystone 提供了对在飞地中执行静态链接的 ELF 文件的支持。但是 ELF 文件的执行依赖系统调用,如果让操作系统内核直接提供系统调用的支持,则会使飞地对操作系统建立不必要的信任,违背了提供飞地的初衷。为此,Keystone 为每个飞地提供了一个小型运行时(runtime),运行时中提供了关键系统调用的支持,并将其余的系统调用转发给内核,如下图: + +![图源:Keystone, EuroSys’20](images/20230626-rvsec-intro-part1/keystone-arch.png) + +图源:Keystone, EuroSys’20 + +Keystone 是第一个仅依赖 RISC-V PMP 实现的基于软件的 TEE,它已经展示出类似近来流行的安全虚拟机的设计。它展示了新思路的同时,留下了很多值得完善的地方: + +- Keystone 中飞地的数量受到 PMP 数量的限制,使得其无法直接在云计算等场景中应用; +- Keystone 的飞地无法直接进行 I/O 操作,而是依赖系统内核代理这部分系统调用,I/O 作为常见的攻击面,对操作系统的 I/O 依赖可能会对飞地安全造成威胁; + +## SMEPMP(为防止 M-mode 内存访问和执行而提供的 PMP 增强,PMP Enhancement for memory access and execution prevention on Machine mode) + +### 设计 SMEPMP 的动机 + +SMEPMP 是一个已经得到 RISC-V 社区批准的安全拓展,全称是为防止 M-mode 内存访问和执行提供的 PMP 增强(PMP Enhancement for memory access and execution prevention on Machine mode),从名称可以看出这个拓展是一个对 PMP 的增强。 + +RISC-V 标准中通过 `sstatus.SUM` 比特和页表项中的 U 比特,提供了对 SMAP(S-mode 内存访问预防,Supervisor Memory Access Prevention)和 SMEP(S-mode 内存执行预防,Supervisor Memory Execution Prevention)的支持:当 `sstatus.SUM` 被设置时,设定了 U 比特的页将不能被 S-mode 访问;设定了 U 比特的页中的代码永远不能被 S-mode 直接执行。SMAP 和 SMEP 的设计也是一种 defend-in-depth,可以用于避免一些巧妙构造的攻击:这些攻击并不直接篡改特权等级的内存,而是通过诱导特权等级去访问或执行本不应该访问或执行的非特权等级内存,来间接攻破特权等级的软件。 + +但是有很多 RISC-V 的设备中只有 U/M-mode,这在嵌入式设备中非常常见。对于这些设备,此前的 RISC-V 标准无法提供 SMAP/SMEP 特性。此外,在存在 S-mode 的设备中,此前标准也无法为 M-mode 对来自 S-mode 的攻击提供 SMAP/SMEP 保护。这是因为此前的 SMAP/SMEP 是通过页表实现的,而 M-mode 会直接访问物理地址。设定有 L 比特的 PMP 是此前唯一可以限制 M-mode 访问的方式(如设定一段区域为只读),但是 PMP 做不到 M-mode 无法访问的同时让 U/S-mode 可以访问。SMEPMP 的设计就是为了给 M-mode 提供 SMAP/SMEP 支持。 + +### SMEPMP 的机制 + +SMEPMP 的运行机制相对 PMP 而言显得十分繁杂,通过图片可以比较清晰地梳理清楚。我们首先讨论当内存访问/执行的地址匹配到了某个 PMP 区域内的情况,如下图: + +![命中 PMP 区域时 SMEPMP 作用效果图](images/20230626-rvsec-intro-part1/smepmp-hit.png) + +`mseccfg` 是 SMEPMP 配置的核心 CSR,我们首先关注其中的 MML(M-mode 锁定,Machine Mode Lockdown)比特。MML 和原先 PMP 配置中的 L 比特决定了大部分情况。 + +- `MML=0` 时(图中左半边),所有设定和没有 SMEPMP 时一致(见前文),即当 L 未设置时,S/U-mode 的访问会根据 PMP 配置来管理(图中的 enforced);而 L 设置后,PMP 配置将对所有等级生效,并锁定。 +- `MML=1` 时(图中右半边,暂时忽略最后三行): + - 若 `L=0`,则 S/U-mode 的访问依据 PMP 配置管理,禁止 M-mode 的访问(图中的denied),从而实现 SMAP/SMEP。 + - 若 `L=1`,则禁止 S/U-mode,PMP 配置对 M-mode 的访问生效。 + +尽管这样的设计一定程度上提供了 SMAP/SMEP,但是它相当不灵活,因为一旦 `MML` 和 `L` 都设置为 1 后,就无法重新让 S/U-mode 能访问该区域。为此,SMEPMP 在 `mseccfg` 中引入了 RLB(规则锁定绕过,Rule Locking Bypass)比特。当 `RLB=1` 时,上图中的锁定效果将被忽略。但是为了防止 L 比特本身失去意义,RLB 本身又加入了锁定特性:当存在至少一个 L 比特设定的 PMP 配置时,如果 RLB 被关闭(置为0),则 RLB 将被锁定在关闭状态,此前带有 L 比特的 PMP 设置也将被锁定。需要注意的是,specification 中注明了 RLB 被设计为一个调试机制,或者为启动过程提供便利和优化的设置。在生产环境中,一旦系统启动完成,软件就不应该再依赖 RLB 特性,否则可能带来安全隐患。 + +除了灵活性外,刚刚的设计中一旦提供 SMAP/SMEP 特性后,就无法让一段内存在所有权限等级中共享,这会对系统性能造成影响,比如无法提供零拷贝等功能。SMEPMP 希望在提供 SMAP/SMEP 的同时,安全地在不同权限等级之间共享内存,这部分设计体现在上图中的后三行。共享内存功能的核心设计思路是写权限和执行权限永远不共存,这也是系统中常见的安全设计原则。依据这个原则,对照图片就可以很好地理解如何进行 SMEPMP 的配置了,此处我们不进行繁杂的列举。 + +上图展示了内存访问命中 PMP 区域时的情况,下面我们讨论未命中时的情况,如图: + +![未命中 PMP 区域时 SMEPMP 作用效果图](images/20230626-rvsec-intro-part1/smepmp-miss.png) + +这里 SMEPMP 为 `mseccfg` 引入了 MMWP(M-mode 白名单策略,Machine Mode Whitelist Policy)比特。`MML=0, MMWP=1` 对应了此前不启用 SMEPMP 的情况。当 `MMWP=1`时,所有的权限等级访问都会被拒绝。当 `MML=1, MMWP=1` 时,则 S/U-mode 的访问会像此前一样直接被拒绝,而 M-mode 仅可以进行读写访问。 + +### SMEPMP 的现有支持 + +在软件方面,目前最新版本的 QEMU(v8.0.2)中,已经提供了对 SMEPMP 的支持(v0.9.3),但实现的版本是获得社区批准之前的版本,并且名称依然保留了 EPMP(增强 PMP,Enhanced PMP)的旧称。获得批准版本的实现需要等待后续开发。此外,暂时没有看到 Linux 中有关于 SMEPMP 的支持。 + +在硬件方面,RISC-V 社区官方的[博文](https://riscv.org/blog/2023/06/noel-v-processors-security-extensions-for-safe-and-secure-computing/)中表示,已经有硬件提供了 SMEPMP 的支持:[NOEL-V](https://www.gaisler.com/index.php/products/processors/noel-v) 处理器。但是在 NOEL-V 官方网站中,SMEPMP 仍处于开发路线中,暂未更新为已实现的特性,所以现有 NOEL-V 设备对 SMEPMP 的支持有待确认。 + +截至目前,未有使用 SMEPMP 的可信计算框架被提出。 + +## SMSTATEEN(状态启用拓展,State Enable Extension) + +隐蔽信道(convert channel)是一个安全领域的研究课题。它是指非特权程序之间通过一些无法被特权等级感知的手段进行通讯,比如某些寄存器中的位、设备的状态等等。这可能会使敏感信息在不知情的情况下被传出,也会对系统的安全造成一定隐患。SMSTATEEN 拓展是针对 RISC-V 各类拓展中提供的 CSR 这一潜在的隐蔽信道而设计的。简单来说,它统一提供了各种特性的开关,防止未启用的特性的 CSR 被用作隐蔽信道。此前 `mstatus.FS/VS` 等比特可以用于控制浮点、向量拓展的启用,但随着拓展变得繁多,使用 `mstatus` 管理所有拓展已经不现实,因此需要引入 SMSTATEEN 拓展。 + +SMSTATEEN 的机制和使用方式非常直接,它为 S/H/M-mode 每个特权等级提供了四个 `stateen` CSR,其中的比特位用于控制所有可选的拓展开关。具体的对应关系可以查看已获社区批准的 [specification](https://github.com/riscv/riscv-state-enable/releases/download/v1.0.0/Smstateen.pdf)。 + +与 SMEPMP 类似,NOEL-V 处理器和 QEMU 中提供了对 SMSTATEEN 的支持。暂时没有看到 Linux 及其他工作对此拓展的支持。 + +## 小结 + +本文介绍了 PMP、SMEPMP、SMSTATEEN 三个已获得批准的 RISC-V 安全拓展标准,并简要介绍了基于 PMP 实现的可信计算框架 Keystone。其中 PMP 是一个官方标准中一个可选的硬件拓展,SMEPMP 针对 PMP 进行了增强,提供了 SMAP/SMEP 保护能力,SMSTATEEN 相对较为简单,用于提供各类拓展的开关控制,以防止其被用于隐蔽信道。目前上游软件对 RISC-V 安全拓展的支持仍然较为初级,有待进一步开发。 + +## 参考资料 + +- [Recently Ratified RISC-V Extensions](https://wiki.riscv.org/display/HOME/Recently+Ratified+Extensions) +- [Extensions in Progress](https://wiki.riscv.org/display/HOME/Specification+Status) +- [NOEL-V Blog Post](https://riscv.org/blog/2023/06/noel-v-processors-security-extensions-for-safe-and-secure-computing/) +- [NOEL-V Webpage](https://www.gaisler.com/index.php/products/processors/noel-v) +- [SMSTATEEN Extension Specification](https://github.com/riscv/riscv-state-enable/releases/download/v1.0.0/Smstateen.pdf) \ No newline at end of file diff --git a/articles/images/20230626-rvsec-intro-part1/NAPOT-encoding.png b/articles/images/20230626-rvsec-intro-part1/NAPOT-encoding.png new file mode 100644 index 0000000000000000000000000000000000000000..e96ce828613a5c78e659f02746618a48d8cecda7 Binary files /dev/null and b/articles/images/20230626-rvsec-intro-part1/NAPOT-encoding.png differ diff --git a/articles/images/20230626-rvsec-intro-part1/RV32-pmpcfg-layout.png b/articles/images/20230626-rvsec-intro-part1/RV32-pmpcfg-layout.png new file mode 100644 index 0000000000000000000000000000000000000000..79e98139710c1f6f23b01bf3f3f71514577067a6 Binary files /dev/null and b/articles/images/20230626-rvsec-intro-part1/RV32-pmpcfg-layout.png differ diff --git a/articles/images/20230626-rvsec-intro-part1/RV64-pmpcfg-layout.png b/articles/images/20230626-rvsec-intro-part1/RV64-pmpcfg-layout.png new file mode 100644 index 0000000000000000000000000000000000000000..52ea5f63d63352caa7cdb297643842047eff9cb7 Binary files /dev/null and b/articles/images/20230626-rvsec-intro-part1/RV64-pmpcfg-layout.png differ diff --git a/articles/images/20230626-rvsec-intro-part1/keystone-arch.png b/articles/images/20230626-rvsec-intro-part1/keystone-arch.png new file mode 100644 index 0000000000000000000000000000000000000000..820e48a4fd911924d4c23c0e442e59c338dfd111 Binary files /dev/null and b/articles/images/20230626-rvsec-intro-part1/keystone-arch.png differ diff --git a/articles/images/20230626-rvsec-intro-part1/keystone-pmp.png b/articles/images/20230626-rvsec-intro-part1/keystone-pmp.png new file mode 100644 index 0000000000000000000000000000000000000000..620e0f2bf00ad0d576ee72e6c7622332a5cda9a5 Binary files /dev/null and b/articles/images/20230626-rvsec-intro-part1/keystone-pmp.png differ diff --git a/articles/images/20230626-rvsec-intro-part1/keystone-sm-api.png b/articles/images/20230626-rvsec-intro-part1/keystone-sm-api.png new file mode 100644 index 0000000000000000000000000000000000000000..62a0ab6ce0ed968322b7de17ce4b25479e064b54 Binary files /dev/null and b/articles/images/20230626-rvsec-intro-part1/keystone-sm-api.png differ diff --git a/articles/images/20230626-rvsec-intro-part1/pmpaddr.png b/articles/images/20230626-rvsec-intro-part1/pmpaddr.png new file mode 100644 index 0000000000000000000000000000000000000000..e7304eefa43e08b909507d2721c711a227be48ab Binary files /dev/null and b/articles/images/20230626-rvsec-intro-part1/pmpaddr.png differ diff --git a/articles/images/20230626-rvsec-intro-part1/pmpcfg-a.png b/articles/images/20230626-rvsec-intro-part1/pmpcfg-a.png new file mode 100644 index 0000000000000000000000000000000000000000..1b8bdf166c194f6e181d4a9fd4ee2d677c1a6bf9 Binary files /dev/null and b/articles/images/20230626-rvsec-intro-part1/pmpcfg-a.png differ diff --git a/articles/images/20230626-rvsec-intro-part1/pmpcfg.png b/articles/images/20230626-rvsec-intro-part1/pmpcfg.png new file mode 100644 index 0000000000000000000000000000000000000000..5a929922a3624f59edc5a2b1ee7892028c0a6142 Binary files /dev/null and b/articles/images/20230626-rvsec-intro-part1/pmpcfg.png differ diff --git a/articles/images/20230626-rvsec-intro-part1/smepmp-hit.png b/articles/images/20230626-rvsec-intro-part1/smepmp-hit.png new file mode 100644 index 0000000000000000000000000000000000000000..ae02ff56156758a369e07858075c55218434f5ab Binary files /dev/null and b/articles/images/20230626-rvsec-intro-part1/smepmp-hit.png differ diff --git a/articles/images/20230626-rvsec-intro-part1/smepmp-miss.png b/articles/images/20230626-rvsec-intro-part1/smepmp-miss.png new file mode 100644 index 0000000000000000000000000000000000000000..51bee68806e31844f84ed6bc2adefcc37a8db4f3 Binary files /dev/null and b/articles/images/20230626-rvsec-intro-part1/smepmp-miss.png differ