blob: 7810b2d3c362625f3b89dfb792e41037880bca15 [file]
// RUN: %clang %cflags -march=armv8.3-a %s -o %t.exe -Wl,--emit-relocs
// RUN: llvm-bolt-binary-analysis --scanners=pauth %t.exe 2>&1 | FileCheck %s
// Test what instructions can be used to terminate the program abnormally
// on security violation.
//
// All test cases have the same structure:
//
// cbz x0, 1f // [a], ensures [c] is never reported as unreachable
// autia x2, x3
// cbz x1, 2f // [b]
// [instruction under test]
// 1:
// ret // [c]
// 2:
// ldr x0, [x2]
// ret
//
// This is to handle three possible cases: the instruction under test may be
// considered by BOLT as
// * trapping (and thus no-return): after being authenticated, x2 is ether
// checked by LDR (if [b] is taken) or the program is terminated
// immediately without leaking x2 (if [b] falls through to the trapping
// instruction under test). Nothing is reported.
// * non-trapping, but no-return (such as calling abort()): x2 is leaked if [b]
// falls through. Authentication oracle is reported.
// * non-trapping and falling-through (i.e. a regular instruction):
// x2 is leaked by [c]. Authentication oracle is reported.
.text
.globl brk_key_ia
.type brk_key_ia,@function
brk_key_ia:
// CHECK-NOT: brk_key_ia
cbz x0, 1f
autia x2, x3
cbz x1, 2f
brk 0xc470
1:
ret
2:
ldr x0, [x2]
ret
.size brk_key_ia, .-brk_key_ia
.globl brk_key_ib
.type brk_key_ib,@function
brk_key_ib:
// CHECK-NOT: brk_key_ib
cbz x0, 1f
autia x2, x3
cbz x1, 2f
brk 0xc471
1:
ret
2:
ldr x0, [x2]
ret
.size brk_key_ib, .-brk_key_ib
.globl brk_key_da
.type brk_key_da,@function
brk_key_da:
// CHECK-NOT: brk_key_da
cbz x0, 1f
autia x2, x3
cbz x1, 2f
brk 0xc472
1:
ret
2:
ldr x0, [x2]
ret
.size brk_key_da, .-brk_key_da
.globl brk_key_db
.type brk_key_db,@function
brk_key_db:
// CHECK-NOT: brk_key_db
cbz x0, 1f
autia x2, x3
cbz x1, 2f
brk 0xc473
1:
ret
2:
ldr x0, [x2]
ret
.size brk_key_db, .-brk_key_db
// The immediate operand of BRK instruction may indicate whether the instruction
// is intended to be a non-recoverable trap: for example, for this code
//
// int test_trap(void) {
// __builtin_trap();
// return 42;
// }
// int test_debugtrap(void) {
// __builtin_debugtrap();
// return 42;
// }
//
// Clang produces the following assembly:
//
// test_trap:
// brk #0x1
// test_debugtrap:
// brk #0xf000
// mov w0, #42
// ret
//
// In GCC, __builtin_trap() uses "brk 0x3e8" (i.e. decimal 1000) and
// __builtin_debugtrap() is not supported.
//
// At the time of writing these test cases, any BRK instruction is considered
// no-return by BOLT, thus it ends its basic block and prevents falling through
// to the next BB.
// FIXME: Make BOLT handle __builtin_debugtrap() properly from the CFG point
// of view.
.globl brk_gcc_builtin_trap
.type brk_gcc_builtin_trap,@function
brk_gcc_builtin_trap:
// CHECK-NOT: brk_gcc_builtin_trap
cbz x0, 1f
autia x2, x3
cbz x1, 2f
brk 0x3e8 // __builtin_trap()
1:
ret
2:
ldr x0, [x2]
ret
.size brk_gcc_builtin_trap, .-brk_gcc_builtin_trap
.globl brk_clang_builtin_trap
.type brk_clang_builtin_trap,@function
brk_clang_builtin_trap:
// CHECK-NOT: brk_clang_builtin_trap
cbz x0, 1f
autia x2, x3
cbz x1, 2f
brk 0x1 // __builtin_trap()
1:
ret
2:
ldr x0, [x2]
ret
.size brk_clang_builtin_trap, .-brk_clang_builtin_trap
.globl brk_clang_builtin_debugtrap
.type brk_clang_builtin_debugtrap,@function
brk_clang_builtin_debugtrap:
// CHECK-LABEL: GS-PAUTH: authentication oracle found in function brk_clang_builtin_debugtrap, basic block {{[^,]+}}, at address
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: autia x2, x3
// CHECK-NEXT: The 0 instructions that leak the affected registers are:
cbz x0, 1f
autia x2, x3
cbz x1, 2f
brk 0xf000 // __builtin_debugtrap()
1:
ret
2:
ldr x0, [x2]
ret
.size brk_clang_builtin_debugtrap, .-brk_clang_builtin_debugtrap
// Conservatively assume BRK with an unknown immediate operand as not suitable
// for terminating the program on security violation.
.globl brk_unknown_imm
.type brk_unknown_imm,@function
brk_unknown_imm:
// CHECK-LABEL: GS-PAUTH: authentication oracle found in function brk_unknown_imm, basic block {{[^,]+}}, at address
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: autia x2, x3
// CHECK-NEXT: The 0 instructions that leak the affected registers are:
cbz x0, 1f
autia x2, x3
cbz x1, 2f
brk 0x3572
1:
ret
2:
ldr x0, [x2]
ret
.size brk_unknown_imm, .-brk_unknown_imm
// Conservatively assume calling the abort() function may be an unsafe way to
// terminate the program, as there is some amount of instructions that would
// be executed when the program state is already tampered with.
.globl call_abort_fn
.type call_abort_fn,@function
call_abort_fn:
// CHECK-LABEL: GS-PAUTH: authentication oracle found in function call_abort_fn, basic block {{[^,]+}}, at address
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: autia x2, x3
// CHECK-NEXT: The 0 instructions that leak the affected registers are:
cbz x0, 1f
autia x2, x3
cbz x1, 2f
b abort // a no-return tail call to abort()
1:
ret
2:
ldr x0, [x2]
ret
.size call_abort_fn, .-call_abort_fn
.globl main
.type main,@function
main:
mov x0, 0
ret
.size main, .-main