Add some infrastructure for testing gcs in userspace. Validate successful and trapped executions of GCSSTR.
Signed-off-by: Richard Henderson <richard.hender...@linaro.org> --- tests/tcg/aarch64/gcs.h | 71 +++++++++++++++++++++++++++++++ tests/tcg/aarch64/gcsstr.c | 48 +++++++++++++++++++++ tests/tcg/aarch64/Makefile.target | 5 +++ 3 files changed, 124 insertions(+) create mode 100644 tests/tcg/aarch64/gcs.h create mode 100644 tests/tcg/aarch64/gcsstr.c diff --git a/tests/tcg/aarch64/gcs.h b/tests/tcg/aarch64/gcs.h new file mode 100644 index 0000000000..99cb4d4e38 --- /dev/null +++ b/tests/tcg/aarch64/gcs.h @@ -0,0 +1,71 @@ +/* + * Linux kernel fallback API definitions for GCS and test helpers. + * + * Copyright (c) 2025 Linaro Ltd + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include <assert.h> +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#include <stdint.h> +#include <unistd.h> +#include <errno.h> +#include <signal.h> +#include <sys/mman.h> +#include <sys/prctl.h> +#include <sys/syscall.h> + +#ifndef PR_GET_SHADOW_STACK_STATUS +#define PR_GET_SHADOW_STACK_STATUS 74 +#endif +#ifndef PR_SET_SHADOW_STACK_STATUS +#define PR_SET_SHADOW_STACK_STATUS 75 +#endif +#ifndef PR_LOCK_SHADOW_STACK_STATUS +#define PR_LOCK_SHADOW_STACK_STATUS 76 +#endif +#ifndef PR_SHADOW_STACK_ENABLE +# define PR_SHADOW_STACK_ENABLE (1 << 0) +# define PR_SHADOW_STACK_WRITE (1 << 1) +# define PR_SHADOW_STACK_PUSH (1 << 2) +#endif +#ifndef SHADOW_STACK_SET_TOKEN +#define SHADOW_STACK_SET_TOKEN (1 << 0) +#endif +#ifndef SHADOW_STACK_SET_MARKER +#define SHADOW_STACK_SET_MARKER (1 << 1) +#endif +#ifndef SEGV_CPERR +#define SEGV_CPERR 10 +#endif +#ifndef __NR_map_shadow_stack +#define __NR_map_shadow_stack 453 +#endif + +/* + * Macros, and implement the syscall inline, lest we fail + * the checked return from any function call. + */ +#define enable_gcs(flags) \ + do { \ + register long num __asm__ ("x8") = __NR_prctl; \ + register long arg1 __asm__ ("x0") = PR_SET_SHADOW_STACK_STATUS; \ + register long arg2 __asm__ ("x1") = PR_SHADOW_STACK_ENABLE | flags; \ + register long arg3 __asm__ ("x2") = 0; \ + register long arg4 __asm__ ("x3") = 0; \ + register long arg5 __asm__ ("x4") = 0; \ + asm volatile("svc #0" \ + : "+r"(arg1) \ + : "r"(arg2), "r"(arg3), "r"(arg4), "r"(arg5), "r"(num) \ + : "memory", "cc"); \ + if (arg1) { \ + errno = -arg1; \ + perror("PR_SET_SHADOW_STACK_STATUS"); \ + exit(2); \ + } \ + } while (0) + +#define gcspr() \ + ({ uint64_t *r; asm volatile("mrs %0, s3_3_c2_c5_1" : "=r"(r)); r; }) diff --git a/tests/tcg/aarch64/gcsstr.c b/tests/tcg/aarch64/gcsstr.c new file mode 100644 index 0000000000..b045aee925 --- /dev/null +++ b/tests/tcg/aarch64/gcsstr.c @@ -0,0 +1,48 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "gcs.h" + +/* + * A single garbage store to the gcs stack. + * The asm inside must be unique, so disallow inlining. + */ +void __attribute__((noinline)) +test_gcsstr(void) +{ + register uint64_t *ptr __asm__("x0") = gcspr(); + /* GCSSTR x1, x0 */ + __asm__("inst_gcsstr: .inst 0xd91f1c01" : : "r"(--ptr)); +} + +static void test_sigsegv(int sig, siginfo_t *info, void *vuc) +{ + ucontext_t *uc = vuc; + uint64_t inst_gcsstr; + + __asm__("adr %0, inst_gcsstr" : "=r"(inst_gcsstr)); + assert(uc->uc_mcontext.pc == inst_gcsstr); + assert(info->si_code == SEGV_CPERR); + /* TODO: Dig for ESR and verify syndrome. */ + exit(0); +} + +int main() +{ + struct sigaction sa = { + .sa_sigaction = test_sigsegv, + .sa_flags = SA_SIGINFO, + }; + + /* Enable GCSSTR and test the store succeeds. */ + enable_gcs(PR_SHADOW_STACK_WRITE); + test_gcsstr(); + + /* Disable GCSSTR and test the resulting sigsegv. */ + enable_gcs(0); + if (sigaction(SIGSEGV, &sa, NULL) < 0) { + perror("sigaction"); + exit(1); + } + test_gcsstr(); + abort(); +} diff --git a/tests/tcg/aarch64/Makefile.target b/tests/tcg/aarch64/Makefile.target index 16ddcf4f88..0347a36e8d 100644 --- a/tests/tcg/aarch64/Makefile.target +++ b/tests/tcg/aarch64/Makefile.target @@ -75,6 +75,11 @@ AARCH64_TESTS += $(SME_TESTS) $(SME_TESTS): CFLAGS += $(CROSS_AS_HAS_ARMV9_SME) endif +# GCS Tests +GCS_TESTS += gcsstr +AARCH64_TESTS += $(GCS_TESTS) +$(GCS_TESTS): gcs.h + # System Registers Tests AARCH64_TESTS += sysregs -- 2.43.0