]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
kselftest: arm64: Check access to GCS after mprotect(PROT_NONE)
authorCatalin Marinas <catalin.marinas@arm.com>
Mon, 23 Feb 2026 17:45:32 +0000 (17:45 +0000)
committerWill Deacon <will@kernel.org>
Wed, 25 Feb 2026 19:53:58 +0000 (19:53 +0000)
A GCS mapping should not be accessible after mprotect(PROT_NONE). Add a
kselftest for this scenario.

Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
Cc: Shuah Khan <skhan@linuxfoundation.org>
Cc: Mark Brown <broonie@kernel.org>
Cc: Will Deacon <will@kernel.org>
Cc: David Hildenbrand <david@kernel.org>
Reviewed-by: Mark Brown <broonie@kernel.org>
Signed-off-by: Will Deacon <will@kernel.org>
tools/testing/selftests/arm64/signal/testcases/gcs_prot_none_fault.c [new file with mode: 0644]

diff --git a/tools/testing/selftests/arm64/signal/testcases/gcs_prot_none_fault.c b/tools/testing/selftests/arm64/signal/testcases/gcs_prot_none_fault.c
new file mode 100644 (file)
index 0000000..2259f45
--- /dev/null
@@ -0,0 +1,76 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2026 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;
+static bool post_mprotect;
+
+#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_prot_none_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]);
+
+       if (mprotect(gcs_page, sysconf(_SC_PAGE_SIZE), PROT_NONE) != 0) {
+               fprintf(stderr, "mprotect(PROT_NONE) failed: %d\n", errno);
+               return 0;
+       }
+       post_mprotect = true;
+
+       /* This should trigger a fault if PROT_NONE is honoured for the GCS page */
+       fprintf(stderr, "Read value after mprotect(PROT_NONE) 0x%lx\n", gcs_page[0]);
+       return 0;
+}
+
+static int gcs_prot_none_fault_signal(struct tdescr *td, siginfo_t *si,
+                                     ucontext_t *uc)
+{
+       ASSERT_GOOD_CONTEXT(uc);
+
+       /* A fault before mprotect(PROT_NONE) is unexpected. */
+       if (!post_mprotect)
+               return 0;
+
+       return 1;
+}
+
+struct tdescr tde = {
+       .name = "GCS PROT_NONE fault",
+       .descr = "Read from GCS after mprotect(PROT_NONE) segfaults",
+       .feats_required = FEAT_GCS,
+       .timeout = 3,
+       .sig_ok = SIGSEGV,
+       .sanity_disabled = true,
+       .init = alloc_gcs,
+       .trigger = gcs_prot_none_fault_trigger,
+       .run = gcs_prot_none_fault_signal,
+};