llvm-project/bolt/test/binary-analysis/AArch64/gs-pauth-authentication-oracles.s
Anatoly Trosinenko a8a2c6fa88
[BOLT] Gadget scanner: fix LR to be safe in leaf functions without CFG (#141824)
After a label in a function without CFG information, use a reasonably
pessimistic estimation of register state (assume that any register that
can be clobbered in this function was actually clobbered) instead of the
most pessimistic "all registers are unsafe". This is the same estimation
as used by the dataflow variant of the analysis when the preceding
instruction is not known for sure.

Without this, leaf functions without CFG information are likely to have
false positive reports about non-protected return instructions, as
1) LR is unlikely to be signed and authenticated in a leaf function and
2) LR is likely to be used by a return instruction near the end of the
   function and
3) the register state is likely to be reset at least once during the
   linear scan through the function
2025-06-25 13:11:23 +03:00

793 lines
27 KiB
ArmAsm

// RUN: %clang %cflags -march=armv8.3-a %s -o %t.exe
// RUN: llvm-bolt-binary-analysis --scanners=pacret %t.exe 2>&1 | FileCheck -check-prefix=PACRET %s
// RUN: llvm-bolt-binary-analysis --scanners=pauth %t.exe 2>&1 | FileCheck %s
// The detection of compiler-generated explicit pointer checks is tested in
// gs-pauth-address-checks.s, for that reason only test here "dummy-load" and
// "high-bits-notbi" checkers, as the shortest examples of checkers that are
// detected per-instruction and per-BB.
// PACRET-NOT: authentication oracle found in function
.text
.type sym,@function
sym:
ret
.size sym, .-sym
.globl callee
.type callee,@function
callee:
ret
.size callee, .-callee
.globl good_ret
.type good_ret,@function
good_ret:
// CHECK-NOT: good_ret
autia x0, x1
ret x0
.size good_ret, .-good_ret
.globl good_call
.type good_call,@function
good_call:
// CHECK-NOT: good_call
paciasp
stp x29, x30, [sp, #-16]!
mov x29, sp
autia x0, x1
blr x0
ldp x29, x30, [sp], #16
autiasp
ret
.size good_call, .-good_call
.globl good_branch
.type good_branch,@function
good_branch:
// CHECK-NOT: good_branch
autia x0, x1
br x0
.size good_branch, .-good_branch
.globl good_load_other_reg
.type good_load_other_reg,@function
good_load_other_reg:
// CHECK-NOT: good_load_other_reg
autia x0, x1
ldr x2, [x0]
ret
.size good_load_other_reg, .-good_load_other_reg
.globl good_load_same_reg
.type good_load_same_reg,@function
good_load_same_reg:
// CHECK-NOT: good_load_same_reg
autia x0, x1
ldr x0, [x0]
ret
.size good_load_same_reg, .-good_load_same_reg
.globl good_explicit_check
.type good_explicit_check,@function
good_explicit_check:
// CHECK-NOT: good_explicit_check
autia x0, x1
eor x16, x0, x0, lsl #1
tbz x16, #62, 1f
brk 0x1234
1:
ret
.size good_explicit_check, .-good_explicit_check
.globl bad_unchecked
.type bad_unchecked,@function
bad_unchecked:
// CHECK-LABEL: GS-PAUTH: authentication oracle found in function bad_unchecked, basic block {{[^,]+}}, at address
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: autia x0, x1
// CHECK-NEXT: The 0 instructions that leak the affected registers are:
autia x0, x1
ret
.size bad_unchecked, .-bad_unchecked
.globl bad_leaked_to_subroutine
.type bad_leaked_to_subroutine,@function
bad_leaked_to_subroutine:
// CHECK-LABEL: GS-PAUTH: authentication oracle found in function bad_leaked_to_subroutine, basic block {{[^,]+}}, at address
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: autia x0, x1
// CHECK-NEXT: The 1 instructions that leak the affected registers are:
// CHECK-NEXT: 1. {{[0-9a-f]+}}: bl callee
// CHECK-NEXT: This happens in the following basic block:
// CHECK-NEXT: {{[0-9a-f]+}}: paciasp
// CHECK-NEXT: {{[0-9a-f]+}}: stp x29, x30, [sp, #-0x10]!
// CHECK-NEXT: {{[0-9a-f]+}}: mov x29, sp
// CHECK-NEXT: {{[0-9a-f]+}}: autia x0, x1
// CHECK-NEXT: {{[0-9a-f]+}}: bl callee
// CHECK-NEXT: {{[0-9a-f]+}}: ldr x2, [x0]
// CHECK-NEXT: {{[0-9a-f]+}}: ldp x29, x30, [sp], #0x10
// CHECK-NEXT: {{[0-9a-f]+}}: autiasp
// CHECK-NEXT: {{[0-9a-f]+}}: ret
paciasp
stp x29, x30, [sp, #-16]!
mov x29, sp
autia x0, x1
bl callee
ldr x2, [x0]
ldp x29, x30, [sp], #16
autiasp
ret
.size bad_leaked_to_subroutine, .-bad_leaked_to_subroutine
.globl bad_unknown_usage_read
.type bad_unknown_usage_read,@function
bad_unknown_usage_read:
// CHECK-LABEL: GS-PAUTH: authentication oracle found in function bad_unknown_usage_read, basic block {{[^,]+}}, at address
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: autia x0, x1
// CHECK-NEXT: The 1 instructions that leak the affected registers are:
// CHECK-NEXT: 1. {{[0-9a-f]+}}: mul x3, x0, x1
// CHECK-NEXT: This happens in the following basic block:
// CHECK-NEXT: {{[0-9a-f]+}}: autia x0, x1
// CHECK-NEXT: {{[0-9a-f]+}}: mul x3, x0, x1
// CHECK-NEXT: {{[0-9a-f]+}}: ldr x2, [x0]
// CHECK-NEXT: {{[0-9a-f]+}}: ret
autia x0, x1
// Registers are not accessible to an attacker under Pointer
// Authentication threat model, until spilled to memory.
// Thus, reporting the below MUL instruction is a false positive, since
// the next LDR instruction prevents any possible spilling of x3 unless
// the authentication succeeded. Though, rejecting anything except for
// a closed list of instruction types is the intended behavior of the
// analysis, so this false positive is by design.
mul x3, x0, x1
ldr x2, [x0]
ret
.size bad_unknown_usage_read, .-bad_unknown_usage_read
.globl bad_store_to_memory_and_wait
.type bad_store_to_memory_and_wait,@function
bad_store_to_memory_and_wait:
// CHECK-LABEL: GS-PAUTH: authentication oracle found in function bad_store_to_memory_and_wait, basic block {{[^,]+}}, at address
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: autia x0, x1
// CHECK-NEXT: The 1 instructions that leak the affected registers are:
// CHECK-NEXT: 1. {{[0-9a-f]+}}: str x0, [x3]
autia x0, x1
cbz x3, 2f
str x0, [x3]
1:
// The thread performs a time-consuming computation while the result of
// authentication is accessible in memory.
nop
2:
ldr x2, [x0]
ret
.size bad_store_to_memory_and_wait, .-bad_store_to_memory_and_wait
// FIXME: Known false negative: if no return instruction is reachable from a
// program point (this probably implies an infinite loop), such
// instruction cannot be detected as an authentication oracle.
.globl bad_store_to_memory_and_hang
.type bad_store_to_memory_and_hang,@function
bad_store_to_memory_and_hang:
// CHECK-NOT: bad_store_to_memory_and_hang
autia x0, x1
cbz x3, 2f
str x0, [x3]
1:
// The thread loops indefinitely while the result of authentication
// is accessible in memory.
b 1b
2:
ldr x2, [x0]
ret
.size bad_store_to_memory_and_hang, .-bad_store_to_memory_and_hang
.globl bad_unknown_usage_subreg_read
.type bad_unknown_usage_subreg_read,@function
bad_unknown_usage_subreg_read:
// CHECK-LABEL: GS-PAUTH: authentication oracle found in function bad_unknown_usage_subreg_read, basic block {{[^,]+}}, at address
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: autia x0, x1
// CHECK-NEXT: The 1 instructions that leak the affected registers are:
// CHECK-NEXT: 1. {{[0-9a-f]+}}: mul w3, w0, w1
// CHECK-NEXT: This happens in the following basic block:
// CHECK-NEXT: {{[0-9a-f]+}}: autia x0, x1
// CHECK-NEXT: {{[0-9a-f]+}}: mul w3, w0, w1
// CHECK-NEXT: {{[0-9a-f]+}}: ldr x2, [x0]
// CHECK-NEXT: {{[0-9a-f]+}}: ret
autia x0, x1
mul w3, w0, w1
ldr x2, [x0]
ret
.size bad_unknown_usage_subreg_read, .-bad_unknown_usage_subreg_read
.globl bad_unknown_usage_update
.type bad_unknown_usage_update,@function
bad_unknown_usage_update:
// CHECK-LABEL: GS-PAUTH: authentication oracle found in function bad_unknown_usage_update, basic block {{[^,]+}}, at address
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: autia x0, x1
// CHECK-NEXT: The 1 instructions that leak the affected registers are:
// CHECK-NEXT: 1. {{[0-9a-f]+}}: movk x0, #0x2a, lsl #16
// CHECK-NEXT: This happens in the following basic block:
// CHECK-NEXT: {{[0-9a-f]+}}: autia x0, x1
// CHECK-NEXT: {{[0-9a-f]+}}: movk x0, #0x2a, lsl #16
// CHECK-NEXT: {{[0-9a-f]+}}: ldr x2, [x0]
// CHECK-NEXT: {{[0-9a-f]+}}: ret
autia x0, x1
movk x0, #42, lsl #16 // does not overwrite x0 completely
ldr x2, [x0]
ret
.size bad_unknown_usage_update, .-bad_unknown_usage_update
.globl good_overwrite_with_constant
.type good_overwrite_with_constant,@function
good_overwrite_with_constant:
// CHECK-NOT: good_overwrite_with_constant
autia x0, x1
mov x0, #42
ret
.size good_overwrite_with_constant, .-good_overwrite_with_constant
// Overwriting sensitive data by instructions with unmodelled side-effects is
// explicitly rejected, even though this particular MRS is safe.
.globl bad_overwrite_with_side_effects
.type bad_overwrite_with_side_effects,@function
bad_overwrite_with_side_effects:
// CHECK-LABEL: GS-PAUTH: authentication oracle found in function bad_overwrite_with_side_effects, basic block {{[^,]+}}, at address
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: autia x0, x1
// CHECK-NEXT: The 0 instructions that leak the affected registers are:
autia x0, x1
mrs x0, CTR_EL0
ret
.size bad_overwrite_with_side_effects, .-bad_overwrite_with_side_effects
// Here the new value written by MUL to x0 is completely unrelated to the result
// of authentication, so this is a false positive.
// FIXME: Can/should we generalize overwriting by constant to handle such cases?
.globl good_unknown_overwrite
.type good_unknown_overwrite,@function
good_unknown_overwrite:
// CHECK-LABEL: GS-PAUTH: authentication oracle found in function good_unknown_overwrite, basic block {{[^,]+}}, at address
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: autia x0, x1
// CHECK-NEXT: The 0 instructions that leak the affected registers are:
autia x0, x1
mul x0, x1, x2
ret
.size good_unknown_overwrite, .-good_unknown_overwrite
// This is a false positive: when a general-purpose register is written to as
// a 32-bit register, its top 32 bits are zeroed, but according to LLVM
// representation, the instruction only overwrites the Wn register.
.globl good_wreg_overwrite
.type good_wreg_overwrite,@function
good_wreg_overwrite:
// CHECK-LABEL: GS-PAUTH: authentication oracle found in function good_wreg_overwrite, basic block {{[^,]+}}, at address
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: autia x0, x1
autia x0, x1
mov w0, #42
ret
.size good_wreg_overwrite, .-good_wreg_overwrite
.globl good_address_arith
.type good_address_arith,@function
good_address_arith:
// CHECK-NOT: good_address_arith
autia x0, x1
add x1, x0, #8
sub x2, x1, #16
mov x3, x2
ldr x4, [x3]
mov x0, #0
mov x1, #0
mov x2, #0
ret
.size good_address_arith, .-good_address_arith
.globl good_ret_multi_bb
.type good_ret_multi_bb,@function
good_ret_multi_bb:
// CHECK-NOT: good_ret_multi_bb
autia x0, x1
cbz x1, 1f
nop
1:
ret x0
.size good_ret_multi_bb, .-good_ret_multi_bb
.globl good_call_multi_bb
.type good_call_multi_bb,@function
good_call_multi_bb:
// CHECK-NOT: good_call_multi_bb
paciasp
stp x29, x30, [sp, #-16]!
mov x29, sp
autia x0, x1
cbz x1, 1f
nop
1:
blr x0
cbz x1, 2f
nop
2:
ldp x29, x30, [sp], #16
autiasp
ret
.size good_call_multi_bb, .-good_call_multi_bb
.globl good_branch_multi_bb
.type good_branch_multi_bb,@function
good_branch_multi_bb:
// CHECK-NOT: good_branch_multi_bb
autia x0, x1
cbz x1, 1f
nop
1:
br x0
.size good_branch_multi_bb, .-good_branch_multi_bb
.globl good_load_other_reg_multi_bb
.type good_load_other_reg_multi_bb,@function
good_load_other_reg_multi_bb:
// CHECK-NOT: good_load_other_reg_multi_bb
autia x0, x1
cbz x1, 1f
nop
1:
ldr x2, [x0]
cbz x1, 2f
nop
2:
ret
.size good_load_other_reg_multi_bb, .-good_load_other_reg_multi_bb
.globl good_load_same_reg_multi_bb
.type good_load_same_reg_multi_bb,@function
good_load_same_reg_multi_bb:
// CHECK-NOT: good_load_same_reg_multi_bb
autia x0, x1
cbz x1, 1f
nop
1:
ldr x0, [x0]
cbz x1, 2f
nop
2:
ret
.size good_load_same_reg_multi_bb, .-good_load_same_reg_multi_bb
.globl good_explicit_check_multi_bb
.type good_explicit_check_multi_bb,@function
good_explicit_check_multi_bb:
// CHECK-NOT: good_explicit_check_multi_bb
autia x0, x1
cbz x1, 1f
nop
1:
eor x16, x0, x0, lsl #1
tbz x16, #62, 2f
brk 0x1234
2:
cbz x1, 3f
nop
3:
ret
.size good_explicit_check_multi_bb, .-good_explicit_check_multi_bb
.globl bad_unchecked_multi_bb
.type bad_unchecked_multi_bb,@function
bad_unchecked_multi_bb:
// CHECK-LABEL: GS-PAUTH: authentication oracle found in function bad_unchecked_multi_bb, basic block {{[^,]+}}, at address
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: autia x0, x1
// CHECK-NEXT: The 0 instructions that leak the affected registers are:
autia x0, x1
cbz x1, 1f
ldr x2, [x0]
1:
ret
.size bad_unchecked_multi_bb, .-bad_unchecked_multi_bb
.globl bad_leaked_to_subroutine_multi_bb
.type bad_leaked_to_subroutine_multi_bb,@function
bad_leaked_to_subroutine_multi_bb:
// CHECK-LABEL: GS-PAUTH: authentication oracle found in function bad_leaked_to_subroutine_multi_bb, basic block {{[^,]+}}, at address
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: autia x0, x1
// CHECK-NEXT: The 1 instructions that leak the affected registers are:
// CHECK-NEXT: 1. {{[0-9a-f]+}}: bl callee
paciasp
stp x29, x30, [sp, #-16]!
mov x29, sp
autia x0, x1
cbz x1, 1f
ldr x2, [x0]
1:
bl callee
ldr x2, [x0]
ldp x29, x30, [sp], #16
autiasp
ret
.size bad_leaked_to_subroutine_multi_bb, .-bad_leaked_to_subroutine_multi_bb
.globl bad_unknown_usage_read_multi_bb
.type bad_unknown_usage_read_multi_bb,@function
bad_unknown_usage_read_multi_bb:
// CHECK-LABEL: GS-PAUTH: authentication oracle found in function bad_unknown_usage_read_multi_bb, basic block {{[^,]+}}, at address
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: autia x0, x1
// CHECK-NEXT: The 1 instructions that leak the affected registers are:
// CHECK-NEXT: 1. {{[0-9a-f]+}}: mul x3, x0, x1
autia x0, x1
cbz x3, 1f
mul x3, x0, x1
1:
ldr x2, [x0]
ret
.size bad_unknown_usage_read_multi_bb, .-bad_unknown_usage_read_multi_bb
.globl bad_unknown_usage_subreg_read_multi_bb
.type bad_unknown_usage_subreg_read_multi_bb,@function
bad_unknown_usage_subreg_read_multi_bb:
// CHECK-LABEL: GS-PAUTH: authentication oracle found in function bad_unknown_usage_subreg_read_multi_bb, basic block {{[^,]+}}, at address
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: autia x0, x1
// CHECK-NEXT: The 1 instructions that leak the affected registers are:
// CHECK-NEXT: 1. {{[0-9a-f]+}}: mul w3, w0, w1
autia x0, x1
cbz x3, 1f
mul w3, w0, w1
1:
ldr x2, [x0]
ret
.size bad_unknown_usage_subreg_read_multi_bb, .-bad_unknown_usage_subreg_read_multi_bb
.globl bad_unknown_usage_update_multi_bb
.type bad_unknown_usage_update_multi_bb,@function
bad_unknown_usage_update_multi_bb:
// CHECK-LABEL: GS-PAUTH: authentication oracle found in function bad_unknown_usage_update_multi_bb, basic block {{[^,]+}}, at address
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: autia x0, x1
// CHECK-NEXT: The 1 instructions that leak the affected registers are:
// CHECK-NEXT: 1. {{[0-9a-f]+}}: movk x0, #0x2a, lsl #16
autia x0, x1
cbz x3, 1f
movk x0, #42, lsl #16 // does not overwrite x0 completely
1:
ldr x2, [x0]
ret
.size bad_unknown_usage_update_multi_bb, .-bad_unknown_usage_update_multi_bb
.globl good_overwrite_with_constant_multi_bb
.type good_overwrite_with_constant_multi_bb,@function
good_overwrite_with_constant_multi_bb:
// CHECK-NOT: good_overwrite_with_constant_multi_bb
autia x0, x1
cbz x3, 1f
1:
mov x0, #42
ret
.size good_overwrite_with_constant_multi_bb, .-good_overwrite_with_constant_multi_bb
.globl good_address_arith_multi_bb
.type good_address_arith_multi_bb,@function
good_address_arith_multi_bb:
// CHECK-NOT: good_address_arith_multi_bb
autia x0, x1
cbz x3, 1f
add x1, x0, #8
sub x2, x1, #16
mov x0, x2
mov x1, #0
mov x2, #0
1:
ldr x3, [x0]
ret
.size good_address_arith_multi_bb, .-good_address_arith_multi_bb
.globl good_ret_nocfg
.type good_ret_nocfg,@function
good_ret_nocfg:
// CHECK-NOT: good_ret_nocfg
adr x2, 1f
br x2
1:
autia x0, x1
ret x0
.size good_ret_nocfg, .-good_ret_nocfg
.globl good_call_nocfg
.type good_call_nocfg,@function
good_call_nocfg:
// CHECK-NOT: good_call_nocfg
paciasp
stp x29, x30, [sp, #-16]!
mov x29, sp
adr x2, 1f
br x2
1:
autia x0, x1
blr x0
ldp x29, x30, [sp], #16
autiasp
ret
.size good_call_nocfg, .-good_call_nocfg
.globl good_branch_nocfg
.type good_branch_nocfg,@function
good_branch_nocfg:
// CHECK-NOT: good_branch_nocfg
adr x2, 1f
br x2
1:
autia x0, x1
br x0
.size good_branch_nocfg, .-good_branch_nocfg
.globl good_load_other_reg_nocfg
.type good_load_other_reg_nocfg,@function
good_load_other_reg_nocfg:
// CHECK-NOT: good_load_other_reg_nocfg
adr x2, 1f
br x2
1:
autia x0, x1
ldr x2, [x0]
ret
.size good_load_other_reg_nocfg, .-good_load_other_reg_nocfg
.globl good_load_same_reg_nocfg
.type good_load_same_reg_nocfg,@function
good_load_same_reg_nocfg:
// CHECK-NOT: good_load_same_reg_nocfg
adr x2, 1f
br x2
1:
autia x0, x1
ldr x0, [x0]
ret
.size good_load_same_reg_nocfg, .-good_load_same_reg_nocfg
// FIXME: Multi-instruction checker sequences are not supported without CFG.
.globl bad_unchecked_nocfg
.type bad_unchecked_nocfg,@function
bad_unchecked_nocfg:
// CHECK-LABEL: GS-PAUTH: authentication oracle found in function bad_unchecked_nocfg, at address
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: autia x0, x1
// CHECK-NEXT: The 0 instructions that leak the affected registers are:
adr x2, 1f
br x2
1:
autia x0, x1
ret
.size bad_unchecked_nocfg, .-bad_unchecked_nocfg
.globl bad_leaked_to_subroutine_nocfg
.type bad_leaked_to_subroutine_nocfg,@function
bad_leaked_to_subroutine_nocfg:
// CHECK-LABEL: GS-PAUTH: authentication oracle found in function bad_leaked_to_subroutine_nocfg, at address
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: autia x0, x1
// CHECK-NEXT: The 1 instructions that leak the affected registers are:
// CHECK-NEXT: 1. {{[0-9a-f]+}}: bl callee # Offset: 24
paciasp
stp x29, x30, [sp, #-16]!
mov x29, sp
adr x2, 1f
br x2
1:
autia x0, x1
bl callee
ldr x2, [x0]
ldp x29, x30, [sp], #16
autiasp
ret
.size bad_leaked_to_subroutine_nocfg, .-bad_leaked_to_subroutine_nocfg
.globl bad_unknown_usage_read_nocfg
.type bad_unknown_usage_read_nocfg,@function
bad_unknown_usage_read_nocfg:
// CHECK-LABEL: GS-PAUTH: authentication oracle found in function bad_unknown_usage_read_nocfg, at address
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: autia x0, x1
// CHECK-NEXT: The 1 instructions that leak the affected registers are:
// CHECK-NEXT: 1. {{[0-9a-f]+}}: mul x3, x0, x1
adr x2, 1f
br x2
1:
autia x0, x1
mul x3, x0, x1
ldr x2, [x0]
ret
.size bad_unknown_usage_read_nocfg, .-bad_unknown_usage_read_nocfg
.globl bad_unknown_usage_subreg_read_nocfg
.type bad_unknown_usage_subreg_read_nocfg,@function
bad_unknown_usage_subreg_read_nocfg:
// CHECK-LABEL: GS-PAUTH: authentication oracle found in function bad_unknown_usage_subreg_read_nocfg, at address
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: autia x0, x1
// CHECK-NEXT: The 1 instructions that leak the affected registers are:
// CHECK-NEXT: 1. {{[0-9a-f]+}}: mul w3, w0, w1
adr x2, 1f
br x2
1:
autia x0, x1
mul w3, w0, w1
ldr x2, [x0]
ret
.size bad_unknown_usage_subreg_read_nocfg, .-bad_unknown_usage_subreg_read_nocfg
.globl bad_unknown_usage_update_nocfg
.type bad_unknown_usage_update_nocfg,@function
bad_unknown_usage_update_nocfg:
// CHECK-LABEL: GS-PAUTH: authentication oracle found in function bad_unknown_usage_update_nocfg, at address
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: autia x0, x1
// CHECK-NEXT: The 1 instructions that leak the affected registers are:
// CHECK-NEXT: 1. {{[0-9a-f]+}}: movk x0, #0x2a, lsl #16
adr x2, 1f
br x2
1:
autia x0, x1
movk x0, #42, lsl #16 // does not overwrite x0 completely
ldr x2, [x0]
ret
.size bad_unknown_usage_update_nocfg, .-bad_unknown_usage_update_nocfg
.globl good_overwrite_with_constant_nocfg
.type good_overwrite_with_constant_nocfg,@function
good_overwrite_with_constant_nocfg:
// CHECK-NOT: good_overwrite_with_constant_nocfg
adr x2, 1f
br x2
1:
autia x0, x1
mov x0, #42
ret
.size good_overwrite_with_constant_nocfg, .-good_overwrite_with_constant_nocfg
.globl good_address_arith_nocfg
.type good_address_arith_nocfg,@function
good_address_arith_nocfg:
// CHECK-NOT: good_address_arith_nocfg
adr x2, 1f
br x2
1:
autia x0, x1
add x1, x0, #8
sub x2, x1, #16
mov x3, x2
ldr x4, [x3]
mov x0, #0
mov x1, #0
mov x2, #0
ret
.size good_address_arith_nocfg, .-good_address_arith_nocfg
.globl good_explicit_check_unrelated_reg
.type good_explicit_check_unrelated_reg,@function
good_explicit_check_unrelated_reg:
// CHECK-LABEL: GS-PAUTH: authentication oracle found in function good_explicit_check_unrelated_reg, basic block {{[^,]+}}, at address
// FIXME: The below instruction is not an authentication oracle
autia x2, x3 // One of possible execution paths after this instruction
// ends at BRK below, thus BRK used as a trap instruction
// should formally "check everything" not to introduce
// false-positive here.
autia x0, x1
eor x16, x0, x0, lsl #1
tbz x16, #62, 1f
brk 0x1234
1:
ldr x4, [x2] // Right before this instruction X2 is checked - this
// should be propagated to the basic block ending with
// TBZ instruction above.
ret
.size good_explicit_check_unrelated_reg, .-good_explicit_check_unrelated_reg
// The last BB (in layout order) is processed first by the data-flow analysis.
// Its initial state is usually filled in a special way (because it ends with
// `ret` instruction), and then affects the state propagated to the other BBs
// Thus, the case of the last instruction in a function being a jump somewhere
// in the middle is special.
.globl good_no_ret_from_last_bb
.type good_no_ret_from_last_bb,@function
good_no_ret_from_last_bb:
// CHECK-NOT: good_no_ret_from_last_bb
paciasp
autiasp // authenticates LR
b 2f
1:
ret
2:
b 1b // LR is dereferenced by `ret`, which is executed next
.size good_no_ret_from_last_bb, .-good_no_ret_from_last_bb
.globl bad_no_ret_from_last_bb
.type bad_no_ret_from_last_bb,@function
bad_no_ret_from_last_bb:
// CHECK-LABEL: GS-PAUTH: authentication oracle found in function bad_no_ret_from_last_bb, basic block {{[^,]+}}, at address
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: autiasp
// CHECK-NEXT: The 0 instructions that leak the affected registers are:
paciasp
autiasp // authenticates LR
b 2f
1:
ret x0
2:
b 1b // X0 (but not LR) is dereferenced by `ret x0`
.size bad_no_ret_from_last_bb, .-bad_no_ret_from_last_bb
// Test that combined auth+something instructions are not reported as
// authentication oracles.
.globl inst_retaa
.type inst_retaa,@function
inst_retaa:
// CHECK-NOT: inst_retaa
paciasp
retaa
.size inst_retaa, .-inst_retaa
.globl inst_blraa
.type inst_blraa,@function
inst_blraa:
// CHECK-NOT: inst_blraa
paciasp
stp x29, x30, [sp, #-16]!
mov x29, sp
blraa x0, x1
ldp x29, x30, [sp], #16
retaa
.size inst_blraa, .-inst_blraa
.globl inst_braa
.type inst_braa,@function
inst_braa:
// CHECK-NOT: inst_braa
braa x0, x1
.size inst_braa, .-inst_braa
.globl inst_ldraa_no_wb
.type inst_ldraa_no_wb,@function
inst_ldraa_no_wb:
// CHECK-NOT: inst_ldraa_no_wb
ldraa x1, [x0]
ret
.size inst_ldraa_no_wb, .-inst_ldraa_no_wb
.globl inst_ldraa_wb
.type inst_ldraa_wb,@function
inst_ldraa_wb:
// CHECK-NOT: inst_ldraa_wb
ldraa x1, [x0]!
ret
.size inst_ldraa_wb, .-inst_ldraa_wb
.globl main
.type main,@function
main:
mov x0, 0
ret
.size main, .-main