v10 2026-11-09

基于pkeys的页表只读加固 · v10

v10 系列围绕 kpkeys 为 arm64 实现 POE 权限覆盖、POR_EL1 上下文切换及页表保护。

原始补丁链接

子 Patch(22)

1/22
mm: Introduce kpkeys
新增 kpkeys_enter_context()/leave_context() 与 kpkeys_state,通过架构钩子保存、切换和恢复 pkeys 权限,并提供 guard 对象宏。
2/22
set_memory: Introduce set_direct_map_pkey() stub
为 direct map 范围新增 set_direct_map_pkey(),用于按页设置 pkey;未实现 kpkeys 的架构使用空 stub,避免调用点条件编译。
3/22
arm64: Introduce por_elx_set_pkey_perms() helper
新增 por_elx_set_pkey_perms(),用 field_prep() 按 pkey 索引编码 POR_ELx 权限位,并重构 arch_set_user_pkey_access() 复用它。
4/22
arm64: Implement asm/kpkeys.h using POE
基于 POE 实现 arm64 kpkeys 钩子:enter 时按上下文改 POR_EL1 并保存旧值,leave 按需恢复;pkey 0 保持 RWX 以兼容代码映射。
5/22
arm64: set_memory: Implement set_direct_map_pkey()
在 arm64 pageattr 中用 POE 实现 set_direct_map_pkey(),遍历目标 direct map 范围并修改 PTE 的 pkey/POIndex 权限。
6/22
arm64: Disable EL2 trapping of POR_EL1
设置 HFGRT{R,W}_EL2 的 nPOR_EL1 位,避免 EL2 陷入内核访问 POR_EL1,使 kpkeys 的权限切换在 EL1 直接生效。
7/22
arm64: Initialize POR_EL1 register on cpu_resume()
cpu_suspend() 后 POR_EL1 值未知;在 cpu_do_resume() 前于 sleep.S 将其初始化为 POR_EL1_INIT,保证唤醒后 kpkeys 默认权限正确。
8/22
arm64: Context-switch POR_EL1
将 POR_EL1 加入线程上下文切换:保存旧值、恢复新值,并在值变化时执行 ISB,防止 kpkeys 临时窗口内调度导致权限泄漏。
9/22
arm64: mm: Enable overlays for all EL1 indirect permissions
在 PIR_EL1 中为内核内存类型启用 Overlay applied,使 POR_EL1 权限覆盖所有内核映射;用户内存类型保持不叠加,避免特权访问用户内存被误检。
10/22
arm64: Enable kpkeys
设置 TCR2_EL1.POE 启用 EL1 POE,初始化 POR_EL1 允许默认 pkey 0,并加 ISB 使限制立即生效;随后选择 ARCH_HAS_KPKEYS。
11/22
mm: kpkeys: Introduce kpkeys_hardened_pgtables feature
引入 kpkeys_hardened_pgtables 配置与架构 opt-in、arch_supports_kpkeys_early(),为后续用特权 pkey 映射页表且仅在特权上下文授权写做准备。
12/22
mm: kpkeys: Protect regular page tables
新增 kpkeys_pgtable_alloc()/free(),在 pagetable_alloc()/free() 中把页表页线性映射切到 KPKEYS_PKEY_PGTABLES 或恢复默认 pkey,保护常规页表。
13/22
mm: kpkeys: Protect early kernel page tables
启用特性时遍历已安装内核页表,用 pagewalk 的 pmd_entry 回调把其指向的 PTE 表页在 direct map 中设为 KPKEYS_PKEY_PGTABLES,覆盖早期页表。
14/22
mm: kpkeys: Protect vmemmap page tables
让后期 vmemmap 页表通过 pagetable_alloc() 分配以获得 pkey 保护,并让 arm64 热插拔路径用 pagetable_free() 释放以恢复默认 pkey。
15/22
arm64: kpkeys: Implement arch_supports_kpkeys_early()
实现 arm64 的 arch_supports_kpkeys_early():在 CPU 特性检测前直接读 boot CPU 的 POE 支持,之后则查 POE 特性,供早期页表布局决策。
16/22
arm64: kpkeys: Support KPKEYS_CTX_PGTABLES
在 arm64 的 kpkeys 上下文权限中,为 KPKEYS_PKEY_PGTABLES 授予写权限仅当进入 KPKEYS_CTX_PGTABLES,否则保持只读。
17/22
arm64: kpkeys: Ensure the linear map can be modified
让 can_set_direct_map() 与 force_pte_mapping() 在 kpkeys_hardened_pgtables 启用时返回真,确保 linear map 可按页改 pkey 且全 PTE 映射。
18/22
arm64: kpkeys: Guard page table writes
在写页表处加入 kpkeys guard,临时切到 KPKEYS_CTX_PGTABLES 允许写 PTE;原子操作循环扩大 guard 范围,配置关闭时为空操作。
19/22
arm64: kpkeys: Batch KPKEYS_CTX_PGTABLES switches
利用 lazy MMU 批量切换:进入时切到 KPKEYS_CTX_PGTABLES、退出恢复,区间内不再改 POR_EL1;用 thread_struct 暂存 kpkeys_state。
20/22
arm64: kpkeys: Enable kpkeys_hardened_pgtables support
在 arm64 的 Kconfig 中,当 ARM64_POE 启用时选择 ARCH_HAS_KPKEYS_HARDENED_PGTABLES,正式声明支持该页表加固特性。
21/22
arm64: mm: Map kernel image alias of init_pg_dir read-only
把 init_pg_dir 的内核镜像别名映射为只读,防止代码绕过 kpkeys 对 linear map 的页表写保护,强制页表修改走受控路径。
22/22
mm: Add basic tests for kpkeys_hardened_pgtables
新增 builtin kunit 测试,尝试直接写内核与用户页表项并断言失败,覆盖早期/常规内核页表等不同分配保护路径。