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