| // -Wl,--no-relax prevents converting ADRP+ADD pairs into NOP+ADR. |
| // RUN: %clang %cflags -march=armv8.3-a -Wl,--no-relax %s -o %t.exe |
| // RUN: llvm-bolt-binary-analysis --scanners=pauth %t.exe 2>&1 | FileCheck %s |
| |
| // Test various patterns that should or should not be considered safe |
| // materialization of PC-relative addresses. |
| // |
| // Note that while "instructions that write to the affected registers" |
| // section of the report is still technically correct, it does not necessarily |
| // mention the instructions that are used incorrectly. |
| |
| .text |
| |
| // Define a function that is reachable by ADR instruction. |
| .type sym,@function |
| sym: |
| ret |
| .size sym, .-sym |
| |
| .globl good_adr |
| .type good_adr,@function |
| good_adr: |
| // CHECK-NOT: good_adr |
| adr x0, sym |
| paciza x0 |
| ret |
| .size good_adr, .-good_adr |
| |
| .globl good_adrp |
| .type good_adrp,@function |
| good_adrp: |
| // CHECK-NOT: good_adrp |
| adrp x0, sym |
| paciza x0 |
| ret |
| .size good_adrp, .-good_adrp |
| |
| .globl good_adrp_add |
| .type good_adrp_add,@function |
| good_adrp_add: |
| // CHECK-NOT: good_adrp_add |
| adrp x0, sym |
| add x0, x0, :lo12:sym |
| paciza x0 |
| ret |
| .size good_adrp_add, .-good_adrp_add |
| |
| .globl good_adrp_add_with_const_offset |
| .type good_adrp_add_with_const_offset,@function |
| good_adrp_add_with_const_offset: |
| // CHECK-NOT: good_adrp_add_with_const_offset |
| adrp x0, sym |
| add x0, x0, :lo12:sym |
| add x0, x0, #8 |
| paciza x0 |
| ret |
| .size good_adrp_add_with_const_offset, .-good_adrp_add_with_const_offset |
| |
| .globl bad_adrp_with_nonconst_offset |
| .type bad_adrp_with_nonconst_offset,@function |
| bad_adrp_with_nonconst_offset: |
| // CHECK-LABEL: GS-PAUTH: signing oracle found in function bad_adrp_with_nonconst_offset, basic block {{[^,]+}}, at address |
| // CHECK-NEXT: The instruction is {{[0-9a-f]+}}: paciza x0 |
| // CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are: |
| // CHECK-NEXT: 1. {{[0-9a-f]+}}: add x0, x0, x1 |
| // CHECK-NEXT: This happens in the following basic block: |
| // CHECK-NEXT: {{[0-9a-f]+}}: adrp x0, #{{.*}} |
| // CHECK-NEXT: {{[0-9a-f]+}}: add x0, x0, x1 |
| // CHECK-NEXT: {{[0-9a-f]+}}: paciza x0 |
| // CHECK-NEXT: {{[0-9a-f]+}}: ret |
| adrp x0, sym |
| add x0, x0, x1 |
| paciza x0 |
| ret |
| .size bad_adrp_with_nonconst_offset, .-bad_adrp_with_nonconst_offset |
| |
| .globl bad_split_adrp |
| .type bad_split_adrp,@function |
| bad_split_adrp: |
| // CHECK-LABEL: GS-PAUTH: signing oracle found in function bad_split_adrp, basic block {{[^,]+}}, at address |
| // CHECK-NEXT: The instruction is {{[0-9a-f]+}}: paciza x0 |
| // CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are: |
| // CHECK-NEXT: 1. {{[0-9a-f]+}}: add x0, x0, #0x{{[0-9a-f]+}} |
| // CHECK-NEXT: This happens in the following basic block: |
| // CHECK-NEXT: {{[0-9a-f]+}}: add x0, x0, #0x{{[0-9a-f]+}} |
| // CHECK-NEXT: {{[0-9a-f]+}}: paciza x0 |
| // CHECK-NEXT: {{[0-9a-f]+}}: ret |
| cbz x2, 1f |
| adrp x0, sym |
| 1: |
| add x0, x0, :lo12:sym |
| paciza x0 |
| ret |
| .size bad_split_adrp, .-bad_split_adrp |
| |
| // Materialization of absolute addresses is not handled, as it is not expected |
| // to be used by real-world code, but can be supported if needed. |
| |
| .globl bad_immediate_constant |
| .type bad_immediate_constant,@function |
| bad_immediate_constant: |
| // CHECK-LABEL: GS-PAUTH: signing oracle found in function bad_immediate_constant, basic block {{[^,]+}}, at address |
| // CHECK-NEXT: The instruction is {{[0-9a-f]+}}: paciza x0 |
| // CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are: |
| // CHECK-NEXT: 1. {{[0-9a-f]+}}: mov x0, #{{.*}} |
| // CHECK-NEXT: This happens in the following basic block: |
| // CHECK-NEXT: {{[0-9a-f]+}}: mov x0, #{{.*}} |
| // CHECK-NEXT: {{[0-9a-f]+}}: paciza x0 |
| // CHECK-NEXT: {{[0-9a-f]+}}: ret |
| movz x0, #1234 |
| paciza x0 |
| ret |
| .size bad_immediate_constant, .-bad_immediate_constant |
| |
| // Any ADR or ADRP instruction followed by any number of increments/decrements |
| // by constant is considered safe. |
| |
| .globl good_adr_with_add |
| .type good_adr_with_add,@function |
| good_adr_with_add: |
| // CHECK-NOT: good_adr_with_add |
| adr x0, sym |
| add x0, x0, :lo12:sym |
| paciza x0 |
| ret |
| .size good_adr_with_add, .-good_adr_with_add |
| |
| .globl good_adrp_with_add_non_consecutive |
| .type good_adrp_with_add_non_consecutive,@function |
| good_adrp_with_add_non_consecutive: |
| // CHECK-NOT: good_adrp_with_add_non_consecutive |
| adrp x0, sym |
| mul x1, x2, x3 |
| add x0, x0, :lo12:sym |
| paciza x0 |
| ret |
| .size good_adrp_with_add_non_consecutive, .-good_adrp_with_add_non_consecutive |
| |
| .globl good_many_offsets |
| .type good_many_offsets,@function |
| good_many_offsets: |
| // CHECK-NOT: good_many_offsets |
| adrp x0, sym |
| add x1, x0, #8 |
| add x2, x1, :lo12:sym |
| paciza x2 |
| ret |
| .size good_many_offsets, .-good_many_offsets |
| |
| .globl good_negative_offset |
| .type good_negative_offset,@function |
| good_negative_offset: |
| // CHECK-NOT: good_negative_offset |
| adr x0, sym |
| sub x1, x0, #8 |
| paciza x1 |
| ret |
| .size good_negative_offset, .-good_negative_offset |
| |
| // MOV Xd, Xm (which is an alias of ORR Xd, XZR, Xm) is handled as part of |
| // support for address arithmetics, but ORR in general is not. |
| // This restriction may be relaxed in the future. |
| |
| .globl good_mov_reg |
| .type good_mov_reg,@function |
| good_mov_reg: |
| // CHECK-NOT: good_mov_reg |
| adrp x0, sym |
| mov x1, x0 |
| orr x2, xzr, x1 // the same as "mov x2, x1" |
| paciza x2 |
| ret |
| .size good_mov_reg, .-good_mov_reg |
| |
| .globl bad_orr_not_xzr |
| .type bad_orr_not_xzr,@function |
| bad_orr_not_xzr: |
| // CHECK-LABEL: GS-PAUTH: signing oracle found in function bad_orr_not_xzr, basic block {{[^,]+}}, at address |
| // CHECK-NEXT: The instruction is {{[0-9a-f]+}}: paciza x2 |
| // CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are: |
| // CHECK-NEXT: 1. {{[0-9a-f]+}}: orr x2, x1, x0 |
| // CHECK-NEXT: This happens in the following basic block: |
| // CHECK-NEXT: {{[0-9a-f]+}}: adrp x0, #{{(0x)?[0-9a-f]+}} |
| // CHECK-NEXT: {{[0-9a-f]+}}: mov x1, #0 |
| // CHECK-NEXT: {{[0-9a-f]+}}: orr x2, x1, x0 |
| // CHECK-NEXT: {{[0-9a-f]+}}: paciza x2 |
| // CHECK-NEXT: {{[0-9a-f]+}}: ret |
| adrp x0, sym |
| // The generic case of "orr Xd, Xn, Xm" is not allowed so far, |
| // even if Xn is known to be safe |
| movz x1, #0 |
| orr x2, x1, x0 |
| paciza x2 |
| ret |
| .size bad_orr_not_xzr, .-bad_orr_not_xzr |
| |
| .globl bad_orr_not_lsl0 |
| .type bad_orr_not_lsl0,@function |
| bad_orr_not_lsl0: |
| // CHECK-LABEL: GS-PAUTH: signing oracle found in function bad_orr_not_lsl0, basic block {{[^,]+}}, at address |
| // CHECK-NEXT: The instruction is {{[0-9a-f]+}}: paciza x2 |
| // CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are: |
| // CHECK-NEXT: 1. {{[0-9a-f]+}}: orr x2, xzr, x0, lsl #1 |
| // CHECK-NEXT: This happens in the following basic block: |
| // CHECK-NEXT: {{[0-9a-f]+}}: adrp x0, #{{(0x)?[0-9a-f]+}} |
| // CHECK-NEXT: {{[0-9a-f]+}}: orr x2, xzr, x0, lsl #1 |
| // CHECK-NEXT: {{[0-9a-f]+}}: paciza x2 |
| // CHECK-NEXT: {{[0-9a-f]+}}: ret |
| adrp x0, sym |
| // Currently, the only allowed form of "orr" is that used by "mov Xd, Xn" alias. |
| // This can be relaxed in the future. |
| orr x2, xzr, x0, lsl #1 |
| paciza x2 |
| ret |
| .size bad_orr_not_lsl0, .-bad_orr_not_lsl0 |
| |
| // Check that the input register operands of `add`/`mov` is correct. |
| |
| .globl bad_add_input_reg |
| .type bad_add_input_reg,@function |
| bad_add_input_reg: |
| // CHECK-LABEL: GS-PAUTH: signing oracle found in function bad_add_input_reg, basic block {{[^,]+}}, at address |
| // CHECK-NEXT: The instruction is {{[0-9a-f]+}}: paciza x0 |
| // CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are: |
| // CHECK-NEXT: 1. {{[0-9a-f]+}}: add x0, x1, #0x{{[0-9a-f]+}} |
| // CHECK-NEXT: This happens in the following basic block: |
| // CHECK-NEXT: {{[0-9a-f]+}}: adrp x0, #{{(0x)?[0-9a-f]+}} |
| // CHECK-NEXT: {{[0-9a-f]+}}: add x0, x1, #0x{{[0-9a-f]+}} |
| // CHECK-NEXT: {{[0-9a-f]+}}: paciza x0 |
| // CHECK-NEXT: {{[0-9a-f]+}}: ret |
| adrp x0, sym |
| add x0, x1, :lo12:sym |
| paciza x0 |
| ret |
| .size bad_add_input_reg, .-bad_add_input_reg |
| |
| .globl bad_mov_input_reg |
| .type bad_mov_input_reg,@function |
| bad_mov_input_reg: |
| // CHECK-LABEL: GS-PAUTH: signing oracle found in function bad_mov_input_reg, basic block {{[^,]+}}, at address |
| // CHECK-NEXT: The instruction is {{[0-9a-f]+}}: paciza x0 |
| // CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are: |
| // CHECK-NEXT: 1. {{[0-9a-f]+}}: mov x0, x1 |
| // CHECK-NEXT: This happens in the following basic block: |
| // CHECK-NEXT: {{[0-9a-f]+}}: adrp x0, #{{(0x)?[0-9a-f]+}} |
| // CHECK-NEXT: {{[0-9a-f]+}}: mov x0, x1 |
| // CHECK-NEXT: {{[0-9a-f]+}}: paciza x0 |
| // CHECK-NEXT: {{[0-9a-f]+}}: ret |
| adrp x0, sym |
| mov x0, x1 |
| paciza x0 |
| ret |
| .size bad_mov_input_reg, .-bad_mov_input_reg |
| |
| .globl main |
| .type main,@function |
| main: |
| mov x0, 0 |
| ret |
| .size main, .-main |