xref: /linux/tools/testing/selftests/arm64/gcs/gcs-locking.c (revision bb29b957eaaa15be33243550d9ee576c2d601669)
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_VARIANT_ADD(valid_modes, write)
81 {
82 	.mode = PR_SHADOW_STACK_WRITE,
83 };
84 
85 FIXTURE_VARIANT_ADD(valid_modes, push)
86 {
87 	.mode = PR_SHADOW_STACK_PUSH,
88 };
89 
90 FIXTURE_VARIANT_ADD(valid_modes, write_push)
91 {
92 	.mode = PR_SHADOW_STACK_WRITE | PR_SHADOW_STACK_PUSH,
93 };
94 
95 FIXTURE_SETUP(valid_modes)
96 {
97 }
98 
99 FIXTURE_TEARDOWN(valid_modes)
100 {
101 }
102 
103 /* We can set the mode at all */
104 TEST_F(valid_modes, set)
105 {
106 	int ret;
107 
108 	ret = my_syscall2(__NR_prctl, PR_SET_SHADOW_STACK_STATUS,
109 			  variant->mode);
110 	ASSERT_EQ(ret, 0);
111 
112 	_exit(0);
113 }
114 
115 /* Enabling, locking then disabling is rejected */
116 TEST_F(valid_modes, enable_lock_disable)
117 {
118 	unsigned long mode;
119 	int ret;
120 
121 	ret = my_syscall2(__NR_prctl, PR_SET_SHADOW_STACK_STATUS,
122 			  variant->mode);
123 	ASSERT_EQ(ret, 0);
124 
125 	ret = prctl(PR_GET_SHADOW_STACK_STATUS, &mode, 0, 0, 0);
126 	ASSERT_EQ(ret, 0);
127 	ASSERT_EQ(mode, variant->mode);
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, 0);
133 	ASSERT_EQ(ret, -EPERM);
134 
135 	_exit(0);
136 }
137 
138 /* Locking then enabling is rejected */
139 TEST_F(valid_modes, lock_enable)
140 {
141 	unsigned long mode;
142 	int ret;
143 
144 	ret = prctl(PR_LOCK_SHADOW_STACK_STATUS, variant->mode, 0, 0, 0);
145 	ASSERT_EQ(ret, 0);
146 
147 	ret = my_syscall2(__NR_prctl, PR_SET_SHADOW_STACK_STATUS,
148 			  variant->mode);
149 	ASSERT_EQ(ret, -EPERM);
150 
151 	ret = prctl(PR_GET_SHADOW_STACK_STATUS, &mode, 0, 0, 0);
152 	ASSERT_EQ(ret, 0);
153 	ASSERT_EQ(mode, 0);
154 
155 	_exit(0);
156 }
157 
158 /* Locking then changing other modes is fine */
159 TEST_F(valid_modes, lock_enable_disable_others)
160 {
161 	unsigned long mode;
162 	int ret;
163 
164 	ret = my_syscall2(__NR_prctl, PR_SET_SHADOW_STACK_STATUS,
165 			  variant->mode);
166 	ASSERT_EQ(ret, 0);
167 
168 	ret = prctl(PR_GET_SHADOW_STACK_STATUS, &mode, 0, 0, 0);
169 	ASSERT_EQ(ret, 0);
170 	ASSERT_EQ(mode, variant->mode);
171 
172 	ret = prctl(PR_LOCK_SHADOW_STACK_STATUS, variant->mode, 0, 0, 0);
173 	ASSERT_EQ(ret, 0);
174 
175 	ret = my_syscall2(__NR_prctl, PR_SET_SHADOW_STACK_STATUS,
176 			  PR_SHADOW_STACK_ALL_MODES);
177 	ASSERT_EQ(ret, 0);
178 
179 	ret = prctl(PR_GET_SHADOW_STACK_STATUS, &mode, 0, 0, 0);
180 	ASSERT_EQ(ret, 0);
181 	ASSERT_EQ(mode, PR_SHADOW_STACK_ALL_MODES);
182 
183 	ret = my_syscall2(__NR_prctl, PR_SET_SHADOW_STACK_STATUS,
184 			  variant->mode);
185 	ASSERT_EQ(ret, 0);
186 
187 	ret = prctl(PR_GET_SHADOW_STACK_STATUS, &mode, 0, 0, 0);
188 	ASSERT_EQ(ret, 0);
189 	ASSERT_EQ(mode, variant->mode);
190 
191 	_exit(0);
192 }
193 
194 int main(int argc, char **argv)
195 {
196 	unsigned long mode;
197 	int ret;
198 
199 	if (!(getauxval(AT_HWCAP) & HWCAP_GCS))
200 		ksft_exit_skip("SKIP GCS not supported\n");
201 
202 	ret = prctl(PR_GET_SHADOW_STACK_STATUS, &mode, 0, 0, 0);
203 	if (ret) {
204 		ksft_print_msg("Failed to read GCS state: %d\n", ret);
205 		return EXIT_FAILURE;
206 	}
207 
208 	if (mode & PR_SHADOW_STACK_ENABLE) {
209 		ksft_print_msg("GCS was enabled, test unsupported\n");
210 		return KSFT_SKIP;
211 	}
212 
213 	return test_harness_run(argc, argv);
214 }
215