| // 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 0xc470 |
| 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 0xc470 |
| 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-NOT: good_explicit_check_unrelated_reg |
| 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 0xc470 |
| 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 |