Browse Source
Add some infrastructure for testing gcs in userspace. Validate successful and trapped executions of GCSSTR. Reviewed-by: Pierrick Bouvier <pierrick.bouvier@linaro.org> Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Message-id: 20251008215613.300150-72-richard.henderson@linaro.org [PMM: fixed hardcoded tabs] Signed-off-by: Peter Maydell <peter.maydell@linaro.org>pull/305/head
committed by
Peter Maydell
3 changed files with 124 additions and 0 deletions
@ -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; }) |
|||
@ -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(); |
|||
} |
|||
Loading…
Reference in new issue