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
alloc_gcs(struct tdescr * td)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
gcs_prot_none_fault_trigger(struct tdescr * td)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
gcs_prot_none_fault_signal(struct tdescr * td,siginfo_t * si,ucontext_t * uc)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 };
77