Skip to content

Commit 58d69a3

Browse files
brooniectmarinas
authored andcommitted
kselftest/arm64: Add test coverage for GCS mode locking
Verify that we can lock individual GCS mode bits, that other modes aren't affected and as a side effect also that every combination of modes can be enabled. Normally the inability to reenable GCS after disabling it would be an issue with testing but fortunately the kselftest_harness runs each test within a fork()ed child. This can be inconvenient for some kinds of testing but here it means that each test is in a separate thread and therefore won't be affected by other tests in the suite. Once we get toolchains with support for enabling GCS by default we will need to take care to not do that in the build system but there are no such toolchains yet so it is not yet an issue. Reviewed-by: Thiago Jung Bauermann <[email protected]> Tested-by: Thiago Jung Bauermann <[email protected]> Signed-off-by: Mark Brown <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Catalin Marinas <[email protected]>
1 parent a505a52 commit 58d69a3

File tree

3 files changed

+202
-1
lines changed

3 files changed

+202
-1
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
basic-gcs
22
libc-gcs
3+
gcs-locking

tools/testing/selftests/arm64/gcs/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
# nolibc.
77
#
88

9-
TEST_GEN_PROGS := basic-gcs libc-gcs
9+
TEST_GEN_PROGS := basic-gcs libc-gcs gcs-locking
1010

