指针认证与分支目标识别
本章规定可选的 Armv8.1-M 指针认证扩展与分支目标识别扩展规则。
1. 实现 PAC 与 BTI¶
PACBTI 扩展是 OPTIONAL 的。
适用于 Armv8.1-M 及以后架构版本的实现。扩展要求为 - PACBTI。
若实现了 PACBTI 扩展,ID_ISAR5.PACBTI 将读出为非零值。
适用于 Armv8.1-M 及以后架构版本的实现。扩展要求为 - PACBTI。
PAC 与 BTI 特性可以针对每个安全状态与特权级别独立启用或禁用。
适用于 Armv8.1-M 及以后架构版本的实现。扩展要求为 - PACBTI。
1.1 指针认证¶
PAC 可按如下方式启用或禁用:
CONTROL.PAC_EN,用于特权模式。CONTROL.UPAC_EN,用于非特权模式。
伪代码函数 PACEnabled() 描述了这一点。
适用于 Armv8.1-M 及以后架构版本的实现。扩展要求为 - PACBTI。
在特权模式下,CONTROL.PAC_EN 与 CONTROL.UPAC_EN 可以使用 MSR 与 MRS 指令访问。
适用于 Armv8.1-M 及以后架构版本的实现。扩展要求为 - PACBTI。
在非特权模式下,CONTROL.UPAC_EN 可以使用 MRS 指令读取。
适用于 Armv8.1-M 及以后架构版本的实现。扩展要求为 - PACBTI。
指针认证使用四个 128 位密钥:
| Non-secure 状态 | Secure 状态 | |
|---|---|---|
| 特权 | PAC_KEY_P_NS |
PAC_KEY_P_S |
| 非特权 | PAC_KEY_U_NS |
PAC_KEY_U_S |
特权软件可以通过 MSR 与 MRS 指令访问这些密钥,其中:
- 寄存器值
PAC_KEY_P_3:PAC_KEY_P_2:PAC_KEY_P_1:PAC_KEY_P_0拼接构成PAC_KEY_P。 - 寄存器值
PAC_KEY_U_3:PAC_KEY_U_2:PAC_KEY_U_1:PAC_KEY_U_0拼接构成PAC_KEY_U。
适用于 Armv8.1-M 及以后架构版本的实现。扩展要求为 - PACBTI。
对于指针认证指令,加密值的生成方式是 IMPLEMENTATION DEFINED 的,可以是以下之一:
- QARMA 算法的 5 轮变体,称为 QARMA5,如 The QARMA Block Cipher Family(Roberto Avanzi,Qualcomm Product Security Initiative)所述,此时
ID_ISAR5.PACBTI的值为0b0001。 - 一种 IMPLEMENTATION DEFINED 算法,此时
ID_ISAR5.PACBTI的值为0b0010。 - QARMA 算法的 3 轮变体,称为 QARMA3,如 The QARMA Block Cipher Family(Roberto Avanzi,Qualcomm Product Security Initiative)所述,此时
ID_ISAR5.PACBTI的值为0b0100。
适用于 Armv8.1-M 及以后架构版本的实现。扩展要求为 - PACBTI。
当使用 IMPLEMENTATION DEFINED 算法时,适用以下规则:
- 该算法必须使用与
ComputePAC()函数相同的输入参数。 - 在以下情况下,该算法必须给出相同的结果:
- 传入该算法的参数(寄存器中的值与密钥)相同。
- PE 执行于相同的安全与特权级别。
适用于 Armv8.1-M 及以后架构版本的实现。扩展要求为 - PACBTI。
允许 IMPLEMENTATION DEFINED 算法依据 PE 的安全与特权级别施加额外的密码学轮次。在不同安全与特权级别使用相同的输入参数与密钥值,可能产生不同的结果。
适用于 Armv8.1-M 及以后架构版本的实现。扩展要求为 - PACBTI。
以下函数描述了 PACBTI 扩展所支持的 QARMA 算法:
ComputePAC()PACCellInvShuffle()PACCellShuffle()PACInvSub()PACMult()PACSubQ3()PACSubQ5()RotCell()TweakCellInvRot()TweakCellRot()TweakInvShuffle()TweakShuffle()
Arm 建议指针认证使用 QARMA 算法族。
适用于 Armv8.1-M 及以后架构版本的实现。扩展要求为 - PACBTI。
PAC 由 PACBTI、PAC 与 PACG 指令生成。
适用于 Armv8.1-M 及以后架构版本的实现。扩展要求为 - PACBTI。
伪代码函数 ComputePAC() 的输入参数为:
- 32 位指针/地址/数据的最高位零扩展到 64 位。
- 32 位修饰符(modifier)的最高位零扩展到 64 位。
- 128 位密码学密钥。
伪代码函数 CreatePAC() 描述了这一点。
适用于 Armv8.1-M 及以后架构版本的实现。扩展要求为 - PACBTI。
若 PAC 认证指令未能验证 PAC,则产生 INVSTATE UsageFault。若用于生成 PAC 的任一输入参数在验证 PAC 之前被修改,即三个通用寄存器之一或密码学密钥被修改,则会产生该错误。AUT、BXAUT 与 AUTG 指令认证 PAC。
适用于 Armv8.1-M 及以后架构版本的实现。扩展要求为 - PACBTI。
当 PACEnabled() 为 FALSE 时:
- PAC 生成指令不创建 PAC,也不更新任何寄存器。这些指令提供的其他功能不受影响。
- PAC 认证指令不验证 PAC,也不产生 INVSTATE UsageFault。这些指令提供的其他功能不受影响。
适用于 Armv8.1-M 及以后架构版本的实现。扩展要求为 - PACBTI。
密码学算法的输出是 32 位 PAC,因此不同的输入参数与密钥组合有可能产生完全相同的 PAC。这类密码学碰撞(cryptographic collision)无法被 PAC 认证指令检测到,也不会产生 INVSTATE UsageFault。
适用于 Armv8.1-M 及以后架构版本的实现。扩展要求为 - PACBTI。
PACBTI 扩展不区分特权 Thread 模式执行与 Handler 模式执行。例如,在 Handler 模式下完成认证的指针,可以在特权 Thread 模式下通过验证,而不产生 INVSTATE UsageFault。
适用于 Armv8.1-M 及以后架构版本的实现。扩展要求为 - PACBTI。
对于以下两对指令之间可以执行的指令数量,没有架构上的限制:
- 生成 PAC 的指令与验证 PAC 的指令之间。例如
PAC与AUT。 - 验证 PAC 的指令与返回指令之间。例如
AUT与BX。
适用于 Armv8.1-M 及以后架构版本的实现。扩展要求为 - PACBTI。
Arm 建议:在指针通过认证之后与对该指针的任何后续引用之间,不应将未保护的指针保存到内存再恢复。
适用于 Armv8.1-M 及以后架构版本的实现。扩展要求为 - PACBTI。
下面是使用 PAC 的示例。
1 LDR R4, =func1 // Load function address.
2 BLX R4 // Call function which is protected by PAC-AUT.
3 ret1:
4 MOV R1, #AA // #AA value stored in R1 register.
5 MOV R2, #BB // #BB value stored in R2 register.
6 LDR R4, =func2
7 BLX R4 // Call function which is protected by PAC-AUT.
8 ret2:
9 AUTG R0, R1, R2 // Validate R0, R1, and R2 registers. If any of the
10 // values or the secret key have changed, then an
11 // INVSTATE UsageFault is generated.
12 // Not supported on legacy implementations.
13 ...
14 func1:
15 PAC R12, LR, SP // Computes the cryptographic code using LR, SP, and
16 // a secret key, then stores it in R12.
17 // This instruction works on legacy implementations.
18 PUSH {R4-R6, R12, LR} // Stack LR if it needs to be used before the AUT.
19 ... // Function body.
20 POP {R4-R6, R12, LR} // Restore the value of LR before authenticating.
21 AUT R12, LR, SP // Computes PAC using LR, SP, and a secret key, and
22 // then compares it against the value in R12.
23 // If the two values match then the LR is validated,
24 // otherwise an INVSTATE UsageFault is generated.
25 // This instruction works on legacy implementations.
26 BX LR // Return to 'ret1'.
27 func2:
28 PAC R12, LR, SP // Calculate PAC for LR.
29 ...
30 PACG R0, R1, R2 // Calculate PAC where desired value is in R1 (#AA).
31 // The modifier value is in R2 (#BB).
32 // Computed PAC is stored in R0 register.
33 // Not supported on legacy implementations.
34 ... // Function body.
35 BXAUT R12, LR, SP // Validate LR and branch to LR ('ret2').
36 // Not supported on legacy implementations.
适用于 Armv8.1-M 及以后架构版本的实现。扩展要求为 - PACBTI。
参见:
- 第 B3.4 节《专用 CONTROL 寄存器》(Special-purpose CONTROL register)。
1.2 分支目标识别¶
BTI 可按如下方式启用或禁用:
CONTROL.BTI_EN,用于特权模式。CONTROL.UBTI_EN,用于非特权模式。
伪代码函数 BTIEnabled() 描述了这一点。
适用于 Armv8.1-M 及以后架构版本的实现。扩展要求为 - PACBTI。
在特权模式下,CONTROL.BTI_EN 与 CONTROL.UBTI_EN 可以使用 MSR 与 MRS 指令访问。
适用于 Armv8.1-M 及以后架构版本的实现。扩展要求为 - PACBTI。
在非特权模式下,CONTROL.UBTI_EN 可以使用 MRS 指令读取。
适用于 Armv8.1-M 及以后架构版本的实现。扩展要求为 - PACBTI。
当针对目标安全与特权状态启用了 BTI 时,任何将 EPSR.B 或 LO_BRANCH_INFO.BTI 置 1 的指令都称为 BTI 置位指令(BTI setting instruction)。BTIEnabled() 描述了这一点。
适用于 Armv8.1-M 及以后架构版本的实现。扩展要求为 - PACBTI。
任何将 EPSR.B 清 0 的指令都称为 BTI 清零指令(BTI clearing instruction)。
适用于 Armv8.1-M 及以后架构版本的实现。扩展要求为 - PACBTI。
异常进入时,EPSR.B 被自动压栈并清 0;异常返回时被恢复。
适用于 Armv8.1-M 及以后架构版本的实现。扩展要求为 - PACBTI。
EPSR.B 不作为 FNC_RETURN 栈帧的一部分被压栈并清 0。
适用于 Armv8.1-M 及以后架构版本的实现。扩展要求为 - PACBTI。
若针对目标安全状态与特权级别启用了 BTI,则在执行以下任一 BTI 置位指令时,EPSR.B 被置 1:
BLX。BLXNS。- 当持有分支地址的寄存器不是 LR 时:
BX。BXNS。- 当地址被加载到 PC 时:
LDR(寄存器)。LDR(字面量)。- 当地址被加载到 PC 且基地址寄存器不是 SP,或者基地址寄存器是 SP 但不发生 SP 回写时:
LDR(立即数)。LDM、LDMIA、LDMFD。LDMDB、LDMEA。
适用于 Armv8.1-M 及以后架构版本的实现。扩展要求为 - PACBTI。
在执行以下任一 BTI 置位指令时,LO_BRANCH_INFO.BTI 位被置 1:
BFLX。- 当持有分支地址的寄存器不是 LR 时:
BFX。
适用于 Armv8.1-M 及以后架构版本的实现。扩展要求为 - PACBTI。
当 BTI 置位的 Branch Future 分支被采用时,若以下全部条件成立,则 EPSR.B 被置 1:
BTIEnabled()为 TRUE。LO_BRANCH_INFO.BTI被置 1。LO_BRANCH_INFO.VALID被置 1。
适用于 Armv8.1-M 及以后架构版本的实现。扩展要求为 - PACBTI。
伪代码函数 HandleLO()、GetInstrExecState() 与 InstructionAdvance() 展示了 BTI 置位的 Branch Future 指令如何将 EPSR.B 置 1。
适用于 Armv8.1-M 及以后架构版本的实现。扩展要求为 - PACBTI。
任何不是 BTI 置位、且将 LO_BRANCH_INFO.VALID 位置 1 的指令,也必须将 LO_BRANCH_INFO.BTI 位清 0。例如 LE 与 LETP 指令。
适用于 Armv8.1-M 及以后架构版本的实现。扩展要求为 - PACBTI。
BXAUT 指令不是 BTI 置位指令,无论使用哪个寄存器作为分支目标。通过 AUT 或 AUTG 指令后接 BX 指令,可以实现带 BTI 置位的等价行为。
适用于 Armv8.1-M 及以后架构版本的实现。扩展要求为 - PACBTI。
POP(单寄存器)是 LDR(立即数)的别名,POP(多寄存器)是 LDM、LDMIA、LDMFD 的别名,它们都不是 BTI 置位指令。
适用于 Armv8.1-M 及以后架构版本的实现。扩展要求为 - PACBTI。
对于从 Non-secure 状态到 Secure 状态的转换,若 SG 指令被 BTI 置位分支作为目标,则由与该分支特权级别相关联的 CONTROL_NS.BTI_EN 与 CONTROL_NS.UBTI_EN 位决定 EPSR.B 是否被置位。
SG 指令是 BTI 清零的,因此若 EPSR.B 已置位,即使 BTI 未启用,它也会被清零。
适用于 Armv8.1-M 及以后架构版本的实现。扩展要求为 - PACBTI && S。
对于从 Secure 状态到 Non-secure 状态的转换,若该转换由 BTI 置位分支引起,例如 BXNS,则将检查与目标 Non-secure 状态特权级别相关联的 CONTROL_NS.BTI_EN 与 CONTROL_NS.UBTI_EN 位,以决定 EPSR.B 是否被置位。
适用于 Armv8.1-M 及以后架构版本的实现。扩展要求为 - PACBTI && S。
以下指令是 BTI 清零的:
BTI。SG。PACBTI。
若 EPSR.B 已置位,即使 BTI 未启用,BTI 清零指令也会将其清 0。
适用于 Armv8.1-M 及以后架构版本的实现。扩展要求为 - PACBTI。
当 EPSR.B 置位时,下一条执行的指令必须是 BTI 清零指令或 BKPT 指令,否则会产生 INVSTATE UsageFault。
若下一条指令是 BTI 清零指令,则 EPSR.B 被清零。若下一条指令是 BKPT 指令,则 EPSR.B 不被清零并保持置位,且不产生 INVSTATE UsageFault。
适用于 Armv8.1-M 及以后架构版本的实现。扩展要求为 - PACBTI。
当需要 PAC 功能但不希望使用 PACBTI 创建着陆垫(landing pad)时,可以使用 PAC 或 PACG 指令。
适用于 Armv8.1-M 及以后架构版本的实现。扩展要求为 - PACBTI。
下面是使用 BTI 的示例。
1 // Current Security state is Non-secure
2 LDR R4, =testBti // Load function address.
3 LDR R3, =ret1
4 BLX R4 // BTI is enabled for Non-secure state, set EPSR.B.
5 ret1:
6 BTI // BTI clearing instruction.
7 LDR R4, =funcTestBti
8 BLX R4 // Set EPSR.B.
9 ret2:
10 LDR R4, =nscBti
11 BLX R4 // Target address is in NSC region, set EPSR.B.
12 end:
13 ...
14 // Function in Non-secure state.
15 testBti:
16 BTI // BTI clearing instruction, valid entry point.
17 ... // LR not used for return, so PAC-AUT is not required.
18 ... // But using LR would be recommended in this scenario.
19 BX R3 // Branch to 'ret1'. LR is not used, so the branch
20 // target must be a BTI clearing instruction.
21 // Function in Non-secure state.
22 funcTestBti:
23 PACBTI R12, LR, SP // BTI clearing instruction, valid entry point.
24 ... // Stacking and unstacking R12, LR, and SP is safe
25 // as any corruption will be detected by BXAUT.
26 BXAUT R12, LR, SP // Validate LR and return to 'ret2', address in LR.
27 // The combined authenticate and branch instruction
28 // is not backwards compatible with legacy software.
29 // Function in Non-secure state.
30 leafFunc:
31 BTI // Since BTI is enabled, a BTI clearing instruction
32 ... // is required.
33 BX leafRet // Does not set BTI, uses FNC_RETURN.
34 ...
35 // Secure and Non-secure callable region.
36 nscBti:
37 SG // Clear BTI, regardless of the Secure BTI settings.
38 B secure // Call Secure function.
39 ...
40 // Function in Secure state.
41 secure:
42 PAC R12, LR, SP // BTI clearing PAC is not required.
43 ...
44 LDR R6, =leafFunc
45 BLXNS R6 // Call Non-secure function.
46 // Instruction implicitly checks BTI settings for the
47 // current privilege state in CONTROL_NS.
48 leafRet:
49 ... // BTI clearing instruction not required.
50 AUT R12, LR, SP // Validate LR.
51 ... // Stacking and unstacking LR and SP is not safe.
52 ... // Corruption of the registers will not be detected.
53 BXNS LR // Branch to 'end'. Does not set BTI.
适用于 Armv8.1-M 及以后架构版本的实现。扩展要求为 - PACBTI。