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