[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