[llvm] [BOLT] Gadget scanner: detect authentication oracles (PR #135663)
Kristof Beyls via llvm-commits
llvm-commits at lists.llvm.org
Wed May 28 02:22:41 PDT 2025
================
@@ -0,0 +1,739 @@
+// 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, at function
+sym:
+ ret
+ .size sym, .-sym
+
+ .globl callee
+ .type callee, at function
+callee:
+ ret
+ .size callee, .-callee
+
+ .globl good_ret
+ .type good_ret, at function
+good_ret:
+// CHECK-NOT: good_ret
+ autia x0, x1
+ ret x0
+ .size good_ret, .-good_ret
+
+ .globl good_call
+ .type good_call, at 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, at 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, at 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, at 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, at 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, at 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, at 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, at 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
+ mul x3, x0, x1
+ ldr x2, [x0]
+ ret
+ .size bad_unknown_usage_read, .-bad_unknown_usage_read
+
+ .globl bad_unknown_usage_subreg_read
+ .type bad_unknown_usage_subreg_read, at 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, at 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_unknown_overwrite
+ .type good_unknown_overwrite, at function
+good_unknown_overwrite:
+// CHECK-NOT: good_unknown_overwrite
+ 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_unknown_wreg_overwrite
+ .type good_unknown_wreg_overwrite, at function
+good_unknown_wreg_overwrite:
+// CHECK-LABEL: GS-PAUTH: authentication oracle found in function good_unknown_wreg_overwrite, basic block {{[^,]+}}, at address
+// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: autia x0, x1
+ autia x0, x1
+ mul w0, w1, w2
+ ret
+ .size good_unknown_wreg_overwrite, .-good_unknown_wreg_overwrite
+
+ .globl good_address_arith
+ .type good_address_arith, at 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, at 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, at 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, at 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, at 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, at 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, at 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, at 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, at 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, at 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, at 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, at 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_unknown_overwrite_multi_bb
+ .type good_unknown_overwrite_multi_bb, at function
+good_unknown_overwrite_multi_bb:
+// CHECK-NOT: good_unknown_overwrite_multi_bb
+ autia x0, x1
+ cbz x3, 1f
+1:
+ mul x0, x1, x2
+ ret
+ .size good_unknown_overwrite_multi_bb, .-good_unknown_overwrite_multi_bb
+
+ .globl good_address_arith_multi_bb
+ .type good_address_arith_multi_bb, at 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, at 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, at 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, at function
+good_branch_nocfg:
+// CHECK-NOT: good_branch_nocfg
+ paciasp
+ adr x2, 1f
+ br x2
+1:
+ autia x0, x1
+ autiasp // authenticate LR before tail call
+ ldr x2, [x30] // check LR before tail call
+ br x0
+ .size good_branch_nocfg, .-good_branch_nocfg
----------------
kbeyls wrote:
Since LR/x30 is not saved to memory in this function, I would've expected there to not be `paciasp`/`autiasp` instructions here typically. Does having these `paciasp`/`autiasp` instructions here add value for the test case?
https://github.com/llvm/llvm-project/pull/135663
More information about the llvm-commits
mailing list