尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

AArch64 Linux 指针认证(Pointer Authentication)完整技术指南:从 PAC 原理到内核实现与 KVM 虚拟化

AArch64 Linux 指针认证(Pointer Authentication)完整技术指南:从 PAC 原理到内核实现与 KVM 虚拟化 AArch64 Linux 指针认证Pointer Authentication完整技术指南从 PAC 原理到内核实现与 KVM 虚拟化【免费下载链接】linuxLinux kernel source tree项目地址: https://gitcode.com/GitHub_Trending/li/linux本指南以 Linux 内核仓库中 Documentation/arch/arm64/pointer-authentication.rst 为骨架系统讲解 ARMv8.3 Pointer Authentication指针认证扩展在 AArch64 Linux 中的实现PAC 的生成与校验原理、五个密钥的划分、内核编译配置CONFIG_ARM64_PTR_AUTH与进程密钥管理prctl、调试用的 regset 接口以及 KVM 虚拟化中的启用与过滤机制。读完本文你将掌握如何在用户态程序中使用 PAC 指令与相关prctl接口、如何通过 PTRACE 获取 PAC 位掩码以及如何为 KVM 客户机配置指针认证能力。架构概述PAC 如何保护指针ARMv8.3 Pointer Authentication 扩展提供了可用于缓解一类攻击攻击者可以破坏某些内存内容例如栈的原语。其核心思想是使用指针认证码Pointer Authentication CodePAC来判定指针是否被意外修改。一个 PAC 由三个要素派生而来指针本身另一个值例如栈指针 SP保存在系统寄存器中的秘密密钥。扩展新增了两类指令一类用于向指针中插入有效的 PAC另一类用于校验/移除指针中的 PAC。PAC 占据指针的高位比特具体位数取决于内核配置的虚拟地址大小以及是否启用了指针标签pointer tagging。值得特别注意的是其中一部分指令从HINT 编码空间hint instruction encoding space分配而来。这意味着在缺少该扩展的硬件上或扩展被禁用时这些指令会表现得如同NOP一样。因此只要应用程序和库使用这些指令无论硬件是否支持该扩展程序都能正确运行——这正是 PAC 指令可以安全地打入用户态 ABI 的前提。扩展一共提供五个独立的密钥用于生成 PAC密钥用途APIAKey指令地址认证instruction addressA 变体APIBKey指令地址认证instruction addressB 变体APDAKey数据地址认证data addressA 变体APDBKey数据地址认证data addressB 变体APGAKey通用认证generic authentication其中两个密钥用于指令地址APIAKey、APIBKey两个用于数据地址APDAKey、APDBKey一个用于通用认证APGAKey。内核侧的用户态密钥结构在 arch/arm64/include/asm/pointer_auth.h 中定义包含apia、apib、apda、apdb、apga五个struct ptrauth_key字段。基本支持内核配置与进程密钥管理编译配置与运行时检测在 AArch64 内核中用户态指针认证支持由CONFIG_ARM64_PTR_AUTH控制其定义位于 arch/arm64/Kconfig该选项默认开启default y用于在EL0用户态启用指针认证指令开启后内核会在每次exec()时为进程初始化秘密密钥并在进程上下文切换时保存/恢复这些密钥功能是运行时检测的若硬件不支持则不会向用户态/KVM 客户机通告也不会启用若 boot CPU 具备该特性而某个晚期lateCPU 不具备该晚期 CPU 会被 parked停用反之若 boot CPU 不具备而晚期 CPU 具备晚期 CPU 仍可启动但特性被禁用。当CONFIG_ARM64_PTR_AUTH被选中且硬件支持时内核会在每次exec*()时为每个进程分配随机的密钥值。这些密钥由进程内的所有线程共享并且在fork()时原样保留给子进程。若进一步选中CONFIG_ARM64_PTR_AUTH_KERNELarch/arm64/Kconfig内核自身会使用 HINT 空间的指针认证指令来保护函数返回地址通过编译选项-mbranch-protection或-msign-return-address实现。用该选项构建的内核在支持或不支持指针认证的硬件上都能正常工作。HWCAP 通告地址认证address authentication能力通过HWCAP_PACA通告通用认证generic authentication能力通过HWCAP_PACG通告。两个位在 arch/arm64/include/uapi/asm/hwcap.h 中定义#define HWCAP_PACA (1 30) #define HWCAP_PACG (1UL 31)对应的字符串名称paca、pacg由 arch/arm64/kernel/cpuinfo.c 提供内核通过 arch/arm64/kernel/cpufeature.c 中的HWCAP_MULTI_CAP机制把硬件能力映射为对应的 HWCAP 位。应用程序可以通过读取辅助向量AT_HWCAP或使用getauxval(AT_HWCAP)来探测这两项能力。PAC 在指针中占据的位数PAC 在指针中占据的比特数为55 - 内核配置的虚拟地址大小例如当虚拟地址大小为 48 位时PAC 宽度为7 位55 - 48 7。这也是为什么文档强调 PAC 的位数会随虚拟地址大小和指针标签的启用情况而变化。通过 prctl 重置密钥除了exec()之外密钥还可以通过PR_PAC_RESET_KEYSprctl 重新初始化为随机值。该接口在内核侧由 arch/arm64/kernel/pointer_auth.c 中的ptrauth_prctl_reset_keys()实现传入的位掩码指定要重置哪些密钥传入0则表示重置全部密钥。密钥对应的位定义在 include/uapi/linux/prctl.h#define PR_PAC_RESET_KEYS 54 # define PR_PAC_APIAKEY (1UL 0) # define PR_PAC_APIBKEY (1UL 1) # define PR_PAC_APDAKEY (1UL 2) # define PR_PAC_APDBKEY (1UL 3) # define PR_PAC_APGAKEY (1UL 4)实现细节arch/arm64/kernel/pointer_auth.c若系统既不支持地址认证也不支持通用认证返回-EINVAL对 32 位 compat 线程返回-EINVALarg为 0 时调用ptrauth_keys_init_user()重置全部密钥arg包含未定义的位或请求的密钥类型在当前系统上不支持时返回-EINVAL对每个被请求的密钥通过get_random_bytes()填充新的随机值最后调用ptrauth_keys_install_user()写入密钥系统寄存器。调试通过 PTRACE 与 regset 获取 PAC 信息当CONFIG_ARM64_PTR_AUTH被选中且硬件支持地址认证时内核会通过NT_ARM_PAC_MASKregset对应struct user_pac_mask暴露 TTBR0 PAC 位的位置用户态可通过PTRACE_GETREGSET获取。相关 regset 编号定义在 include/uapi/linux/elf.h#define NT_ARM_PAC_MASK 0x406 /* ARM pointer authentication code masks */ #define NT_ARM_PACA_KEYS 0x407 /* ARM pointer authentication address keys */ #define NT_ARM_PACG_KEYS 0x408 /* ARM pointer authentication generic key */ #define NT_ARM_PAC_ENABLED_KEYS 0x40a /* arm64 ptr auth enabled keys (prctl()) */使用该接口时需要注意以下几点该 regset 仅在 HWCAP_PACA 置位时暴露数据指针和指令指针会分别暴露掩码因为二者的 PAC 位集合可能不同掩码仅适用于TTBR0地址空间不能直接应用到 TTBR1 地址例如内核指针。此外当同时配置了CONFIG_CHECKPOINT_RESTORE时内核还会暴露NT_ARM_PACA_KEYS与NT_ARM_PACG_KEYSregset对应struct user_pac_address_keys与struct user_pac_generic_keys。这两个 regset 可用于读取和设置某个线程的密钥为 CRIU 之类的检查点/恢复工具提供了密钥迁移能力。虚拟化在 KVM 客户机中启用指针认证启用方式在 KVM 中指针认证在每个虚拟 CPU 初始化时通过向KVM_ARM_VCPU_INIT传入两个标志来启用KVM_ARM_VCPU_PTRAUTH_ADDRESS地址认证KVM_ARM_VCPU_PTRAUTH_GENERIC通用认证两个标志的定义位于 arch/arm64/include/uapi/asm/kvm.h#define KVM_ARM_VCPU_PTRAUTH_ADDRESS 5 /* VCPU uses address authentication */ #define KVM_ARM_VCPU_PTRAUTH_GENERIC 6 /* VCPU uses generic authentication */当前的 KVM 客户机实现是同时启用两个特性的因此内核在启用指针认证前会同时检查这两个用户态标志。之所以保留两个独立的标志是为了在未来支持两个特性独立启用时避免用户态 ABI 发生变化。对 VHE 的依赖按照 Arm 架构规范指针认证特性是随VHEVirtualization Host Extensions特性一起实现的因此 KVM arm64 的 ptrauth 代码依赖 VHE 模式存在。换言之要为客户机启用指针认证宿主机必须运行在 VHE 模式下。未启用时的过滤与异常注入当这些 vcpu 特性标志未被设置时KVM 会执行以下过滤动作从KVM_GET/SET_REG_*ioctl 中过滤掉指针认证的系统密钥寄存器即这些寄存器对用户态不可见、不可写从cpufeature ID 寄存器中屏蔽mask对应的特性位使客户机在检查自身能力时看不到指针认证客户机中任何使用指针认证指令的尝试都会导致向客户机注入UNDEFINED 异常。这套机制保证了宿主机与客户机的能力协商是显式的、可控的未协商的客户机不会意外地使用到不受信任的密钥寄存器。启用与禁用密钥PR_PAC_SET_ENABLED_KEYS接口与示例prctlPR_PAC_SET_ENABLED_KEYS允许用户程序控制特定任务中哪些 PAC 密钥处于启用状态。它接收两个参数第一个参数PR_PAC_APIAKEY、PR_PAC_APIBKEY、PR_PAC_APDAKEY、PR_PAC_APDBKEY的位掩码指定本次调用影响哪些密钥第二个参数同样的位集合掩码指定这些密钥应被启用还是禁用置位表示启用清零表示禁用。原文档给出的示例下述调用禁用除 IB key 之外的所有密钥prctl(PR_PAC_SET_ENABLED_KEYS, PR_PAC_APIAKEY | PR_PAC_APIBKEY | PR_PAC_APDAKEY | PR_PAC_APDBKEY, PR_PAC_APIBKEY, 0, 0);该 prctl 对应的系统调用号PR_PAC_SET_ENABLED_KEYS 60以及配套的查询接口PR_PAC_GET_ENABLED_KEYS 61定义于 include/uapi/linux/prctl.h。内核实现原理内核侧的ptrauth_set_enabled_keys()arch/arm64/kernel/pointer_auth.c实现相当直观系统不支持地址认证、或调用者是 32 位 compat 线程时返回-EINVAL校验参数合法性keys不能超出PR_PAC_ENABLED_KEYS_MASK即 IA/IB/DA/DB 四位的集合enabled必须是keys的子集通过arg_to_enxx_mask()把用户态的密钥位映射为 SCTLR_EL1 中的控制位SCTLR_ELx_ENIA、SCTLR_ELx_ENIB、SCTLR_ELx_ENDA、SCTLR_ELx_ENDB更新任务结构体thread.sctlr_user中保存的用户态 SCTLR 值若目标是当前任务则立即调用update_sctlr_el1()写回硬件寄存器。对应的ptrauth_get_enabled_keys()arch/arm64/kernel/pointer_auth.c则反向把thread.sctlr_user中置位的SCTLR_ELx_EN*位翻译回PR_PAC_AP*KEY位掩码返回给用户态。SCTLR 用户态控制位快照的保存与恢复使每个任务可以拥有彼此独立的密钥启用状态。为什么需要这个接口新旧二进制的 ABI 互操作这个接口的主要价值在于启用一种使用 PAC 指令签名/认证函数指针及其他对外暴露指针的用户态 ABI同时让符合该 ABI 的二进制能与不签名、不认证指针的遗留二进制互相操作。具体做法是动态加载器dynamic loader或早期启动代码在确认进程可能加载遗留二进制之后、但在执行任何 PAC 指令之前尽早发出这个 prctl。这样新 ABI 程序可以按需关闭部分密钥从而与旧二进制安全共存。默认状态与继承规则为保证与旧内核版本的兼容性进程启动时IA、IB、DA、DB 四个密钥全部处于启用状态这一状态会在exec()时重置为默认值通过fork()和clone()创建的进程会继承调用进程的密钥启用状态内核实现上即为继承thread.sctlr_user快照。性能建议文档明确给出建议尽量避免禁用 IAAPIAKey密钥。因为与其他密钥相比禁用 IA 会带来更高的性能开销。这提示在设计与 PAC 相关的用户态 ABI 时优先保持指令地址认证处于开启状态。小结AArch64 Linux 对 ARMv8.3 指针认证的支持贯穿了用户态EL0、内核态EL1与虚拟化EL2三个层次用户态通过HWCAP_PACA/HWCAP_PACG探测能力通过PR_PAC_RESET_KEYS重置五把随机密钥通过PR_PAC_SET/GET_ENABLED_KEYS精细控制每把密钥的启用状态内核CONFIG_ARM64_PTR_AUTH负责 exec() 时初始化密钥并随进程上下文切换CONFIG_ARM64_PTR_AUTH_KERNEL用 PAC 指令保护内核自身的返回地址调试者可通过NT_ARM_PAC_MASK等 regset 经 PTRACE 获取 PAC 位掩码乃至读写线程密钥虚拟化KVM 通过KVM_ARM_VCPU_PTRAUTH_ADDRESS/KVM_ARM_VCPU_PTRAUTH_GENERIC两个标志为客户机启用指针认证当前需两个特性同时启用且依赖 VHE未协商的客户机则会被屏蔽特性、过滤密钥寄存器并在使用指令时收到 UNDEFINED 异常。由于 PAC 指令的 HINT 编码特性所有使用这些指令的程序在无扩展硬件上仍然能正常运行这为 PAC 作为跨硬件兼容的安全机制进入用户态 ABI 铺平了道路。【免费下载链接】linuxLinux kernel source tree项目地址: https://gitcode.com/GitHub_Trending/li/linux创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表