AArch64 Linux 中的指针认证¶
作者:Mark Rutland <mark.rutland@arm.com>
日期:2017-07-19
本文档简要介绍了 AArch64 Linux 中提供的指针认证功能。
架构概述¶
ARMv8.3 指针认证扩展添加了一些原语,可用于缓解某些攻击,其中攻击者可以破坏某些内存的内容(例如堆栈)。
该扩展使用指针认证码 (PAC) 来确定指针是否被意外修改。 PAC 来自指针、另一个值(例如堆栈指针)以及系统寄存器中保存的密钥。
该扩展添加了将有效 PAC 插入指针以及验证/从指针中删除 PAC 的指令。 PAC 占用指针的多个高位,这取决于配置的虚拟地址大小以及是否正在使用指针标记。
这些指令的一个子集已从 HINT 编码空间分配。 在没有扩展(或禁用时),这些指令的行为类似于 NOP。 使用这些指令的应用程序和库无论是否存在扩展,都可以正常运行。
该扩展提供了五个单独的密钥来生成 PAC - 两个用于指令地址(APIAKey、APIBKey),两个用于数据地址(APDAKey、APDBKey)和一个用于通用身份验证(APGAKey)。
基本支持¶
当选择 CONFIG_ARM64_PTR_AUTH 并且存在相关的硬件支持时,内核将在 exec*() 时为每个进程分配随机密钥值。 这些密钥由进程内的所有线程共享,并在 fork() 时保留。
地址认证功能的出现通过 HWCAP_PACA 进行通告,通用认证功能通过 HWCAP_PACG 进行通告。
PAC 在指针中占用的位数是 55 减去内核配置的虚拟地址大小。 例如,在虚拟地址大小为 48 的情况下,PAC 的宽度为 7 位。
当选择 ARM64_PTR_AUTH_KERNEL 时,内核将使用 HINT 空间指针认证指令编译,以保护函数返回。 使用此选项构建的内核将在具有或不具有指针认证支持的硬件上工作。
除了 exec() 之外,还可以使用 PR_PAC_RESET_KEYS prctl 将密钥重新初始化为随机值。 PR_PAC_APIAKEY、PR_PAC_APIBKEY、PR_PAC_APDAKEY、PR_PAC_APDBKEY 和 PR_PAC_APGAKEY 的位掩码指定要重新初始化的密钥; 指定 0 表示“所有密钥”。
调试¶
当选择 CONFIG_ARM64_PTR_AUTH 并且存在地址认证的硬件支持时,内核将在 NT_ARM_PAC_MASK regset (struct user_pac_mask) 中公开 TTBR0 PAC 位的的位置,用户空间可以通过 PTRACE_GETREGSET 获取该位置。
仅当设置了 HWCAP_PACA 时,才公开 regset。 数据指针和指令指针会公开单独的掩码,因为两者的 PAC 位集可能不同。 请注意,这些掩码应用于 TTBR0 地址,并且不适用于 TTBR1 地址(例如,内核指针)。
此外,当也设置了 CONFIG_CHECKPOINT_RESTORE 时,内核将公开 NT_ARM_PACA_KEYS 和 NT_ARM_PACG_KEYS regset(结构 user_pac_address_keys 和 struct user_pac_generic_keys)。 这些可用于获取和设置线程的密钥。
虚拟化¶
当每个虚拟 CPU 通过传递标志 KVM_ARM_VCPU_PTRAUTH_[ADDRESS/GENERIC] 并请求启用这两个单独的 CPU 功能进行初始化时,KVM 来宾中启用指针身份验证。 当前的 KVM 来宾实现通过一起启用这两个功能来工作,因此在启用指针身份验证之前会检查这两个用户空间标志。 如果将来添加支持以允许这两个功能彼此独立地启用,则单独的用户空间标志将允许不进行用户空间 ABI 更改。
由于 Arm 架构指定指针认证功能与 VHE 功能一起实现,因此 KVM arm64 ptrauth 代码依赖于 VHE 模式的存在。
此外,当未设置这些 vcpu 功能标志时,KVM 将从 KVM_GET/SET_REG_* ioctl 中筛选出指针认证系统密钥寄存器,并从 cpufeature ID 寄存器中屏蔽这些功能。 任何使用指针认证指令的尝试都会导致将 UNDEFINED 异常注入到来宾中。
启用和禁用密钥¶
prctl PR_PAC_SET_ENABLED_KEYS 允许用户程序控制在特定任务中启用哪些 PAC 密钥。 它接受两个参数,第一个参数是 PR_PAC_APIAKEY、PR_PAC_APIBKEY、PR_PAC_APDAKEY 和 PR_PAC_APDBKEY 的位掩码,指定此 prctl 应影响哪些密钥,第二个参数是相同位的位掩码,指定是否应启用或禁用密钥。 例如
prctl(PR_PAC_SET_ENABLED_KEYS,
PR_PAC_APIAKEY | PR_PAC_APIBKEY | PR_PAC_APDAKEY | PR_PAC_APDBKEY,
PR_PAC_APIBKEY, 0, 0);
禁用除 IB 密钥之外的所有密钥。
这有用的主要原因是启用使用 PAC 指令签名和验证函数指针以及函数外部公开的其他指针的用户空间 ABI,同时仍允许符合 ABI 的二进制文件与不签名或验证指针的旧二进制文件互操作。
想法是,动态加载器或早期启动代码会在确定进程可能会加载旧二进制文件之后,但在执行任何 PAC 指令之前,很早发出此 prctl。
为了与以前的内核版本兼容,进程启动时启用 IA、IB、DA 和 DB,并在 exec() 上重置为此状态。 通过 fork() 和 clone() 创建的进程从调用进程继承密钥启用状态。
建议避免禁用 IA 密钥,因为这比禁用任何其他密钥的性能开销更高。