Files
linux/tools/testing/selftests/arm64/bti/test.c
Mark Brown 1c3b614548 kselftest/arm64: Run BTI selftests on systems without BTI
The BTI selftests are built both with and without BTI support, validating
both the generation of BTI signals as expected for binaries without BTI
support. Both versions of the binary currently skip all their tests when
the system does not support BTI, however this is excessive since we do have
a defined ABI for how the programs should function in this case (especially
for the non-BTI binary). Update the test program to run all the tests
unconditionally, adding a runtime adjustment of the expected results on
systems that don't support BTI where we currently handle the build time
case.

The tests all use HINT space instructions, BTI itself is a HINT as is
are the PAC instructions that function as landing pads, so nothing in the
tests depends on support for BTI in the kernel or hardware.

Signed-off-by: Mark Brown <broonie@kernel.org>
Link: https://lore.kernel.org/r/20230110-arm64-bti-selftest-skip-v1-2-143ecdc84567@kernel.org
Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
2023-01-12 17:10:01 +00:00

231 lines
4.9 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2019,2021 Arm Limited
* Original author: Dave Martin <Dave.Martin@arm.com>
*/
#include "system.h"
#include <stdbool.h>
#include <stddef.h>
#include <linux/errno.h>
#include <linux/auxvec.h>
#include <linux/signal.h>
#include <asm/sigcontext.h>
#include <asm/ucontext.h>
typedef struct ucontext ucontext_t;
#include "btitest.h"
#include "compiler.h"
#include "signal.h"
#define EXPECTED_TESTS 18
static volatile unsigned int test_num = 1;
static unsigned int test_passed;
static unsigned int test_failed;
static unsigned int test_skipped;
static void fdputs(int fd, const char *str)
{
size_t len = 0;
const char *p = str;
while (*p++)
++len;
write(fd, str, len);
}
static void putstr(const char *str)
{
fdputs(1, str);
}
static void putnum(unsigned int num)
{
char c;
if (num / 10)
putnum(num / 10);
c = '0' + (num % 10);
write(1, &c, 1);
}
#define puttestname(test_name, trampoline_name) do { \
putstr(test_name); \
putstr("/"); \
putstr(trampoline_name); \
} while (0)
void print_summary(void)
{
putstr("# Totals: pass:");
putnum(test_passed);
putstr(" fail:");
putnum(test_failed);
putstr(" xfail:0 xpass:0 skip:");
putnum(test_skipped);
putstr(" error:0\n");
}
static const char *volatile current_test_name;
static const char *volatile current_trampoline_name;
static volatile int sigill_expected, sigill_received;
static void handler(int n, siginfo_t *si __always_unused,
void *uc_ __always_unused)
{
ucontext_t *uc = uc_;
putstr("# \t[SIGILL in ");
puttestname(current_test_name, current_trampoline_name);
putstr(", BTYPE=");
write(1, &"00011011"[((uc->uc_mcontext.pstate & PSR_BTYPE_MASK)
>> PSR_BTYPE_SHIFT) * 2], 2);
if (!sigill_expected) {
putstr("]\n");
putstr("not ok ");
putnum(test_num);
putstr(" ");
puttestname(current_test_name, current_trampoline_name);
putstr("(unexpected SIGILL)\n");
print_summary();
exit(128 + n);
}
putstr(" (expected)]\n");
sigill_received = 1;
/* zap BTYPE so that resuming the faulting code will work */
uc->uc_mcontext.pstate &= ~PSR_BTYPE_MASK;
}
/* Does the system have BTI? */
static bool have_bti;
static void __do_test(void (*trampoline)(void (*)(void)),
void (*fn)(void),
const char *trampoline_name,
const char *name,
int expect_sigill)
{
/*
* Branch Target exceptions should only happen for BTI
* binaries running on a system with BTI:
*/
if (!BTI || !have_bti)
expect_sigill = 0;
sigill_expected = expect_sigill;
sigill_received = 0;
current_test_name = name;
current_trampoline_name = trampoline_name;
trampoline(fn);
if (expect_sigill && !sigill_received) {
putstr("not ok ");
test_failed++;
} else {
putstr("ok ");
test_passed++;
}
putnum(test_num++);
putstr(" ");
puttestname(name, trampoline_name);
putstr("\n");
}
#define do_test(expect_sigill_br_x0, \
expect_sigill_br_x16, \
expect_sigill_blr, \
name) \
do { \
__do_test(call_using_br_x0, name, "call_using_br_x0", #name, \
expect_sigill_br_x0); \
__do_test(call_using_br_x16, name, "call_using_br_x16", #name, \
expect_sigill_br_x16); \
__do_test(call_using_blr, name, "call_using_blr", #name, \
expect_sigill_blr); \
} while (0)
void start(int *argcp)
{
struct sigaction sa;
void *const *p;
const struct auxv_entry {
unsigned long type;
unsigned long val;
} *auxv;
unsigned long hwcap = 0, hwcap2 = 0;
putstr("TAP version 13\n");
putstr("1..");
putnum(EXPECTED_TESTS);
putstr("\n");
/* Gross hack for finding AT_HWCAP2 from the initial process stack: */
p = (void *const *)argcp + 1 + *argcp + 1; /* start of environment */
/* step over environment */
while (*p++)
;
for (auxv = (const struct auxv_entry *)p; auxv->type != AT_NULL; ++auxv) {
switch (auxv->type) {
case AT_HWCAP:
hwcap = auxv->val;
break;
case AT_HWCAP2:
hwcap2 = auxv->val;
break;
default:
break;
}
}
if (hwcap & HWCAP_PACA)
putstr("# HWCAP_PACA present\n");
else
putstr("# HWCAP_PACA not present\n");
if (hwcap2 & HWCAP2_BTI) {
putstr("# HWCAP2_BTI present\n");
if (!(hwcap & HWCAP_PACA))
putstr("# Bad hardware? Expect problems.\n");
have_bti = true;
} else {
putstr("# HWCAP2_BTI not present\n");
have_bti = false;
}
putstr("# Test binary");
if (!BTI)
putstr(" not");
putstr(" built for BTI\n");
sa.sa_handler = (sighandler_t)(void *)handler;
sa.sa_flags = SA_SIGINFO;
sigemptyset(&sa.sa_mask);
sigaction(SIGILL, &sa, NULL);
sigaddset(&sa.sa_mask, SIGILL);
sigprocmask(SIG_UNBLOCK, &sa.sa_mask, NULL);
do_test(1, 1, 1, nohint_func);
do_test(1, 1, 1, bti_none_func);
do_test(1, 0, 0, bti_c_func);
do_test(0, 0, 1, bti_j_func);
do_test(0, 0, 0, bti_jc_func);
do_test(1, 0, 0, paciasp_func);
print_summary();
if (test_num - 1 != EXPECTED_TESTS)
putstr("# WARNING - EXPECTED TEST COUNT WRONG\n");
if (test_failed)
exit(1);
else
exit(0);
}