Skip to content

Commit 9d1a7c4

Browse files
ctmarinaswilldeacon
authored andcommitted
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>
1 parent 47a8aad commit 9d1a7c4

1 file changed

Lines changed: 76 additions & 0 deletions

File tree

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
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+
};

0 commit comments

Comments
 (0)