Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

kselftest: arm64: Add BTI tests

Add some tests that verify that BTI functions correctly for static binaries
built with and without BTI support, verifying that SIGILL is generated when
expected and is not generated in other situations.

Since BTI support is still being rolled out in distributions these tests
are built entirely free standing, no libc support is used at all so none
of the standard helper functions for kselftest can be used and we open
code everything. This also means we aren't testing the kernel support for
the dynamic linker, though the test program can be readily adapted for
that once it becomes something that we can reliably build and run.

These tests were originally written by Dave Martin, I've adapted them for
kselftest, mainly around the build system and the output format.

Signed-off-by: Mark Brown <broonie@kernel.org>
Cc: Dave Martin <Dave.Martin@arm.com>
Link: https://lore.kernel.org/r/20210309193731.57247-1-broonie@kernel.org
Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>

authored by

Mark Brown and committed by
Catalin Marinas
314bcbf0 75347add

+637 -1
+1 -1
tools/testing/selftests/arm64/Makefile
··· 4 4 ARCH ?= $(shell uname -m 2>/dev/null || echo not) 5 5 6 6 ifneq (,$(filter $(ARCH),aarch64 arm64)) 7 - ARM64_SUBTARGETS ?= tags signal pauth fp mte 7 + ARM64_SUBTARGETS ?= tags signal pauth fp mte bti 8 8 else 9 9 ARM64_SUBTARGETS := 10 10 endif
+2
tools/testing/selftests/arm64/bti/.gitignore
··· 1 + btitest 2 + nobtitest
+61
tools/testing/selftests/arm64/bti/Makefile
··· 1 + # SPDX-License-Identifier: GPL-2.0 2 + 3 + TEST_GEN_PROGS := btitest nobtitest 4 + 5 + PROGS := $(patsubst %,gen/%,$(TEST_GEN_PROGS)) 6 + 7 + # These tests are built as freestanding binaries since otherwise BTI 8 + # support in ld.so is required which is not currently widespread; when 9 + # it is available it will still be useful to test this separately as the 10 + # cases for statically linked and dynamically lined binaries are 11 + # slightly different. 12 + 13 + CFLAGS_NOBTI = -DBTI=0 14 + CFLAGS_BTI = -mbranch-protection=standard -DBTI=1 15 + 16 + CFLAGS_COMMON = -ffreestanding -Wall -Wextra $(CFLAGS) 17 + 18 + BTI_CC_COMMAND = $(CC) $(CFLAGS_BTI) $(CFLAGS_COMMON) -c -o $@ $< 19 + NOBTI_CC_COMMAND = $(CC) $(CFLAGS_NOBTI) $(CFLAGS_COMMON) -c -o $@ $< 20 + 21 + %-bti.o: %.c 22 + $(BTI_CC_COMMAND) 23 + 24 + %-bti.o: %.S 25 + $(BTI_CC_COMMAND) 26 + 27 + %-nobti.o: %.c 28 + $(NOBTI_CC_COMMAND) 29 + 30 + %-nobti.o: %.S 31 + $(NOBTI_CC_COMMAND) 32 + 33 + BTI_OBJS = \ 34 + test-bti.o \ 35 + signal-bti.o \ 36 + start-bti.o \ 37 + syscall-bti.o \ 38 + system-bti.o \ 39 + teststubs-bti.o \ 40 + trampoline-bti.o 41 + gen/btitest: $(BTI_OBJS) 42 + $(CC) $(CFLAGS_BTI) $(CFLAGS_COMMON) -nostdlib -o $@ $^ 43 + 44 + NOBTI_OBJS = \ 45 + test-nobti.o \ 46 + signal-nobti.o \ 47 + start-nobti.o \ 48 + syscall-nobti.o \ 49 + system-nobti.o \ 50 + teststubs-nobti.o \ 51 + trampoline-nobti.o 52 + gen/nobtitest: $(NOBTI_OBJS) 53 + $(CC) $(CFLAGS_BTI) $(CFLAGS_COMMON) -nostdlib -o $@ $^ 54 + 55 + # Including KSFT lib.mk here will also mangle the TEST_GEN_PROGS list 56 + # to account for any OUTPUT target-dirs optionally provided by 57 + # the toplevel makefile 58 + include ../../lib.mk 59 + 60 + $(TEST_GEN_PROGS): $(PROGS) 61 + cp $(PROGS) $(OUTPUT)/
+80
tools/testing/selftests/arm64/bti/assembler.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + /* 3 + * Copyright (C) 2019 Arm Limited 4 + * Original author: Dave Martin <Dave.Martin@arm.com> 5 + */ 6 + 7 + #ifndef ASSEMBLER_H 8 + #define ASSEMBLER_H 9 + 10 + #define NT_GNU_PROPERTY_TYPE_0 5 11 + #define GNU_PROPERTY_AARCH64_FEATURE_1_AND 0xc0000000 12 + 13 + /* Bits for GNU_PROPERTY_AARCH64_FEATURE_1_BTI */ 14 + #define GNU_PROPERTY_AARCH64_FEATURE_1_BTI (1U << 0) 15 + #define GNU_PROPERTY_AARCH64_FEATURE_1_PAC (1U << 1) 16 + 17 + 18 + .macro startfn name:req 19 + .globl \name 20 + \name: 21 + .macro endfn 22 + .size \name, . - \name 23 + .type \name, @function 24 + .purgem endfn 25 + .endm 26 + .endm 27 + 28 + .macro emit_aarch64_feature_1_and 29 + .pushsection .note.gnu.property, "a" 30 + .align 3 31 + .long 2f - 1f 32 + .long 6f - 3f 33 + .long NT_GNU_PROPERTY_TYPE_0 34 + 1: .string "GNU" 35 + 2: 36 + .align 3 37 + 3: .long GNU_PROPERTY_AARCH64_FEATURE_1_AND 38 + .long 5f - 4f 39 + 4: 40 + #if BTI 41 + .long GNU_PROPERTY_AARCH64_FEATURE_1_PAC | \ 42 + GNU_PROPERTY_AARCH64_FEATURE_1_BTI 43 + #else 44 + .long 0 45 + #endif 46 + 5: 47 + .align 3 48 + 6: 49 + .popsection 50 + .endm 51 + 52 + .macro paciasp 53 + hint 0x19 54 + .endm 55 + 56 + .macro autiasp 57 + hint 0x1d 58 + .endm 59 + 60 + .macro __bti_ 61 + hint 0x20 62 + .endm 63 + 64 + .macro __bti_c 65 + hint 0x22 66 + .endm 67 + 68 + .macro __bti_j 69 + hint 0x24 70 + .endm 71 + 72 + .macro __bti_jc 73 + hint 0x26 74 + .endm 75 + 76 + .macro bti what= 77 + __bti_\what 78 + .endm 79 + 80 + #endif /* ! ASSEMBLER_H */
+23
tools/testing/selftests/arm64/bti/btitest.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + /* 3 + * Copyright (C) 2019 Arm Limited 4 + * Original author: Dave Martin <Dave.Martin@arm.com> 5 + */ 6 + 7 + #ifndef BTITEST_H 8 + #define BTITEST_H 9 + 10 + /* Trampolines for calling the test stubs: */ 11 + void call_using_br_x0(void (*)(void)); 12 + void call_using_br_x16(void (*)(void)); 13 + void call_using_blr(void (*)(void)); 14 + 15 + /* Test stubs: */ 16 + void nohint_func(void); 17 + void bti_none_func(void); 18 + void bti_c_func(void); 19 + void bti_j_func(void); 20 + void bti_jc_func(void); 21 + void paciasp_func(void); 22 + 23 + #endif /* !BTITEST_H */
+21
tools/testing/selftests/arm64/bti/compiler.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + /* 3 + * Copyright (C) 2019 Arm Limited 4 + * Original author: Dave Martin <Dave.Martin@arm.com> 5 + */ 6 + 7 + #ifndef COMPILER_H 8 + #define COMPILER_H 9 + 10 + #define __always_unused __attribute__((__unused__)) 11 + #define __noreturn __attribute__((__noreturn__)) 12 + #define __unreachable() __builtin_unreachable() 13 + 14 + /* curse(e) has value e, but the compiler cannot assume so */ 15 + #define curse(e) ({ \ 16 + __typeof__(e) __curse_e = (e); \ 17 + asm ("" : "+r" (__curse_e)); \ 18 + __curse_e; \ 19 + }) 20 + 21 + #endif /* ! COMPILER_H */
+2
tools/testing/selftests/arm64/bti/gen/.gitignore
··· 1 + btitest 2 + nobtitest
+37
tools/testing/selftests/arm64/bti/signal.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Copyright (C) 2019 Arm Limited 4 + * Original author: Dave Martin <Dave.Martin@arm.com> 5 + */ 6 + 7 + #include "system.h" 8 + #include "signal.h" 9 + 10 + int sigemptyset(sigset_t *s) 11 + { 12 + unsigned int i; 13 + 14 + for (i = 0; i < _NSIG_WORDS; ++i) 15 + s->sig[i] = 0; 16 + 17 + return 0; 18 + } 19 + 20 + int sigaddset(sigset_t *s, int n) 21 + { 22 + if (n < 1 || n > _NSIG) 23 + return -EINVAL; 24 + 25 + s->sig[(n - 1) / _NSIG_BPW] |= 1UL << (n - 1) % _NSIG_BPW; 26 + return 0; 27 + } 28 + 29 + int sigaction(int n, struct sigaction *sa, const struct sigaction *old) 30 + { 31 + return syscall(__NR_rt_sigaction, n, sa, old, sizeof(sa->sa_mask)); 32 + } 33 + 34 + int sigprocmask(int how, const sigset_t *mask, sigset_t *old) 35 + { 36 + return syscall(__NR_rt_sigprocmask, how, mask, old, sizeof(*mask)); 37 + }
+21
tools/testing/selftests/arm64/bti/signal.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + /* 3 + * Copyright (C) 2019 Arm Limited 4 + * Original author: Dave Martin <Dave.Martin@arm.com> 5 + */ 6 + 7 + #ifndef SIGNAL_H 8 + #define SIGNAL_H 9 + 10 + #include <linux/signal.h> 11 + 12 + #include "system.h" 13 + 14 + typedef __sighandler_t sighandler_t; 15 + 16 + int sigemptyset(sigset_t *s); 17 + int sigaddset(sigset_t *s, int n); 18 + int sigaction(int n, struct sigaction *sa, const struct sigaction *old); 19 + int sigprocmask(int how, const sigset_t *mask, sigset_t *old); 20 + 21 + #endif /* ! SIGNAL_H */
+14
tools/testing/selftests/arm64/bti/start.S
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + /* 3 + * Copyright (C) 2019 Arm Limited 4 + * Original author: Dave Martin <Dave.Martin@arm.com> 5 + */ 6 + 7 + #include "assembler.h" 8 + 9 + startfn _start 10 + mov x0, sp 11 + b start 12 + endfn 13 + 14 + emit_aarch64_feature_1_and
+23
tools/testing/selftests/arm64/bti/syscall.S
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + /* 3 + * Copyright (C) 2019 Arm Limited 4 + * Original author: Dave Martin <Dave.Martin@arm.com> 5 + */ 6 + 7 + #include "assembler.h" 8 + 9 + startfn syscall 10 + bti c 11 + mov w8, w0 12 + mov x0, x1 13 + mov x1, x2 14 + mov x2, x3 15 + mov x3, x4 16 + mov x4, x5 17 + mov x5, x6 18 + mov x6, x7 19 + svc #0 20 + ret 21 + endfn 22 + 23 + emit_aarch64_feature_1_and
+22
tools/testing/selftests/arm64/bti/system.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Copyright (C) 2019 Arm Limited 4 + * Original author: Dave Martin <Dave.Martin@arm.com> 5 + */ 6 + 7 + #include "system.h" 8 + 9 + #include <asm/unistd.h> 10 + 11 + #include "compiler.h" 12 + 13 + void __noreturn exit(int n) 14 + { 15 + syscall(__NR_exit, n); 16 + __unreachable(); 17 + } 18 + 19 + ssize_t write(int fd, const void *buf, size_t size) 20 + { 21 + return syscall(__NR_write, fd, buf, size); 22 + }
+28
tools/testing/selftests/arm64/bti/system.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + /* 3 + * Copyright (C) 2019 Arm Limited 4 + * Original author: Dave Martin <Dave.Martin@arm.com> 5 + */ 6 + 7 + #ifndef SYSTEM_H 8 + #define SYSTEM_H 9 + 10 + #include <linux/types.h> 11 + #include <linux/stddef.h> 12 + 13 + typedef __kernel_size_t size_t; 14 + typedef __kernel_ssize_t ssize_t; 15 + 16 + #include <linux/errno.h> 17 + #include <asm/hwcap.h> 18 + #include <asm/ptrace.h> 19 + #include <asm/unistd.h> 20 + 21 + #include "compiler.h" 22 + 23 + long syscall(int nr, ...); 24 + 25 + void __noreturn exit(int n); 26 + ssize_t write(int fd, const void *buf, size_t size); 27 + 28 + #endif /* ! SYSTEM_H */
+234
tools/testing/selftests/arm64/bti/test.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Copyright (C) 2019,2021 Arm Limited 4 + * Original author: Dave Martin <Dave.Martin@arm.com> 5 + */ 6 + 7 + #include "system.h" 8 + 9 + #include <linux/errno.h> 10 + #include <linux/auxvec.h> 11 + #include <linux/signal.h> 12 + #include <asm/sigcontext.h> 13 + #include <asm/ucontext.h> 14 + 15 + typedef struct ucontext ucontext_t; 16 + 17 + #include "btitest.h" 18 + #include "compiler.h" 19 + #include "signal.h" 20 + 21 + #define EXPECTED_TESTS 18 22 + 23 + static volatile unsigned int test_num = 1; 24 + static unsigned int test_passed; 25 + static unsigned int test_failed; 26 + static unsigned int test_skipped; 27 + 28 + static void fdputs(int fd, const char *str) 29 + { 30 + size_t len = 0; 31 + const char *p = str; 32 + 33 + while (*p++) 34 + ++len; 35 + 36 + write(fd, str, len); 37 + } 38 + 39 + static void putstr(const char *str) 40 + { 41 + fdputs(1, str); 42 + } 43 + 44 + static void putnum(unsigned int num) 45 + { 46 + char c; 47 + 48 + if (num / 10) 49 + putnum(num / 10); 50 + 51 + c = '0' + (num % 10); 52 + write(1, &c, 1); 53 + } 54 + 55 + #define puttestname(test_name, trampoline_name) do { \ 56 + putstr(test_name); \ 57 + putstr("/"); \ 58 + putstr(trampoline_name); \ 59 + } while (0) 60 + 61 + void print_summary(void) 62 + { 63 + putstr("# Totals: pass:"); 64 + putnum(test_passed); 65 + putstr(" fail:"); 66 + putnum(test_failed); 67 + putstr(" xfail:0 xpass:0 skip:"); 68 + putnum(test_skipped); 69 + putstr(" error:0\n"); 70 + } 71 + 72 + static const char *volatile current_test_name; 73 + static const char *volatile current_trampoline_name; 74 + static volatile int sigill_expected, sigill_received; 75 + 76 + static void handler(int n, siginfo_t *si __always_unused, 77 + void *uc_ __always_unused) 78 + { 79 + ucontext_t *uc = uc_; 80 + 81 + putstr("# \t[SIGILL in "); 82 + puttestname(current_test_name, current_trampoline_name); 83 + putstr(", BTYPE="); 84 + write(1, &"00011011"[((uc->uc_mcontext.pstate & PSR_BTYPE_MASK) 85 + >> PSR_BTYPE_SHIFT) * 2], 2); 86 + if (!sigill_expected) { 87 + putstr("]\n"); 88 + putstr("not ok "); 89 + putnum(test_num); 90 + putstr(" "); 91 + puttestname(current_test_name, current_trampoline_name); 92 + putstr("(unexpected SIGILL)\n"); 93 + print_summary(); 94 + exit(128 + n); 95 + } 96 + 97 + putstr(" (expected)]\n"); 98 + sigill_received = 1; 99 + /* zap BTYPE so that resuming the faulting code will work */ 100 + uc->uc_mcontext.pstate &= ~PSR_BTYPE_MASK; 101 + } 102 + 103 + static int skip_all; 104 + 105 + static void __do_test(void (*trampoline)(void (*)(void)), 106 + void (*fn)(void), 107 + const char *trampoline_name, 108 + const char *name, 109 + int expect_sigill) 110 + { 111 + if (skip_all) { 112 + test_skipped++; 113 + putstr("ok "); 114 + putnum(test_num); 115 + putstr(" "); 116 + puttestname(name, trampoline_name); 117 + putstr(" # SKIP\n"); 118 + 119 + return; 120 + } 121 + 122 + /* Branch Target exceptions should only happen in BTI binaries: */ 123 + if (!BTI) 124 + expect_sigill = 0; 125 + 126 + sigill_expected = expect_sigill; 127 + sigill_received = 0; 128 + current_test_name = name; 129 + current_trampoline_name = trampoline_name; 130 + 131 + trampoline(fn); 132 + 133 + if (expect_sigill && !sigill_received) { 134 + putstr("not ok "); 135 + test_failed++; 136 + } else { 137 + putstr("ok "); 138 + test_passed++; 139 + } 140 + putnum(test_num++); 141 + putstr(" "); 142 + puttestname(name, trampoline_name); 143 + putstr("\n"); 144 + } 145 + 146 + #define do_test(expect_sigill_br_x0, \ 147 + expect_sigill_br_x16, \ 148 + expect_sigill_blr, \ 149 + name) \ 150 + do { \ 151 + __do_test(call_using_br_x0, name, "call_using_br_x0", #name, \ 152 + expect_sigill_br_x0); \ 153 + __do_test(call_using_br_x16, name, "call_using_br_x16", #name, \ 154 + expect_sigill_br_x16); \ 155 + __do_test(call_using_blr, name, "call_using_blr", #name, \ 156 + expect_sigill_blr); \ 157 + } while (0) 158 + 159 + void start(int *argcp) 160 + { 161 + struct sigaction sa; 162 + void *const *p; 163 + const struct auxv_entry { 164 + unsigned long type; 165 + unsigned long val; 166 + } *auxv; 167 + unsigned long hwcap = 0, hwcap2 = 0; 168 + 169 + putstr("TAP version 13\n"); 170 + putstr("1.."); 171 + putnum(EXPECTED_TESTS); 172 + putstr("\n"); 173 + 174 + /* Gross hack for finding AT_HWCAP2 from the initial process stack: */ 175 + p = (void *const *)argcp + 1 + *argcp + 1; /* start of environment */ 176 + /* step over environment */ 177 + while (*p++) 178 + ; 179 + for (auxv = (const struct auxv_entry *)p; auxv->type != AT_NULL; ++auxv) { 180 + switch (auxv->type) { 181 + case AT_HWCAP: 182 + hwcap = auxv->val; 183 + break; 184 + case AT_HWCAP2: 185 + hwcap2 = auxv->val; 186 + break; 187 + default: 188 + break; 189 + } 190 + } 191 + 192 + if (hwcap & HWCAP_PACA) 193 + putstr("# HWCAP_PACA present\n"); 194 + else 195 + putstr("# HWCAP_PACA not present\n"); 196 + 197 + if (hwcap2 & HWCAP2_BTI) { 198 + putstr("# HWCAP2_BTI present\n"); 199 + if (!(hwcap & HWCAP_PACA)) 200 + putstr("# Bad hardware? Expect problems.\n"); 201 + } else { 202 + putstr("# HWCAP2_BTI not present\n"); 203 + skip_all = 1; 204 + } 205 + 206 + putstr("# Test binary"); 207 + if (!BTI) 208 + putstr(" not"); 209 + putstr(" built for BTI\n"); 210 + 211 + sa.sa_handler = (sighandler_t)(void *)handler; 212 + sa.sa_flags = SA_SIGINFO; 213 + sigemptyset(&sa.sa_mask); 214 + sigaction(SIGILL, &sa, NULL); 215 + sigaddset(&sa.sa_mask, SIGILL); 216 + sigprocmask(SIG_UNBLOCK, &sa.sa_mask, NULL); 217 + 218 + do_test(1, 1, 1, nohint_func); 219 + do_test(1, 1, 1, bti_none_func); 220 + do_test(1, 0, 0, bti_c_func); 221 + do_test(0, 0, 1, bti_j_func); 222 + do_test(0, 0, 0, bti_jc_func); 223 + do_test(1, 0, 0, paciasp_func); 224 + 225 + print_summary(); 226 + 227 + if (test_num - 1 != EXPECTED_TESTS) 228 + putstr("# WARNING - EXPECTED TEST COUNT WRONG\n"); 229 + 230 + if (test_failed) 231 + exit(1); 232 + else 233 + exit(0); 234 + }
+39
tools/testing/selftests/arm64/bti/teststubs.S
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + /* 3 + * Copyright (C) 2019 Arm Limited 4 + * Original author: Dave Martin <Dave.Martin@arm.com> 5 + */ 6 + 7 + #include "assembler.h" 8 + 9 + startfn bti_none_func 10 + bti 11 + ret 12 + endfn 13 + 14 + startfn bti_c_func 15 + bti c 16 + ret 17 + endfn 18 + 19 + startfn bti_j_func 20 + bti j 21 + ret 22 + endfn 23 + 24 + startfn bti_jc_func 25 + bti jc 26 + ret 27 + endfn 28 + 29 + startfn paciasp_func 30 + paciasp 31 + autiasp 32 + ret 33 + endfn 34 + 35 + startfn nohint_func 36 + ret 37 + endfn 38 + 39 + emit_aarch64_feature_1_and
+29
tools/testing/selftests/arm64/bti/trampoline.S
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + /* 3 + * Copyright (C) 2019 Arm Limited 4 + * Original author: Dave Martin <Dave.Martin@arm.com> 5 + */ 6 + 7 + #include "assembler.h" 8 + 9 + startfn call_using_br_x0 10 + bti c 11 + br x0 12 + endfn 13 + 14 + startfn call_using_br_x16 15 + bti c 16 + mov x16, x0 17 + br x16 18 + endfn 19 + 20 + startfn call_using_blr 21 + paciasp 22 + stp x29, x30, [sp, #-16]! 23 + blr x0 24 + ldp x29, x30, [sp], #16 25 + autiasp 26 + ret 27 + endfn 28 + 29 + emit_aarch64_feature_1_and