MPK(转载)
MPK 的目标是提供每个 core 独立的内存访问权限管理。使用 MPK 之前,首先要将内存分组,最多分成 16 组。这 16 组的二进制编号 0b0000 ~ 0b1111
被称为 protection key。MPK 使用 x86 page table 中的 4 个 unused bits 来 label 分组信息:比如,如果这 4 个 bits 被设置成 0b1001
,那么这个 page 就属于第 9 组。
分组之后,MPK 的使用就无需页表参与了。CPU 的每个 core 都有一个 32-bit 的寄存器(PKRU)来储存当前 core 对每个组的访问权限。PKRU 寄存器中包含 16 对 bit pair,对应 16 个 page group,每个 pair 包含两个 bit, 分别代表 R / W 权限。每个 pair 的取值对应三种可能的权限 (0, 0): read/write,(1, 0): read-only,(x, 1): no-access。如下图所示:
注:尽管图中的 Group 15 和 Group 14 中 page 都是连续的,但是这并不是必须的。)
PKRU 寄存器可以通过 WRPKRU 和 RDPKRU 这两条指令来写入/读取。
到此为止 MPK 看起来很像 Arm 中的 TZASC 或者 RISC-V 中的 PMP。但是 MPK 和它们有一个重大区别:WRPKRU 和 RDPKRU 都不是特权指令!也就是说,在 ring 3(User space)中就可以直接运行这两条指令,来修改 MPK 的设置。
因为这个 PKRU 不是由特权指令读写的,MPK 能够提供的安全性其实是相当有限的,那要它还有什么用呢?MPK 的优点在于快,进行一次 PKRU 读写只需要 20 cycles 左右,相比进行一次 context switch 来说,PKRU 的切换非常快速。
因此 MPK 的目标并不是去防御尽可能多的攻击,而是以非常廉价的开销去防御一些特定类别的攻击。