1111
LDLIBS+=-lpthread
1212

Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
// SPDX-License-Identifier: GPL-2.0-only
2+
/*
3+
* Copyright (C) 2023 ARM Limited.
4+
*
5+
* Tests for GCS mode locking. These tests rely on both having GCS
6+
* unconfigured on entry and on the kselftest harness running each
7+
* test in a fork()ed process which will have it's own mode.
8+
*/
9+
10+
#include <limits.h>
11+
12+
#include <sys/auxv.h>
13+
#include <sys/prctl.h>
14+
15+
#include <asm/hwcap.h>
16+
17+
#include "kselftest_harness.h"
18+
19+
#include "gcs-util.h"
20+
21+
#define my_syscall2(num, arg1, arg2) \
22+
({ \
23+
register long _num __asm__ ("x8") = (num); \
24+
register long _arg1 __asm__ ("x0") = (long)(arg1); \
25+
register long _arg2 __asm__ ("x1") = (long)(arg2); \
26+
register long _arg3 __asm__ ("x2") = 0; \
27+
register long _arg4 __asm__ ("x3") = 0; \
28+
register long _arg5 __asm__ ("x4") = 0; \
29+
\
30+
__asm__ volatile ( \
31+
"svc #0\n" \
32+
: "=r"(_arg1) \
33+
: "r"(_arg1), "r"(_arg2), \
34+
"r"(_arg3), "r"(_arg4), \
35+
"r"(_arg5), "r"(_num) \
36+
: "memory", "cc" \
37+
); \
38+
_arg1; \
39+
})
40+
41+
/* No mode bits are rejected for locking */
42+
TEST(lock_all_modes)
43+
{
44+
int ret;
45+
46+
ret = prctl(PR_LOCK_SHADOW_STACK_STATUS, ULONG_MAX, 0, 0, 0);
47+
ASSERT_EQ(ret, 0);
48+
}
49+
50+
FIXTURE(valid_modes)
51+
{
52+
};
53+
54+
FIXTURE_VARIANT(valid_modes)
55+
{
56+
unsigned long mode;
57+
};
58+
59+
FIXTURE_VARIANT_ADD(valid_modes, enable)
60+
{
61+
.mode = PR_SHADOW_STACK_ENABLE,
62+
};
63+
64+
FIXTURE_VARIANT_ADD(valid_modes, enable_write)
65+
{
66+
.mode = PR_SHADOW_STACK_ENABLE | PR_SHADOW_STACK_WRITE,
67+
};
68+
69+
FIXTURE_VARIANT_ADD(valid_modes, enable_push)
70+
{
71+
.mode = PR_SHADOW_STACK_ENABLE | PR_SHADOW_STACK_PUSH,
72+
};
73+
74+
FIXTURE_VARIANT_ADD(valid_modes, enable_write_push)
75+
{
76+
.mode = PR_SHADOW_STACK_ENABLE | PR_SHADOW_STACK_WRITE |
77+
PR_SHADOW_STACK_PUSH,
78+
};
79+
80+
FIXTURE_SETUP(valid_modes)
81+
{
82+
}
83+
84+
FIXTURE_TEARDOWN(valid_modes)
85+
{
86+
}
87+
88+
/* We can set the mode at all */
89+
TEST_F(valid_modes, set)
90+
{
91+
int ret;
92+
93+
ret = my_syscall2(__NR_prctl, PR_SET_SHADOW_STACK_STATUS,
94+
variant->mode);
95+
ASSERT_EQ(ret, 0);
96+
97+
_exit(0);
98+
}
99+
100+
/* Enabling, locking then disabling is rejected */
101+
TEST_F(valid_modes, enable_lock_disable)
102+
{
103+
unsigned long mode;
104+
int ret;
105+
106+
ret = my_syscall2(__NR_prctl, PR_SET_SHADOW_STACK_STATUS,
107+
variant->mode);
108+
ASSERT_EQ(ret, 0);
109+
110+
ret = prctl(PR_GET_SHADOW_STACK_STATUS, &mode, 0, 0, 0);
111+
ASSERT_EQ(ret, 0);
112+
ASSERT_EQ(mode, variant->mode);
113+
114+
ret = prctl(PR_LOCK_SHADOW_STACK_STATUS, variant->mode, 0, 0, 0);
115+
ASSERT_EQ(ret, 0);
116+
117+
ret = my_syscall2(__NR_prctl, PR_SET_SHADOW_STACK_STATUS, 0);
118+
ASSERT_EQ(ret, -EBUSY);
119+
120+
_exit(0);
121+
}
122+
123+
/* Locking then enabling is rejected */
124+
TEST_F(valid_modes, lock_enable)
125+
{
126+
unsigned long mode;
127+
int ret;
128+
129+
ret = prctl(PR_LOCK_SHADOW_STACK_STATUS, variant->mode, 0, 0, 0);
130+
ASSERT_EQ(ret, 0);
131+
132+
ret = my_syscall2(__NR_prctl, PR_SET_SHADOW_STACK_STATUS,
133+
variant->mode);
134+
ASSERT_EQ(ret, -EBUSY);
135+
136+
ret = prctl(PR_GET_SHADOW_STACK_STATUS, &mode, 0, 0, 0);
137+
ASSERT_EQ(ret, 0);
138+
ASSERT_EQ(mode, 0);
139+
140+
_exit(0);
141+
}
142+
143+
/* Locking then changing other modes is fine */
144+
TEST_F(valid_modes, lock_enable_disable_others)
145+
{
146+
unsigned long mode;
147+
int ret;
148+
149+
ret = my_syscall2(__NR_prctl, PR_SET_SHADOW_STACK_STATUS,
150+
variant->mode);
151+
ASSERT_EQ(ret, 0);
152+
153+
ret = prctl(PR_GET_SHADOW_STACK_STATUS, &mode, 0, 0, 0);
154+
ASSERT_EQ(ret, 0);
155+
ASSERT_EQ(mode, variant->mode);
156+
157+
ret = prctl(PR_LOCK_SHADOW_STACK_STATUS, variant->mode, 0, 0, 0);
158+
ASSERT_EQ(ret, 0);
159+
160+
ret = my_syscall2(__NR_prctl, PR_SET_SHADOW_STACK_STATUS,
161+
PR_SHADOW_STACK_ALL_MODES);
162+
ASSERT_EQ(ret, 0);
163+
164+
ret = prctl(PR_GET_SHADOW_STACK_STATUS, &mode, 0, 0, 0);
165+
ASSERT_EQ(ret, 0);
166+
ASSERT_EQ(mode, PR_SHADOW_STACK_ALL_MODES);
167+
168+
169+
ret = my_syscall2(__NR_prctl, PR_SET_SHADOW_STACK_STATUS,
170+
variant->mode);
171+
ASSERT_EQ(ret, 0);
172+
173+
ret = prctl(PR_GET_SHADOW_STACK_STATUS, &mode, 0, 0, 0);
174+
ASSERT_EQ(ret, 0);
175+
ASSERT_EQ(mode, variant->mode);
176+
177+
_exit(0);
178+
}
179+
180+
int main(int argc, char **argv)
181+
{
182+
unsigned long mode;
183+
int ret;
184+
185+
if (!(getauxval(AT_HWCAP) & HWCAP_GCS))
186+
ksft_exit_skip("SKIP GCS not supported\n");
187+
188+
ret = prctl(PR_GET_SHADOW_STACK_STATUS, &mode, 0, 0, 0);
189+
if (ret) {
190+
ksft_print_msg("Failed to read GCS state: %d\n", ret);
191+
return EXIT_FAILURE;
192+
}
193+
194+
if (mode & PR_SHADOW_STACK_ENABLE) {
195+
ksft_print_msg("GCS was enabled, test unsupported\n");
196+
return KSFT_SKIP;
197+
}
198+
199+
return test_harness_run(argc, argv);
200+
}

0 commit comments

Comments
 (0)