Commit 026ad4c1 authored by Dave Martin's avatar Dave Martin

arm64: bti: Import basic BTI tests

Import basic development tests for Branch Target Identification
(BTI, part of ARMv8.5-A).
Signed-off-by: default avatarDave Martin <Dave.Martin@arm.com>
parents
*.[od]
*~
btitest
nobtitest
This diff is collapsed.
# SPDX-License-Identifier: GPL-2.0-only
# Copyright (C) 2019 Arm Limited
# Original author: Dave Martin <Dave.Martin@arm.com>
CFLAGS = -O2 -g
CPPFLAGS =
CROSS_COMPILE = aarch64-linux-gnu-
CC = $(CROSS_COMPILE)gcc
TESTS = btitest nobtitest
CFLAGS_NOBTI = -DBTI=0
CFLAGS_BTI = -mbranch-protection=standard -DBTI=1
CFLAGS_COMMON = -ffreestanding -Wall -Wextra -MMD $(CFLAGS)
BTI_CC_COMMAND = \
$(CC) $(CPPFLAGS) $(CFLAGS_BTI) $(CFLAGS_COMMON) -c -o $@ $<
NOBTI_CC_COMMAND = \
$(CC) $(CPPFLAGS) $(CFLAGS_NOBTI) $(CFLAGS_COMMON) -c -o $@ $<
all: FORCE $(TESTS) ;
FORCE: ;
-include *.d
%-bti.o: %.c
$(BTI_CC_COMMAND)
%-bti.o: %.S
$(BTI_CC_COMMAND)
%-nobti.o: %.c
$(NOBTI_CC_COMMAND)
%-nobti.o: %.S
$(NOBTI_CC_COMMAND)
BTI_OBJS = \
btitest-bti.o \
signal-bti.o \
start-bti.o \
syscall-bti.o \
system-bti.o \
teststubs-bti.o \
trampoline-bti.o
btitest: $(BTI_OBJS)
CFLAGS-btitest = $(CFLAGS_BTI)
NOBTI_OBJS = \
btitest-nobti.o \
signal-nobti.o \
start-nobti.o \
syscall-nobti.o \
system-nobti.o \
teststubs-nobti.o \
trampoline-nobti.o
nobtitest: $(NOBTI_OBJS)
CFLAGS-nobtitest = $(CFLAGS_NOBTI)
$(TESTS):
$(CC) $(CPPFLAGS) $(CFLAGS-$@) $(CFLAGS_COMMON) -nostdlib -o $@ $^
clean: FORCE
$(RM) $(TESTS) *.[od]
Building
========
linux-tests$ make
If your toolchain's headers are not sufficiently up to date to contain
the BTI and Pointer authentication definitions, you may need to install
the headers from a new enough kernel version, e.g.:
linux$ make ARCH=arm64 headers_install
linux-tests$ make CPPFLAGS=-I<path to linux directory>/usr/include
Copy all the resulting binaries to your target filesystem:
btitest:
checks that BTI-enabled binaries see BTI working as expected.
nobtitest:
checks that non-BTI-enabled binaries do _not_ see BTI working.
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (C) 2019 Arm Limited
* Original author: Dave Martin <Dave.Martin@arm.com>
*/
#ifndef ASSEMBLER_H
#define ASSEMBLER_H
#define NT_GNU_PROPERTY_TYPE_0 5
#define GNU_PROPERTY_AARCH64_FEATURE_1_AND 0xc0000000
/* Bits for GNU_PROPERTY_AARCH64_FEATURE_1_BTI */
#define GNU_PROPERTY_AARCH64_FEATURE_1_BTI (1U << 0)
#define GNU_PROPERTY_AARCH64_FEATURE_1_PAC (1U << 1)
.macro startfn name:req
.globl \name
\name:
.macro endfn
.size \name, . - \name
.type \name, @function
.purgem endfn
.endm
.endm
.macro emit_aarch64_feature_1_and
.pushsection .note.gnu.property, "a"
.align 3
.long 2f - 1f
.long 6f - 3f
.long NT_GNU_PROPERTY_TYPE_0
1: .string "GNU"
2:
.align 3
3: .long GNU_PROPERTY_AARCH64_FEATURE_1_AND
.long 5f - 4f
4:
#if BTI
.long GNU_PROPERTY_AARCH64_FEATURE_1_PAC | \
GNU_PROPERTY_AARCH64_FEATURE_1_BTI
#else
.long 0
#endif
5:
.align 3
6:
.popsection
.endm
.macro paciasp
hint 0x19
.endm
.macro autiasp
hint 0x1d
.endm
.macro __bti_
hint 0x20
.endm
.macro __bti_c
hint 0x22
.endm
.macro __bti_j
hint 0x24
.endm
.macro __bti_jc
hint 0x26
.endm
.macro bti what=
__bti_\what
.endm
#endif /* ! ASSEMBLER_H */
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2019 Arm Limited
* Original author: Dave Martin <Dave.Martin@arm.com>
*/
#include "system.h"
#include <linux/errno.h>
#include <linux/auxvec.h>
#include <linux/signal.h>
#include <asm/hwcap.h>
#include <asm/sigcontext.h>
#include <asm/ucontext.h>
typedef struct ucontext ucontext_t;
#include "btitest.h"
#include "compiler.h"
#include "signal.h"
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);
}
#define puttestname(test_name, trampoline_name) do { \
putstr(test_name); \
putstr("/"); \
putstr(trampoline_name); \
} while (0)
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");
puttestname(current_test_name, current_trampoline_name);
putstr(":\tFAIL (unexpected SIGILL)\n");
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;
}
static int skip_all = 0;
static void __do_test(void (*trampoline)(void (*)(void)),
void (*fn)(void),
const char *trampoline_name,
const char *name,
int expect_sigill)
{
if (skip_all) {
puttestname(name, trampoline_name);
putstr(":\tSKIPPED\n");
return;
}
/* Branch Target exceptions should only happen in BTI binaries: */
if (!BTI)
expect_sigill = 0;
sigill_expected = expect_sigill;
sigill_received = 0;
current_test_name = name;
current_trampoline_name = trampoline_name;
trampoline(fn);
puttestname(name, trampoline_name);
if (expect_sigill && !sigill_received) {
putstr(":\tFAIL (no SIGILL)\n");
exit(1);
} else {
putstr(":\tPASS\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;
/* Gross hack for finding AT_HWCAP2 from the initial process stack: */
p = (void *const *)argcp + 1 + *argcp + 1; /* start of environment */
while (*p++) ; /* step over environment */
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;
}
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");
} else {
putstr("HWCAP2_BTI not present\n");
skip_all = 1;
}
putstr("Test binary");
if (!BTI)
putstr(" not");
putstr(" built for BTI\n");
putstr("---\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);
putstr("\nNo test failures.\n");
exit(0);
}
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (C) 2019 Arm Limited
* Original author: Dave Martin <Dave.Martin@arm.com>
*/
#ifndef BTITEST_H
#define BTITEST_H
/* Trampolines for calling the test stubs: */
void call_using_br_x0(void (*)(void));
void call_using_br_x16(void (*)(void));
void call_using_blr(void (*)(void));
/* Test stubs: */
void nohint_func(void);
void bti_none_func(void);
void bti_c_func(void);
void bti_j_func(void);
void bti_jc_func(void);
void paciasp_func(void);
#endif /* !BTITEST_H */
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (C) 2019 Arm Limited
* Original author: Dave Martin <Dave.Martin@arm.com>
*/
#ifndef COMPILER_H
#define COMPILER_H
#define __always_unused __attribute__((__unused__))
#define __noreturn __attribute__((__noreturn__))
#define __unreachable() __builtin_unreachable()
/* curse(e) has value e, but the compiler cannot assume so */
#define curse(e) ({ \
__typeof__(e) __curse_e = (e); \
asm ("" : "+r" (__curse_e)); \
__curse_e; \
})
#endif /* ! COMPILER_H */
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2019 Arm Limited
* Original author: Dave Martin <Dave.Martin@arm.com>
*/
#include "system.h"
#include "signal.h"
int sigemptyset(sigset_t *s)
{
unsigned int i;
for (i = 0; i < _NSIG_WORDS; ++i)
s->sig[i] = 0;
return 0;
}
int sigaddset(sigset_t *s, int n)
{
if (n < 1 || n > _NSIG)
return -EINVAL;
s->sig[(n - 1) / _NSIG_BPW] |= 1UL << (n - 1) % _NSIG_BPW;
return 0;
}
int sigaction(int n, struct sigaction *sa, const struct sigaction *old)
{
return syscall(__NR_rt_sigaction, n, sa, old, sizeof sa->sa_mask);
}
int sigprocmask(int how, const sigset_t *mask, sigset_t *old)
{
return syscall(__NR_rt_sigprocmask, how, mask, old, sizeof(*mask));
}
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (C) 2019 Arm Limited
* Original author: Dave Martin <Dave.Martin@arm.com>
*/
#ifndef SIGNAL_H
#define SIGNAL_H
#include <linux/signal.h>
#include "system.h"
typedef __sighandler_t sighandler_t;
int sigemptyset(sigset_t *s);
int sigaddset(sigset_t *s, int n);
int sigaction(int n, struct sigaction *sa, const struct sigaction *old);
int sigprocmask(int how, const sigset_t *mask, sigset_t *old);
#endif /* ! SIGNAL_H */
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2019 Arm Limited
* Original author: Dave Martin <Dave.Martin@arm.com>
*/
#include "assembler.h"
startfn _start
mov x0, sp
b start
endfn
emit_aarch64_feature_1_and
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2019 Arm Limited
* Original author: Dave Martin <Dave.Martin@arm.com>
*/
#include "assembler.h"
startfn syscall
bti c
mov w8, w0
mov x0, x1
mov x1, x2
mov x2, x3
mov x3, x4
mov x4, x5
mov x5, x6
mov x6, x7
svc #0
ret
endfn
emit_aarch64_feature_1_and
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2019 Arm Limited
* Original author: Dave Martin <Dave.Martin@arm.com>
*/
#include "system.h"
#include <asm/unistd.h>
#include "compiler.h"
void __noreturn exit(int n)
{
syscall(__NR_exit, n);
__unreachable();
}
ssize_t write(int fd, const void *buf, size_t size)
{
return syscall(__NR_write, fd, buf, size);
}
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (C) 2019 Arm Limited
* Original author: Dave Martin <Dave.Martin@arm.com>
*/
#ifndef SYSTEM_H
#define SYSTEM_H
#include <linux/types.h>
#include <linux/stddef.h>
typedef __kernel_size_t size_t;
typedef __kernel_ssize_t ssize_t;
#define NULL ((void *)0)
#include <linux/errno.h>
#include <asm/hwcap.h>
#include <asm/ptrace.h>
#include <asm/unistd.h>
#include "compiler.h"
long syscall(int nr, ...);
void __noreturn exit(int n);
ssize_t write(int fd, const void *buf, size_t size);
#ifndef PSR_BTYPE_SHIFT
#define PSR_BTYPE_SHIFT 10
#endif
#endif /* ! SYSTEM_H */
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2019 Arm Limited
* Original author: Dave Martin <Dave.Martin@arm.com>
*/
#include "assembler.h"
startfn bti_none_func
bti
ret
endfn
startfn bti_c_func
bti c
ret
endfn
startfn bti_j_func
bti j
ret
endfn
startfn bti_jc_func
bti jc
ret
endfn
startfn paciasp_func
paciasp
autiasp
ret
endfn
startfn nohint_func
ret
endfn
emit_aarch64_feature_1_and
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2019 Arm Limited
* Original author: Dave Martin <Dave.Martin@arm.com>
*/
#include "assembler.h"
startfn call_using_br_x0
bti c
br x0
endfn
startfn call_using_br_x16
bti c
mov x16, x0
br x16
endfn
startfn call_using_blr
paciasp
stp x29, x30, [sp, #-16]!
blr x0
ldp x29, x30, [sp], #16
autiasp
ret
endfn
emit_aarch64_feature_1_and
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment