Do some testing of the signal handling for GCS, checking that a GCS
frame has the expected information in it and that the expected signals
are delivered with invalid operations.

Reviewed-by: Thiago Jung Bauermann <thiago.bauerm...@linaro.org>
Signed-off-by: Mark Brown <broo...@kernel.org>
---
 tools/testing/selftests/arm64/signal/.gitignore    |  1 +
 .../selftests/arm64/signal/test_signals_utils.h    | 10 +++
 .../arm64/signal/testcases/gcs_exception_fault.c   | 62 +++++++++++++++
 .../selftests/arm64/signal/testcases/gcs_frame.c   | 88 ++++++++++++++++++++++
 .../arm64/signal/testcases/gcs_write_fault.c       | 67 ++++++++++++++++
 5 files changed, 228 insertions(+)

diff --git a/tools/testing/selftests/arm64/signal/.gitignore 
b/tools/testing/selftests/arm64/signal/.gitignore
index 1ce5b5eac386..75d691c13207 100644
--- a/tools/testing/selftests/arm64/signal/.gitignore
+++ b/tools/testing/selftests/arm64/signal/.gitignore
@@ -2,6 +2,7 @@
 mangle_*
 fake_sigreturn_*
 fpmr_*
+gcs_*
 sme_*
 ssve_*
 sve_*
diff --git a/tools/testing/selftests/arm64/signal/test_signals_utils.h 
b/tools/testing/selftests/arm64/signal/test_signals_utils.h
index 1e80808ee105..36fc12b3cd60 100644
--- a/tools/testing/selftests/arm64/signal/test_signals_utils.h
+++ b/tools/testing/selftests/arm64/signal/test_signals_utils.h
@@ -6,6 +6,7 @@
 
 #include <assert.h>
 #include <stdio.h>
+#include <stdint.h>
 #include <string.h>
 
 #include <linux/compiler.h>
@@ -47,6 +48,15 @@ void test_result(struct tdescr *td);
                _arg1;                                                  \
        })
 
+static inline __attribute__((always_inline)) uint64_t get_gcspr_el0(void)
+{
+       uint64_t val;
+
+       asm volatile("mrs %0, S3_3_C2_C5_1" : "=r" (val));
+
+       return val;
+}
+
 static inline bool feats_ok(struct tdescr *td)
 {
        if (td->feats_incompatible & td->feats_supported)
diff --git 
a/tools/testing/selftests/arm64/signal/testcases/gcs_exception_fault.c 
b/tools/testing/selftests/arm64/signal/testcases/gcs_exception_fault.c
new file mode 100644
index 000000000000..6228448b2ae7
--- /dev/null
+++ b/tools/testing/selftests/arm64/signal/testcases/gcs_exception_fault.c
@@ -0,0 +1,62 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 ARM Limited
+ */
+
+#include <errno.h>
+#include <signal.h>
+#include <unistd.h>
+
+#include <sys/mman.h>
+#include <sys/prctl.h>
+
+#include "test_signals_utils.h"
+#include "testcases.h"
+
+/*
+ * We should get this from asm/siginfo.h but the testsuite is being
+ * clever with redefining siginfo_t.
+ */
+#ifndef SEGV_CPERR
+#define SEGV_CPERR 10
+#endif
+
+static inline void gcsss1(uint64_t Xt)
+{
+       asm volatile (
+               "sys #3, C7, C7, #2, %0\n"
+               :
+               : "rZ" (Xt)
+               : "memory");
+}
+
+static int gcs_op_fault_trigger(struct tdescr *td)
+{
+       /*
+        * The slot below our current GCS should be in a valid GCS but
+        * must not have a valid cap in it.
+        */
+       gcsss1(get_gcspr_el0() - 8);
+
+       return 0;
+}
+
+static int gcs_op_fault_signal(struct tdescr *td, siginfo_t *si,
+                                 ucontext_t *uc)
+{
+       ASSERT_GOOD_CONTEXT(uc);
+
+       return 1;
+}
+
+struct tdescr tde = {
+       .name = "Invalid GCS operation",
+       .descr = "An invalid GCS operation generates the expected signal",
+       .feats_required = FEAT_GCS,
+       .timeout = 3,
+       .sig_ok = SIGSEGV,
+       .sig_ok_code = SEGV_CPERR,
+       .sanity_disabled = true,
+       .trigger = gcs_op_fault_trigger,
+       .run = gcs_op_fault_signal,
+};
diff --git a/tools/testing/selftests/arm64/signal/testcases/gcs_frame.c 
b/tools/testing/selftests/arm64/signal/testcases/gcs_frame.c
new file mode 100644
index 000000000000..b405d82321da
--- /dev/null
+++ b/tools/testing/selftests/arm64/signal/testcases/gcs_frame.c
@@ -0,0 +1,88 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 ARM Limited
+ */
+
+#include <signal.h>
+#include <ucontext.h>
+#include <sys/prctl.h>
+
+#include "test_signals_utils.h"
+#include "testcases.h"
+
+static union {
+       ucontext_t uc;
+       char buf[1024 * 64];
+} context;
+
+static int gcs_regs(struct tdescr *td, siginfo_t *si, ucontext_t *uc)
+{
+       size_t offset;
+       struct _aarch64_ctx *head = GET_BUF_RESV_HEAD(context);
+       struct gcs_context *gcs;
+       unsigned long expected, gcspr;
+       uint64_t *u64_val;
+       int ret;
+
+       ret = prctl(PR_GET_SHADOW_STACK_STATUS, &expected, 0, 0, 0);
+       if (ret != 0) {
+               fprintf(stderr, "Unable to query GCS status\n");
+               return 1;
+       }
+
+       /* We expect a cap to be added to the GCS in the signal frame */
+       gcspr = get_gcspr_el0();
+       gcspr -= 8;
+       fprintf(stderr, "Expecting GCSPR_EL0 %lx\n", gcspr);
+
+       if (!get_current_context(td, &context.uc, sizeof(context))) {
+               fprintf(stderr, "Failed getting context\n");
+               return 1;
+       }
+
+       /* Ensure that the signal restore token was consumed */
+       u64_val = (uint64_t *)get_gcspr_el0() + 1;
+       if (*u64_val) {
+               fprintf(stderr, "GCS value at %p is %lx not 0\n",
+                       u64_val, *u64_val);
+               return 1;
+       }
+
+       fprintf(stderr, "Got context\n");
+
+       head = get_header(head, GCS_MAGIC, GET_BUF_RESV_SIZE(context),
+                         &offset);
+       if (!head) {
+               fprintf(stderr, "No GCS context\n");
+               return 1;
+       }
+
+       gcs = (struct gcs_context *)head;
+
+       /* Basic size validation is done in get_current_context() */
+
+       if (gcs->features_enabled != expected) {
+               fprintf(stderr, "Features enabled %llx but expected %lx\n",
+                       gcs->features_enabled, expected);
+               return 1;
+       }
+
+       if (gcs->gcspr != gcspr) {
+               fprintf(stderr, "Got GCSPR %llx but expected %lx\n",
+                       gcs->gcspr, gcspr);
+               return 1;
+       }
+
+       fprintf(stderr, "GCS context validated\n");
+       td->pass = 1;
+
+       return 0;
+}
+
+struct tdescr tde = {
+       .name = "GCS basics",
+       .descr = "Validate a GCS signal context",
+       .feats_required = FEAT_GCS,
+       .timeout = 3,
+       .run = gcs_regs,
+};
diff --git a/tools/testing/selftests/arm64/signal/testcases/gcs_write_fault.c 
b/tools/testing/selftests/arm64/signal/testcases/gcs_write_fault.c
new file mode 100644
index 000000000000..faeabb18c4b2
--- /dev/null
+++ b/tools/testing/selftests/arm64/signal/testcases/gcs_write_fault.c
@@ -0,0 +1,67 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 ARM Limited
+ */
+
+#include <errno.h>
+#include <signal.h>
+#include <unistd.h>
+
+#include <sys/mman.h>
+#include <sys/prctl.h>
+
+#include "test_signals_utils.h"
+#include "testcases.h"
+
+static uint64_t *gcs_page;
+
+#ifndef __NR_map_shadow_stack
+#define __NR_map_shadow_stack 453
+#endif
+
+static bool alloc_gcs(struct tdescr *td)
+{
+       long page_size = sysconf(_SC_PAGE_SIZE);
+
+       gcs_page = (void *)syscall(__NR_map_shadow_stack, 0,
+                                  page_size, 0);
+       if (gcs_page == MAP_FAILED) {
+               fprintf(stderr, "Failed to map %ld byte GCS: %d\n",
+                       page_size, errno);
+               return false;
+       }
+
+       return true;
+}
+
+static int gcs_write_fault_trigger(struct tdescr *td)
+{
+       /* Verify that the page is readable (ie, not completely unmapped) */
+       fprintf(stderr, "Read value 0x%lx\n", gcs_page[0]);
+
+       /* A regular write should trigger a fault */
+       gcs_page[0] = EINVAL;
+
+       return 0;
+}
+
+static int gcs_write_fault_signal(struct tdescr *td, siginfo_t *si,
+                                 ucontext_t *uc)
+{
+       ASSERT_GOOD_CONTEXT(uc);
+
+       return 1;
+}
+
+
+struct tdescr tde = {
+       .name = "GCS write fault",
+       .descr = "Normal writes to a GCS segfault",
+       .feats_required = FEAT_GCS,
+       .timeout = 3,
+       .sig_ok = SIGSEGV,
+       .sanity_disabled = true,
+       .init = alloc_gcs,
+       .trigger = gcs_write_fault_trigger,
+       .run = gcs_write_fault_signal,
+};

-- 
2.39.2


Reply via email to