Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

kselftest: arm64: Check access to GCS after mprotect(PROT_NONE)

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>

authored by

Catalin Marinas and committed by
Will Deacon
9d1a7c4a 47a8aad1

+76
+76
tools/testing/selftests/arm64/signal/testcases/gcs_prot_none_fault.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Copyright (C) 2026 ARM Limited 4 + */ 5 + 6 + #include <errno.h> 7 + #include <signal.h> 8 + #include <unistd.h> 9 + 10 + #include <sys/mman.h> 11 + #include <sys/prctl.h> 12 + 13 + #include "test_signals_utils.h" 14 + #include "testcases.h" 15 + 16 + static uint64_t *gcs_page; 17 + static bool post_mprotect; 18 + 19 + #ifndef __NR_map_shadow_stack 20 + #define __NR_map_shadow_stack 453 21 + #endif 22 + 23 + static bool alloc_gcs(struct tdescr *td) 24 + { 25 + long page_size = sysconf(_SC_PAGE_SIZE); 26 + 27 + gcs_page = (void *)syscall(__NR_map_shadow_stack, 0, 28 + page_size, 0); 29 + if (gcs_page == MAP_FAILED) { 30 + fprintf(stderr, "Failed to map %ld byte GCS: %d\n", 31 + page_size, errno); 32 + return false; 33 + } 34 + 35 + return true; 36 + } 37 + 38 + static int gcs_prot_none_fault_trigger(struct tdescr *td) 39 + { 40 + /* Verify that the page is readable (ie, not completely unmapped) */ 41 + fprintf(stderr, "Read value 0x%lx\n", gcs_page[0]); 42 + 43 + if (mprotect(gcs_page, sysconf(_SC_PAGE_SIZE), PROT_NONE) != 0) { 44 + fprintf(stderr, "mprotect(PROT_NONE) failed: %d\n", errno); 45 + return 0; 46 + } 47 + post_mprotect = true; 48 + 49 + /* This should trigger a fault if PROT_NONE is honoured for the GCS page */ 50 + fprintf(stderr, "Read value after mprotect(PROT_NONE) 0x%lx\n", gcs_page[0]); 51 + return 0; 52 + } 53 + 54 + static int gcs_prot_none_fault_signal(struct tdescr *td, siginfo_t *si, 55 + ucontext_t *uc) 56 + { 57 + ASSERT_GOOD_CONTEXT(uc); 58 + 59 + /* A fault before mprotect(PROT_NONE) is unexpected. */ 60 + if (!post_mprotect) 61 + return 0; 62 + 63 + return 1; 64 + } 65 + 66 + struct tdescr tde = { 67 + .name = "GCS PROT_NONE fault", 68 + .descr = "Read from GCS after mprotect(PROT_NONE) segfaults", 69 + .feats_required = FEAT_GCS, 70 + .timeout = 3, 71 + .sig_ok = SIGSEGV, 72 + .sanity_disabled = true, 73 + .init = alloc_gcs, 74 + .trigger = gcs_prot_none_fault_trigger, 75 + .run = gcs_prot_none_fault_signal, 76 + };