|
| 1 | +// RUN: %clang %cflags -march=armv8.3-a %s -o %t.exe -Wl,--emit-relocs |
| 2 | +// RUN: llvm-bolt-binary-analysis --scanners=pauth %t.exe 2>&1 | FileCheck %s |
| 3 | + |
| 4 | +// Test what instructions can be used to terminate the program abnormally |
| 5 | +// on security violation. |
| 6 | +// |
| 7 | +// All test cases have the same structure: |
| 8 | +// |
| 9 | +// cbz x0, 1f // [a], ensures [c] is never reported as unreachable |
| 10 | +// autia x2, x3 |
| 11 | +// cbz x1, 2f // [b] |
| 12 | +// [instruction under test] |
| 13 | +// 1: |
| 14 | +// ret // [c] |
| 15 | +// 2: |
| 16 | +// ldr x0, [x2] |
| 17 | +// ret |
| 18 | +// |
| 19 | +// This is to handle three possible cases: the instruction under test may be |
| 20 | +// considered by BOLT as |
| 21 | +// * trapping (and thus no-return): after being authenticated, x2 is ether |
| 22 | +// checked by LDR (if [b] is taken) or the program is terminated |
| 23 | +// immediately without leaking x2 (if [b] falls through to the trapping |
| 24 | +// instruction under test). Nothing is reported. |
| 25 | +// * non-trapping, but no-return (such as calling abort()): x2 is leaked if [b] |
| 26 | +// falls through. Authentication oracle is reported. |
| 27 | +// * non-trapping and falling-through (i.e. a regular instruction): |
| 28 | +// x2 is leaked by [c]. Authentication oracle is reported. |
| 29 | + |
| 30 | + .text |
| 31 | + |
| 32 | + .globl brk_key_ia |
| 33 | + .type brk_key_ia,@function |
| 34 | +brk_key_ia: |
| 35 | +// CHECK-NOT: brk_key_ia |
| 36 | + cbz x0, 1f |
| 37 | + autia x2, x3 |
| 38 | + cbz x1, 2f |
| 39 | + brk 0xc470 |
| 40 | +1: |
| 41 | + ret |
| 42 | +2: |
| 43 | + ldr x0, [x2] |
| 44 | + ret |
| 45 | + .size brk_key_ia, .-brk_key_ia |
| 46 | + |
| 47 | + .globl brk_key_ib |
| 48 | + .type brk_key_ib,@function |
| 49 | +brk_key_ib: |
| 50 | +// CHECK-NOT: brk_key_ib |
| 51 | + cbz x0, 1f |
| 52 | + autia x2, x3 |
| 53 | + cbz x1, 2f |
| 54 | + brk 0xc471 |
| 55 | +1: |
| 56 | + ret |
| 57 | +2: |
| 58 | + ldr x0, [x2] |
| 59 | + ret |
| 60 | + .size brk_key_ib, .-brk_key_ib |
| 61 | + |
| 62 | + .globl brk_key_da |
| 63 | + .type brk_key_da,@function |
| 64 | +brk_key_da: |
| 65 | +// CHECK-NOT: brk_key_da |
| 66 | + cbz x0, 1f |
| 67 | + autia x2, x3 |
| 68 | + cbz x1, 2f |
| 69 | + brk 0xc472 |
| 70 | +1: |
| 71 | + ret |
| 72 | +2: |
| 73 | + ldr x0, [x2] |
| 74 | + ret |
| 75 | + .size brk_key_da, .-brk_key_da |
| 76 | + |
| 77 | + .globl brk_key_db |
| 78 | + .type brk_key_db,@function |
| 79 | +brk_key_db: |
| 80 | +// CHECK-NOT: brk_key_db |
| 81 | + cbz x0, 1f |
| 82 | + autia x2, x3 |
| 83 | + cbz x1, 2f |
| 84 | + brk 0xc473 |
| 85 | +1: |
| 86 | + ret |
| 87 | +2: |
| 88 | + ldr x0, [x2] |
| 89 | + ret |
| 90 | + .size brk_key_db, .-brk_key_db |
| 91 | + |
| 92 | +// The immediate operand of BRK instruction may indicate whether the instruction |
| 93 | +// is intended to be a non-recoverable trap: for example, for this code |
| 94 | +// |
| 95 | +// int test_trap(void) { |
| 96 | +// __builtin_trap(); |
| 97 | +// return 42; |
| 98 | +// } |
| 99 | +// int test_debugtrap(void) { |
| 100 | +// __builtin_debugtrap(); |
| 101 | +// return 42; |
| 102 | +// } |
| 103 | +// |
| 104 | +// Clang produces the following assembly: |
| 105 | +// |
| 106 | +// test_trap: |
| 107 | +// brk #0x1 |
| 108 | +// test_debugtrap: |
| 109 | +// brk #0xf000 |
| 110 | +// mov w0, #42 |
| 111 | +// ret |
| 112 | +// |
| 113 | +// In GCC, __builtin_trap() uses "brk 0x3e8" (i.e. decimal 1000) and |
| 114 | +// __builtin_debugtrap() is not supported. |
| 115 | +// |
| 116 | +// At the time of writing these test cases, any BRK instruction is considered |
| 117 | +// no-return by BOLT, thus it ends its basic block and prevents falling through |
| 118 | +// to the next BB. |
| 119 | +// FIXME: Make BOLT handle __builtin_debugtrap() properly from the CFG point |
| 120 | +// of view. |
| 121 | + |
| 122 | + .globl brk_gcc_builtin_trap |
| 123 | + .type brk_gcc_builtin_trap,@function |
| 124 | +brk_gcc_builtin_trap: |
| 125 | +// CHECK-NOT: brk_gcc_builtin_trap |
| 126 | + cbz x0, 1f |
| 127 | + autia x2, x3 |
| 128 | + cbz x1, 2f |
| 129 | + brk 0x3e8 // __builtin_trap() |
| 130 | +1: |
| 131 | + ret |
| 132 | +2: |
| 133 | + ldr x0, [x2] |
| 134 | + ret |
| 135 | + .size brk_gcc_builtin_trap, .-brk_gcc_builtin_trap |
| 136 | + |
| 137 | + .globl brk_clang_builtin_trap |
| 138 | + .type brk_clang_builtin_trap,@function |
| 139 | +brk_clang_builtin_trap: |
| 140 | +// CHECK-NOT: brk_clang_builtin_trap |
| 141 | + cbz x0, 1f |
| 142 | + autia x2, x3 |
| 143 | + cbz x1, 2f |
| 144 | + brk 0x1 // __builtin_trap() |
| 145 | +1: |
| 146 | + ret |
| 147 | +2: |
| 148 | + ldr x0, [x2] |
| 149 | + ret |
| 150 | + .size brk_clang_builtin_trap, .-brk_clang_builtin_trap |
| 151 | + |
| 152 | + .globl brk_clang_builtin_debugtrap |
| 153 | + .type brk_clang_builtin_debugtrap,@function |
| 154 | +brk_clang_builtin_debugtrap: |
| 155 | +// CHECK-LABEL: GS-PAUTH: authentication oracle found in function brk_clang_builtin_debugtrap, basic block {{[^,]+}}, at address |
| 156 | +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: autia x2, x3 |
| 157 | +// CHECK-NEXT: The 0 instructions that leak the affected registers are: |
| 158 | + cbz x0, 1f |
| 159 | + autia x2, x3 |
| 160 | + cbz x1, 2f |
| 161 | + brk 0xf000 // __builtin_debugtrap() |
| 162 | +1: |
| 163 | + ret |
| 164 | +2: |
| 165 | + ldr x0, [x2] |
| 166 | + ret |
| 167 | + .size brk_clang_builtin_debugtrap, .-brk_clang_builtin_debugtrap |
| 168 | + |
| 169 | +// Conservatively assume BRK with an unknown immediate operand as not suitable |
| 170 | +// for terminating the program on security violation. |
| 171 | + .globl brk_unknown_imm |
| 172 | + .type brk_unknown_imm,@function |
| 173 | +brk_unknown_imm: |
| 174 | +// CHECK-LABEL: GS-PAUTH: authentication oracle found in function brk_unknown_imm, basic block {{[^,]+}}, at address |
| 175 | +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: autia x2, x3 |
| 176 | +// CHECK-NEXT: The 0 instructions that leak the affected registers are: |
| 177 | + cbz x0, 1f |
| 178 | + autia x2, x3 |
| 179 | + cbz x1, 2f |
| 180 | + brk 0x3572 |
| 181 | +1: |
| 182 | + ret |
| 183 | +2: |
| 184 | + ldr x0, [x2] |
| 185 | + ret |
| 186 | + .size brk_unknown_imm, .-brk_unknown_imm |
| 187 | + |
| 188 | +// Conservatively assume calling the abort() function may be an unsafe way to |
| 189 | +// terminate the program, as there is some amount of instructions that would |
| 190 | +// be executed when the program state is already tampered with. |
| 191 | + .globl call_abort_fn |
| 192 | + .type call_abort_fn,@function |
| 193 | +call_abort_fn: |
| 194 | +// CHECK-LABEL: GS-PAUTH: authentication oracle found in function call_abort_fn, basic block {{[^,]+}}, at address |
| 195 | +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: autia x2, x3 |
| 196 | +// CHECK-NEXT: The 0 instructions that leak the affected registers are: |
| 197 | + cbz x0, 1f |
| 198 | + autia x2, x3 |
| 199 | + cbz x1, 2f |
| 200 | + b abort // a no-return tail call to abort() |
| 201 | +1: |
| 202 | + ret |
| 203 | +2: |
| 204 | + ldr x0, [x2] |
| 205 | + ret |
| 206 | + .size call_abort_fn, .-call_abort_fn |
| 207 | + |
| 208 | + .globl main |
| 209 | + .type main,@function |
| 210 | +main: |
| 211 | + mov x0, 0 |
| 212 | + ret |
| 213 | + .size main, .-main |
0 commit comments