Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/tools/testing/selftests/arm64/gcs/basic-gcs.c
29270 views
1
// SPDX-License-Identifier: GPL-2.0-only
2
/*
3
* Copyright (C) 2023 ARM Limited.
4
*/
5
6
#include <limits.h>
7
#include <stdbool.h>
8
9
#include <linux/prctl.h>
10
11
#include <sys/mman.h>
12
#include <asm/mman.h>
13
#include <asm/hwcap.h>
14
#include <linux/sched.h>
15
16
#include "kselftest.h"
17
#include "gcs-util.h"
18
19
/* nolibc doesn't have sysconf(), just hard code the maximum */
20
static size_t page_size = 65536;
21
22
static __attribute__((noinline)) void valid_gcs_function(void)
23
{
24
/* Do something the compiler can't optimise out */
25
my_syscall1(__NR_prctl, PR_SVE_GET_VL);
26
}
27
28
static inline int gcs_set_status(unsigned long mode)
29
{
30
bool enabling = mode & PR_SHADOW_STACK_ENABLE;
31
int ret;
32
unsigned long new_mode;
33
34
/*
35
* The prctl takes 1 argument but we need to ensure that the
36
* other 3 values passed in registers to the syscall are zero
37
* since the kernel validates them.
38
*/
39
ret = my_syscall5(__NR_prctl, PR_SET_SHADOW_STACK_STATUS, mode,
40
0, 0, 0);
41
42
if (ret == 0) {
43
ret = my_syscall5(__NR_prctl, PR_GET_SHADOW_STACK_STATUS,
44
&new_mode, 0, 0, 0);
45
if (ret == 0) {
46
if (new_mode != mode) {
47
ksft_print_msg("Mode set to %lx not %lx\n",
48
new_mode, mode);
49
ret = -EINVAL;
50
}
51
} else {
52
ksft_print_msg("Failed to validate mode: %d\n", ret);
53
}
54
55
if (enabling != chkfeat_gcs()) {
56
ksft_print_msg("%senabled by prctl but %senabled in CHKFEAT\n",
57
enabling ? "" : "not ",
58
chkfeat_gcs() ? "" : "not ");
59
ret = -EINVAL;
60
}
61
}
62
63
return ret;
64
}
65
66
/* Try to read the status */
67
static bool read_status(void)
68
{
69
unsigned long state;
70
int ret;
71
72
ret = my_syscall5(__NR_prctl, PR_GET_SHADOW_STACK_STATUS,
73
&state, 0, 0, 0);
74
if (ret != 0) {
75
ksft_print_msg("Failed to read state: %d\n", ret);
76
return false;
77
}
78
79
return state & PR_SHADOW_STACK_ENABLE;
80
}
81
82
/* Just a straight enable */
83
static bool base_enable(void)
84
{
85
int ret;
86
87
ret = gcs_set_status(PR_SHADOW_STACK_ENABLE);
88
if (ret) {
89
ksft_print_msg("PR_SHADOW_STACK_ENABLE failed %d\n", ret);
90
return false;
91
}
92
93
return true;
94
}
95
96
/* Check we can read GCSPR_EL0 when GCS is enabled */
97
static bool read_gcspr_el0(void)
98
{
99
unsigned long *gcspr_el0;
100
101
ksft_print_msg("GET GCSPR\n");
102
gcspr_el0 = get_gcspr();
103
ksft_print_msg("GCSPR_EL0 is %p\n", gcspr_el0);
104
105
return true;
106
}
107
108
/* Also allow writes to stack */
109
static bool enable_writeable(void)
110
{
111
int ret;
112
113
ret = gcs_set_status(PR_SHADOW_STACK_ENABLE | PR_SHADOW_STACK_WRITE);
114
if (ret) {
115
ksft_print_msg("PR_SHADOW_STACK_ENABLE writeable failed: %d\n", ret);
116
return false;
117
}
118
119
ret = gcs_set_status(PR_SHADOW_STACK_ENABLE);
120
if (ret) {
121
ksft_print_msg("failed to restore plain enable %d\n", ret);
122
return false;
123
}
124
125
return true;
126
}
127
128
/* Also allow writes to stack */
129
static bool enable_push_pop(void)
130
{
131
int ret;
132
133
ret = gcs_set_status(PR_SHADOW_STACK_ENABLE | PR_SHADOW_STACK_PUSH);
134
if (ret) {
135
ksft_print_msg("PR_SHADOW_STACK_ENABLE with push failed: %d\n",
136
ret);
137
return false;
138
}
139
140
ret = gcs_set_status(PR_SHADOW_STACK_ENABLE);
141
if (ret) {
142
ksft_print_msg("failed to restore plain enable %d\n", ret);
143
return false;
144
}
145
146
return true;
147
}
148
149
/* Enable GCS and allow everything */
150
static bool enable_all(void)
151
{
152
int ret;
153
154
ret = gcs_set_status(PR_SHADOW_STACK_ENABLE | PR_SHADOW_STACK_PUSH |
155
PR_SHADOW_STACK_WRITE);
156
if (ret) {
157
ksft_print_msg("PR_SHADOW_STACK_ENABLE with everything failed: %d\n",
158
ret);
159
return false;
160
}
161
162
ret = gcs_set_status(PR_SHADOW_STACK_ENABLE);
163
if (ret) {
164
ksft_print_msg("failed to restore plain enable %d\n", ret);
165
return false;
166
}
167
168
return true;
169
}
170
171
static bool enable_invalid(void)
172
{
173
int ret = gcs_set_status(ULONG_MAX);
174
if (ret == 0) {
175
ksft_print_msg("GCS_SET_STATUS %lx succeeded\n", ULONG_MAX);
176
return false;
177
}
178
179
return true;
180
}
181
182
/* Map a GCS */
183
static bool map_guarded_stack(void)
184
{
185
int ret;
186
uint64_t *buf;
187
uint64_t expected_cap;
188
int elem;
189
bool pass = true;
190
191
buf = (void *)my_syscall3(__NR_map_shadow_stack, 0, page_size,
192
SHADOW_STACK_SET_MARKER |
193
SHADOW_STACK_SET_TOKEN);
194
if (buf == MAP_FAILED) {
195
ksft_print_msg("Failed to map %lu byte GCS: %d\n",
196
page_size, errno);
197
return false;
198
}
199
ksft_print_msg("Mapped GCS at %p-%p\n", buf,
200
(void *)((uint64_t)buf + page_size));
201
202
/* The top of the newly allocated region should be 0 */
203
elem = (page_size / sizeof(uint64_t)) - 1;
204
if (buf[elem]) {
205
ksft_print_msg("Last entry is 0x%llx not 0x0\n", buf[elem]);
206
pass = false;
207
}
208
209
/* Then a valid cap token */
210
elem--;
211
expected_cap = ((uint64_t)buf + page_size - 16);
212
expected_cap &= GCS_CAP_ADDR_MASK;
213
expected_cap |= GCS_CAP_VALID_TOKEN;
214
if (buf[elem] != expected_cap) {
215
ksft_print_msg("Cap entry is 0x%llx not 0x%llx\n",
216
buf[elem], expected_cap);
217
pass = false;
218
}
219
ksft_print_msg("cap token is 0x%llx\n", buf[elem]);
220
221
/* The rest should be zeros */
222
for (elem = 0; elem < page_size / sizeof(uint64_t) - 2; elem++) {
223
if (!buf[elem])
224
continue;
225
ksft_print_msg("GCS slot %d is 0x%llx not 0x0\n",
226
elem, buf[elem]);
227
pass = false;
228
}
229
230
ret = munmap(buf, page_size);
231
if (ret != 0) {
232
ksft_print_msg("Failed to unmap %ld byte GCS: %d\n",
233
page_size, errno);
234
pass = false;
235
}
236
237
return pass;
238
}
239
240
/* A fork()ed process can run */
241
static bool test_fork(void)
242
{
243
unsigned long child_mode;
244
int ret, status;
245
pid_t pid;
246
bool pass = true;
247
248
pid = fork();
249
if (pid == -1) {
250
ksft_print_msg("fork() failed: %d\n", errno);
251
pass = false;
252
goto out;
253
}
254
if (pid == 0) {
255
/* In child, make sure we can call a function, read
256
* the GCS pointer and status and then exit */
257
valid_gcs_function();
258
get_gcspr();
259
260
ret = my_syscall5(__NR_prctl, PR_GET_SHADOW_STACK_STATUS,
261
&child_mode, 0, 0, 0);
262
if (ret == 0 && !(child_mode & PR_SHADOW_STACK_ENABLE)) {
263
ksft_print_msg("GCS not enabled in child\n");
264
ret = -EINVAL;
265
}
266
267
exit(ret);
268
}
269
270
/*
271
* In parent, check we can still do function calls then block
272
* for the child.
273
*/
274
valid_gcs_function();
275
276
ksft_print_msg("Waiting for child %d\n", pid);
277
278
ret = waitpid(pid, &status, 0);
279
if (ret == -1) {
280
ksft_print_msg("Failed to wait for child: %d\n",
281
errno);
282
return false;
283
}
284
285
if (!WIFEXITED(status)) {
286
ksft_print_msg("Child exited due to signal %d\n",
287
WTERMSIG(status));
288
pass = false;
289
} else {
290
if (WEXITSTATUS(status)) {
291
ksft_print_msg("Child exited with status %d\n",
292
WEXITSTATUS(status));
293
pass = false;
294
}
295
}
296
297
out:
298
299
return pass;
300
}
301
302
/* A vfork()ed process can run and exit */
303
static bool test_vfork(void)
304
{
305
unsigned long child_mode;
306
int ret, status;
307
pid_t pid;
308
bool pass = true;
309
310
pid = vfork();
311
if (pid == -1) {
312
ksft_print_msg("vfork() failed: %d\n", errno);
313
pass = false;
314
goto out;
315
}
316
if (pid == 0) {
317
/*
318
* In child, make sure we can call a function, read
319
* the GCS pointer and status and then exit.
320
*/
321
valid_gcs_function();
322
get_gcspr();
323
324
ret = my_syscall5(__NR_prctl, PR_GET_SHADOW_STACK_STATUS,
325
&child_mode, 0, 0, 0);
326
if (ret == 0 && !(child_mode & PR_SHADOW_STACK_ENABLE)) {
327
ksft_print_msg("GCS not enabled in child\n");
328
ret = EXIT_FAILURE;
329
}
330
331
_exit(ret);
332
}
333
334
/*
335
* In parent, check we can still do function calls then check
336
* on the child.
337
*/
338
valid_gcs_function();
339
340
ksft_print_msg("Waiting for child %d\n", pid);
341
342
ret = waitpid(pid, &status, 0);
343
if (ret == -1) {
344
ksft_print_msg("Failed to wait for child: %d\n",
345
errno);
346
return false;
347
}
348
349
if (!WIFEXITED(status)) {
350
ksft_print_msg("Child exited due to signal %d\n",
351
WTERMSIG(status));
352
pass = false;
353
} else if (WEXITSTATUS(status)) {
354
ksft_print_msg("Child exited with status %d\n",
355
WEXITSTATUS(status));
356
pass = false;
357
}
358
359
out:
360
361
return pass;
362
}
363
364
typedef bool (*gcs_test)(void);
365
366
static struct {
367
char *name;
368
gcs_test test;
369
bool needs_enable;
370
} tests[] = {
371
{ "read_status", read_status },
372
{ "base_enable", base_enable, true },
373
{ "read_gcspr_el0", read_gcspr_el0 },
374
{ "enable_writeable", enable_writeable, true },
375
{ "enable_push_pop", enable_push_pop, true },
376
{ "enable_all", enable_all, true },
377
{ "enable_invalid", enable_invalid, true },
378
{ "map_guarded_stack", map_guarded_stack },
379
{ "fork", test_fork },
380
{ "vfork", test_vfork },
381
};
382
383
int main(void)
384
{
385
int i, ret;
386
unsigned long gcs_mode;
387
388
ksft_print_header();
389
390
if (!(getauxval(AT_HWCAP) & HWCAP_GCS))
391
ksft_exit_skip("SKIP GCS not supported\n");
392
393
ret = my_syscall5(__NR_prctl, PR_GET_SHADOW_STACK_STATUS,
394
&gcs_mode, 0, 0, 0);
395
if (ret != 0)
396
ksft_exit_fail_msg("Failed to read GCS state: %d\n", ret);
397
398
if (!(gcs_mode & PR_SHADOW_STACK_ENABLE)) {
399
gcs_mode = PR_SHADOW_STACK_ENABLE;
400
ret = my_syscall5(__NR_prctl, PR_SET_SHADOW_STACK_STATUS,
401
gcs_mode, 0, 0, 0);
402
if (ret != 0)
403
ksft_exit_fail_msg("Failed to enable GCS: %d\n", ret);
404
}
405
406
ksft_set_plan(ARRAY_SIZE(tests));
407
408
for (i = 0; i < ARRAY_SIZE(tests); i++) {
409
ksft_test_result((*tests[i].test)(), "%s\n", tests[i].name);
410
}
411
412
/* One last test: disable GCS, we can do this one time */
413
ret = my_syscall5(__NR_prctl, PR_SET_SHADOW_STACK_STATUS, 0, 0, 0, 0);
414
if (ret != 0)
415
ksft_print_msg("Failed to disable GCS: %d\n", ret);
416
417
ksft_finished();
418
419
return 0;
420
}
421
422