
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
793 lines
27 KiB
ArmAsm
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
|