Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/tools/testing/selftests/arm64/gcs/libc-gcs.c
26292 views
1
// SPDX-License-Identifier: GPL-2.0-only
2
/*
3
* Copyright (C) 2023 ARM Limited.
4
*/
5
6
#define _GNU_SOURCE
7
8
#include <pthread.h>
9
#include <stdbool.h>
10
11
#include <sys/auxv.h>
12
#include <sys/mman.h>
13
#include <sys/prctl.h>
14
#include <sys/ptrace.h>
15
#include <sys/uio.h>
16
17
#include <asm/hwcap.h>
18
#include <asm/mman.h>
19
20
#include <linux/compiler.h>
21
22
#include "kselftest_harness.h"
23
24
#include "gcs-util.h"
25
26
#define my_syscall2(num, arg1, arg2) \
27
({ \
28
register long _num __asm__ ("x8") = (num); \
29
register long _arg1 __asm__ ("x0") = (long)(arg1); \
30
register long _arg2 __asm__ ("x1") = (long)(arg2); \
31
register long _arg3 __asm__ ("x2") = 0; \
32
register long _arg4 __asm__ ("x3") = 0; \
33
register long _arg5 __asm__ ("x4") = 0; \
34
\
35
__asm__ volatile ( \
36
"svc #0\n" \
37
: "=r"(_arg1) \
38
: "r"(_arg1), "r"(_arg2), \
39
"r"(_arg3), "r"(_arg4), \
40
"r"(_arg5), "r"(_num) \
41
: "memory", "cc" \
42
); \
43
_arg1; \
44
})
45
46
static noinline void gcs_recurse(int depth)
47
{
48
if (depth)
49
gcs_recurse(depth - 1);
50
51
/* Prevent tail call optimization so we actually recurse */
52
asm volatile("dsb sy" : : : "memory");
53
}
54
55
/* Smoke test that a function call and return works*/
56
TEST(can_call_function)
57
{
58
gcs_recurse(0);
59
}
60
61
static void *gcs_test_thread(void *arg)
62
{
63
int ret;
64
unsigned long mode;
65
66
/*
67
* Some libcs don't seem to fill unused arguments with 0 but
68
* the kernel validates this so we supply all 5 arguments.
69
*/
70
ret = prctl(PR_GET_SHADOW_STACK_STATUS, &mode, 0, 0, 0);
71
if (ret != 0) {
72
ksft_print_msg("PR_GET_SHADOW_STACK_STATUS failed: %d\n", ret);
73
return NULL;
74
}
75
76
if (!(mode & PR_SHADOW_STACK_ENABLE)) {
77
ksft_print_msg("GCS not enabled in thread, mode is %lu\n",
78
mode);
79
return NULL;
80
}
81
82
/* Just in case... */
83
gcs_recurse(0);
84
85
/* Use a non-NULL value to indicate a pass */
86
return &gcs_test_thread;
87
}
88
89
/* Verify that if we start a new thread it has GCS enabled */
90
TEST(gcs_enabled_thread)
91
{
92
pthread_t thread;
93
void *thread_ret;
94
int ret;
95
96
ret = pthread_create(&thread, NULL, gcs_test_thread, NULL);
97
ASSERT_TRUE(ret == 0);
98
if (ret != 0)
99
return;
100
101
ret = pthread_join(thread, &thread_ret);
102
ASSERT_TRUE(ret == 0);
103
if (ret != 0)
104
return;
105
106
ASSERT_TRUE(thread_ret != NULL);
107
}
108
109
/* Read the GCS until we find the terminator */
110
TEST(gcs_find_terminator)
111
{
112
unsigned long *gcs, *cur;
113
114
gcs = get_gcspr();
115
cur = gcs;
116
while (*cur)
117
cur++;
118
119
ksft_print_msg("GCS in use from %p-%p\n", gcs, cur);
120
121
/*
122
* We should have at least whatever called into this test so
123
* the two pointer should differ.
124
*/
125
ASSERT_TRUE(gcs != cur);
126
}
127
128
/*
129
* We can access a GCS via ptrace
130
*
131
* This could usefully have a fixture but note that each test is
132
* fork()ed into a new child whcih causes issues. Might be better to
133
* lift at least some of this out into a separate, non-harness, test
134
* program.
135
*/
136
TEST(ptrace_read_write)
137
{
138
pid_t child, pid;
139
int ret, status;
140
siginfo_t si;
141
uint64_t val, rval, gcspr;
142
struct user_gcs child_gcs;
143
struct iovec iov, local_iov, remote_iov;
144
145
child = fork();
146
if (child == -1) {
147
ksft_print_msg("fork() failed: %d (%s)\n",
148
errno, strerror(errno));
149
ASSERT_NE(child, -1);
150
}
151
152
if (child == 0) {
153
/*
154
* In child, make sure there's something on the stack and
155
* ask to be traced.
156
*/
157
gcs_recurse(0);
158
if (ptrace(PTRACE_TRACEME, -1, NULL, NULL))
159
ksft_exit_fail_msg("PTRACE_TRACEME %s",
160
strerror(errno));
161
162
if (raise(SIGSTOP))
163
ksft_exit_fail_msg("raise(SIGSTOP) %s",
164
strerror(errno));
165
166
return;
167
}
168
169
ksft_print_msg("Child: %d\n", child);
170
171
/* Attach to the child */
172
while (1) {
173
int sig;
174
175
pid = wait(&status);
176
if (pid == -1) {
177
ksft_print_msg("wait() failed: %s",
178
strerror(errno));
179
goto error;
180
}
181
182
/*
183
* This should never happen but it's hard to flag in
184
* the framework.
185
*/
186
if (pid != child)
187
continue;
188
189
if (WIFEXITED(status) || WIFSIGNALED(status))
190
ksft_exit_fail_msg("Child died unexpectedly\n");
191
192
if (!WIFSTOPPED(status))
193
goto error;
194
195
sig = WSTOPSIG(status);
196
197
if (ptrace(PTRACE_GETSIGINFO, pid, NULL, &si)) {
198
if (errno == ESRCH) {
199
ASSERT_NE(errno, ESRCH);
200
return;
201
}
202
203
if (errno == EINVAL) {
204
sig = 0; /* bust group-stop */
205
goto cont;
206
}
207
208
ksft_print_msg("PTRACE_GETSIGINFO: %s\n",
209
strerror(errno));
210
goto error;
211
}
212
213
if (sig == SIGSTOP && si.si_code == SI_TKILL &&
214
si.si_pid == pid)
215
break;
216
217
cont:
218
if (ptrace(PTRACE_CONT, pid, NULL, sig)) {
219
if (errno == ESRCH) {
220
ASSERT_NE(errno, ESRCH);
221
return;
222
}
223
224
ksft_print_msg("PTRACE_CONT: %s\n", strerror(errno));
225
goto error;
226
}
227
}
228
229
/* Where is the child GCS? */
230
iov.iov_base = &child_gcs;
231
iov.iov_len = sizeof(child_gcs);
232
ret = ptrace(PTRACE_GETREGSET, child, NT_ARM_GCS, &iov);
233
if (ret != 0) {
234
ksft_print_msg("Failed to read child GCS state: %s (%d)\n",
235
strerror(errno), errno);
236
goto error;
237
}
238
239
/* We should have inherited GCS over fork(), confirm */
240
if (!(child_gcs.features_enabled & PR_SHADOW_STACK_ENABLE)) {
241
ASSERT_TRUE(child_gcs.features_enabled &
242
PR_SHADOW_STACK_ENABLE);
243
goto error;
244
}
245
246
gcspr = child_gcs.gcspr_el0;
247
ksft_print_msg("Child GCSPR 0x%lx, flags %llx, locked %llx\n",
248
gcspr, child_gcs.features_enabled,
249
child_gcs.features_locked);
250
251
/* Ideally we'd cross check with the child memory map */
252
253
errno = 0;
254
val = ptrace(PTRACE_PEEKDATA, child, (void *)gcspr, NULL);
255
ret = errno;
256
if (ret != 0)
257
ksft_print_msg("PTRACE_PEEKDATA failed: %s (%d)\n",
258
strerror(ret), ret);
259
EXPECT_EQ(ret, 0);
260
261
/* The child should be in a function, the GCSPR shouldn't be 0 */
262
EXPECT_NE(val, 0);
263
264
/* Same thing via process_vm_readv() */
265
local_iov.iov_base = &rval;
266
local_iov.iov_len = sizeof(rval);
267
remote_iov.iov_base = (void *)gcspr;
268
remote_iov.iov_len = sizeof(rval);
269
ret = process_vm_readv(child, &local_iov, 1, &remote_iov, 1, 0);
270
if (ret == -1)
271
ksft_print_msg("process_vm_readv() failed: %s (%d)\n",
272
strerror(errno), errno);
273
EXPECT_EQ(ret, sizeof(rval));
274
EXPECT_EQ(val, rval);
275
276
/* Write data via a peek */
277
ret = ptrace(PTRACE_POKEDATA, child, (void *)gcspr, NULL);
278
if (ret == -1)
279
ksft_print_msg("PTRACE_POKEDATA failed: %s (%d)\n",
280
strerror(errno), errno);
281
EXPECT_EQ(ret, 0);
282
EXPECT_EQ(0, ptrace(PTRACE_PEEKDATA, child, (void *)gcspr, NULL));
283
284
/* Restore what we had before */
285
ret = ptrace(PTRACE_POKEDATA, child, (void *)gcspr, val);
286
if (ret == -1)
287
ksft_print_msg("PTRACE_POKEDATA failed: %s (%d)\n",
288
strerror(errno), errno);
289
EXPECT_EQ(ret, 0);
290
EXPECT_EQ(val, ptrace(PTRACE_PEEKDATA, child, (void *)gcspr, NULL));
291
292
/* That's all, folks */
293
kill(child, SIGKILL);
294
return;
295
296
error:
297
kill(child, SIGKILL);
298
ASSERT_FALSE(true);
299
}
300
301
FIXTURE(map_gcs)
302
{
303
unsigned long *stack;
304
};
305
306
FIXTURE_VARIANT(map_gcs)
307
{
308
size_t stack_size;
309
unsigned long flags;
310
};
311
312
FIXTURE_VARIANT_ADD(map_gcs, s2k_cap_marker)
313
{
314
.stack_size = 2 * 1024,
315
.flags = SHADOW_STACK_SET_MARKER | SHADOW_STACK_SET_TOKEN,
316
};
317
318
FIXTURE_VARIANT_ADD(map_gcs, s2k_cap)
319
{
320
.stack_size = 2 * 1024,
321
.flags = SHADOW_STACK_SET_TOKEN,
322
};
323
324
FIXTURE_VARIANT_ADD(map_gcs, s2k_marker)
325
{
326
.stack_size = 2 * 1024,
327
.flags = SHADOW_STACK_SET_MARKER,
328
};
329
330
FIXTURE_VARIANT_ADD(map_gcs, s2k)
331
{
332
.stack_size = 2 * 1024,
333
.flags = 0,
334
};
335
336
FIXTURE_VARIANT_ADD(map_gcs, s4k_cap_marker)
337
{
338
.stack_size = 4 * 1024,
339
.flags = SHADOW_STACK_SET_MARKER | SHADOW_STACK_SET_TOKEN,
340
};
341
342
FIXTURE_VARIANT_ADD(map_gcs, s4k_cap)
343
{
344
.stack_size = 4 * 1024,
345
.flags = SHADOW_STACK_SET_TOKEN,
346
};
347
348
FIXTURE_VARIANT_ADD(map_gcs, s3k_marker)
349
{
350
.stack_size = 4 * 1024,
351
.flags = SHADOW_STACK_SET_MARKER,
352
};
353
354
FIXTURE_VARIANT_ADD(map_gcs, s4k)
355
{
356
.stack_size = 4 * 1024,
357
.flags = 0,
358
};
359
360
FIXTURE_VARIANT_ADD(map_gcs, s16k_cap_marker)
361
{
362
.stack_size = 16 * 1024,
363
.flags = SHADOW_STACK_SET_MARKER | SHADOW_STACK_SET_TOKEN,
364
};
365
366
FIXTURE_VARIANT_ADD(map_gcs, s16k_cap)
367
{
368
.stack_size = 16 * 1024,
369
.flags = SHADOW_STACK_SET_TOKEN,
370
};
371
372
FIXTURE_VARIANT_ADD(map_gcs, s16k_marker)
373
{
374
.stack_size = 16 * 1024,
375
.flags = SHADOW_STACK_SET_MARKER,
376
};
377
378
FIXTURE_VARIANT_ADD(map_gcs, s16k)
379
{
380
.stack_size = 16 * 1024,
381
.flags = 0,
382
};
383
384
FIXTURE_VARIANT_ADD(map_gcs, s64k_cap_marker)
385
{
386
.stack_size = 64 * 1024,
387
.flags = SHADOW_STACK_SET_MARKER | SHADOW_STACK_SET_TOKEN,
388
};
389
390
FIXTURE_VARIANT_ADD(map_gcs, s64k_cap)
391
{
392
.stack_size = 64 * 1024,
393
.flags = SHADOW_STACK_SET_TOKEN,
394
};
395
396
FIXTURE_VARIANT_ADD(map_gcs, s64k_marker)
397
{
398
.stack_size = 64 * 1024,
399
.flags = SHADOW_STACK_SET_MARKER,
400
};
401
402
FIXTURE_VARIANT_ADD(map_gcs, s64k)
403
{
404
.stack_size = 64 * 1024,
405
.flags = 0,
406
};
407
408
FIXTURE_VARIANT_ADD(map_gcs, s128k_cap_marker)
409
{
410
.stack_size = 128 * 1024,
411
.flags = SHADOW_STACK_SET_MARKER | SHADOW_STACK_SET_TOKEN,
412
};
413
414
FIXTURE_VARIANT_ADD(map_gcs, s128k_cap)
415
{
416
.stack_size = 128 * 1024,
417
.flags = SHADOW_STACK_SET_TOKEN,
418
};
419
420
FIXTURE_VARIANT_ADD(map_gcs, s128k_marker)
421
{
422
.stack_size = 128 * 1024,
423
.flags = SHADOW_STACK_SET_MARKER,
424
};
425
426
FIXTURE_VARIANT_ADD(map_gcs, s128k)
427
{
428
.stack_size = 128 * 1024,
429
.flags = 0,
430
};
431
432
FIXTURE_SETUP(map_gcs)
433
{
434
self->stack = (void *)syscall(__NR_map_shadow_stack, 0,
435
variant->stack_size,
436
variant->flags);
437
ASSERT_FALSE(self->stack == MAP_FAILED);
438
ksft_print_msg("Allocated stack from %p-%p\n", self->stack,
439
self->stack + variant->stack_size);
440
}
441
442
FIXTURE_TEARDOWN(map_gcs)
443
{
444
int ret;
445
446
if (self->stack != MAP_FAILED) {
447
ret = munmap(self->stack, variant->stack_size);
448
ASSERT_EQ(ret, 0);
449
}
450
}
451
452
/* The stack has a cap token */
453
TEST_F(map_gcs, stack_capped)
454
{
455
unsigned long *stack = self->stack;
456
size_t cap_index;
457
458
cap_index = (variant->stack_size / sizeof(unsigned long));
459
460
switch (variant->flags & (SHADOW_STACK_SET_MARKER | SHADOW_STACK_SET_TOKEN)) {
461
case SHADOW_STACK_SET_MARKER | SHADOW_STACK_SET_TOKEN:
462
cap_index -= 2;
463
break;
464
case SHADOW_STACK_SET_TOKEN:
465
cap_index -= 1;
466
break;
467
case SHADOW_STACK_SET_MARKER:
468
case 0:
469
/* No cap, no test */
470
return;
471
}
472
473
ASSERT_EQ(stack[cap_index], GCS_CAP(&stack[cap_index]));
474
}
475
476
/* The top of the stack is 0 */
477
TEST_F(map_gcs, stack_terminated)
478
{
479
unsigned long *stack = self->stack;
480
size_t term_index;
481
482
if (!(variant->flags & SHADOW_STACK_SET_MARKER))
483
return;
484
485
term_index = (variant->stack_size / sizeof(unsigned long)) - 1;
486
487
ASSERT_EQ(stack[term_index], 0);
488
}
489
490
/* Writes should fault */
491
TEST_F_SIGNAL(map_gcs, not_writeable, SIGSEGV)
492
{
493
self->stack[0] = 0;
494
}
495
496
/* Put it all together, we can safely switch to and from the stack */
497
TEST_F(map_gcs, stack_switch)
498
{
499
size_t cap_index;
500
cap_index = (variant->stack_size / sizeof(unsigned long));
501
unsigned long *orig_gcspr_el0, *pivot_gcspr_el0;
502
503
/* Skip over the stack terminator and point at the cap */
504
switch (variant->flags & (SHADOW_STACK_SET_MARKER | SHADOW_STACK_SET_TOKEN)) {
505
case SHADOW_STACK_SET_MARKER | SHADOW_STACK_SET_TOKEN:
506
cap_index -= 2;
507
break;
508
case SHADOW_STACK_SET_TOKEN:
509
cap_index -= 1;
510
break;
511
case SHADOW_STACK_SET_MARKER:
512
case 0:
513
/* No cap, no test */
514
return;
515
}
516
pivot_gcspr_el0 = &self->stack[cap_index];
517
518
/* Pivot to the new GCS */
519
ksft_print_msg("Pivoting to %p from %p, target has value 0x%lx\n",
520
pivot_gcspr_el0, get_gcspr(),
521
*pivot_gcspr_el0);
522
gcsss1(pivot_gcspr_el0);
523
orig_gcspr_el0 = gcsss2();
524
ksft_print_msg("Pivoted to %p from %p, target has value 0x%lx\n",
525
get_gcspr(), orig_gcspr_el0,
526
*pivot_gcspr_el0);
527
528
ksft_print_msg("Pivoted, GCSPR_EL0 now %p\n", get_gcspr());
529
530
/* New GCS must be in the new buffer */
531
ASSERT_TRUE((unsigned long)get_gcspr() > (unsigned long)self->stack);
532
ASSERT_TRUE((unsigned long)get_gcspr() <=
533
(unsigned long)self->stack + variant->stack_size);
534
535
/* We should be able to use all but 2 slots of the new stack */
536
ksft_print_msg("Recursing %zu levels\n", cap_index - 1);
537
gcs_recurse(cap_index - 1);
538
539
/* Pivot back to the original GCS */
540
gcsss1(orig_gcspr_el0);
541
pivot_gcspr_el0 = gcsss2();
542
543
gcs_recurse(0);
544
ksft_print_msg("Pivoted back to GCSPR_EL0 0x%p\n", get_gcspr());
545
}
546
547
/* We fault if we try to go beyond the end of the stack */
548
TEST_F_SIGNAL(map_gcs, stack_overflow, SIGSEGV)
549
{
550
size_t cap_index;
551
cap_index = (variant->stack_size / sizeof(unsigned long));
552
unsigned long *orig_gcspr_el0, *pivot_gcspr_el0;
553
554
/* Skip over the stack terminator and point at the cap */
555
switch (variant->flags & (SHADOW_STACK_SET_MARKER | SHADOW_STACK_SET_TOKEN)) {
556
case SHADOW_STACK_SET_MARKER | SHADOW_STACK_SET_TOKEN:
557
cap_index -= 2;
558
break;
559
case SHADOW_STACK_SET_TOKEN:
560
cap_index -= 1;
561
break;
562
case SHADOW_STACK_SET_MARKER:
563
case 0:
564
/* No cap, no test but we need to SEGV to avoid a false fail */
565
orig_gcspr_el0 = get_gcspr();
566
*orig_gcspr_el0 = 0;
567
return;
568
}
569
pivot_gcspr_el0 = &self->stack[cap_index];
570
571
/* Pivot to the new GCS */
572
ksft_print_msg("Pivoting to %p from %p, target has value 0x%lx\n",
573
pivot_gcspr_el0, get_gcspr(),
574
*pivot_gcspr_el0);
575
gcsss1(pivot_gcspr_el0);
576
orig_gcspr_el0 = gcsss2();
577
ksft_print_msg("Pivoted to %p from %p, target has value 0x%lx\n",
578
pivot_gcspr_el0, orig_gcspr_el0,
579
*pivot_gcspr_el0);
580
581
ksft_print_msg("Pivoted, GCSPR_EL0 now %p\n", get_gcspr());
582
583
/* New GCS must be in the new buffer */
584
ASSERT_TRUE((unsigned long)get_gcspr() > (unsigned long)self->stack);
585
ASSERT_TRUE((unsigned long)get_gcspr() <=
586
(unsigned long)self->stack + variant->stack_size);
587
588
/* Now try to recurse, we should fault doing this. */
589
ksft_print_msg("Recursing %zu levels...\n", cap_index + 1);
590
gcs_recurse(cap_index + 1);
591
ksft_print_msg("...done\n");
592
593
/* Clean up properly to try to guard against spurious passes. */
594
gcsss1(orig_gcspr_el0);
595
pivot_gcspr_el0 = gcsss2();
596
ksft_print_msg("Pivoted back to GCSPR_EL0 0x%p\n", get_gcspr());
597
}
598
599
FIXTURE(map_invalid_gcs)
600
{
601
};
602
603
FIXTURE_VARIANT(map_invalid_gcs)
604
{
605
size_t stack_size;
606
};
607
608
FIXTURE_SETUP(map_invalid_gcs)
609
{
610
}
611
612
FIXTURE_TEARDOWN(map_invalid_gcs)
613
{
614
}
615
616
/* GCS must be larger than 16 bytes */
617
FIXTURE_VARIANT_ADD(map_invalid_gcs, too_small)
618
{
619
.stack_size = 8,
620
};
621
622
/* GCS size must be 16 byte aligned */
623
FIXTURE_VARIANT_ADD(map_invalid_gcs, unligned_1) { .stack_size = 1024 + 1 };
624
FIXTURE_VARIANT_ADD(map_invalid_gcs, unligned_2) { .stack_size = 1024 + 2 };
625
FIXTURE_VARIANT_ADD(map_invalid_gcs, unligned_3) { .stack_size = 1024 + 3 };
626
FIXTURE_VARIANT_ADD(map_invalid_gcs, unligned_4) { .stack_size = 1024 + 4 };
627
FIXTURE_VARIANT_ADD(map_invalid_gcs, unligned_5) { .stack_size = 1024 + 5 };
628
FIXTURE_VARIANT_ADD(map_invalid_gcs, unligned_6) { .stack_size = 1024 + 6 };
629
FIXTURE_VARIANT_ADD(map_invalid_gcs, unligned_7) { .stack_size = 1024 + 7 };
630
631
TEST_F(map_invalid_gcs, do_map)
632
{
633
void *stack;
634
635
stack = (void *)syscall(__NR_map_shadow_stack, 0,
636
variant->stack_size, 0);
637
ASSERT_TRUE(stack == MAP_FAILED);
638
if (stack != MAP_FAILED)
639
munmap(stack, variant->stack_size);
640
}
641
642
FIXTURE(invalid_mprotect)
643
{
644
unsigned long *stack;
645
size_t stack_size;
646
};
647
648
FIXTURE_VARIANT(invalid_mprotect)
649
{
650
unsigned long flags;
651
};
652
653
FIXTURE_SETUP(invalid_mprotect)
654
{
655
self->stack_size = sysconf(_SC_PAGE_SIZE);
656
self->stack = (void *)syscall(__NR_map_shadow_stack, 0,
657
self->stack_size, 0);
658
ASSERT_FALSE(self->stack == MAP_FAILED);
659
ksft_print_msg("Allocated stack from %p-%p\n", self->stack,
660
self->stack + self->stack_size);
661
}
662
663
FIXTURE_TEARDOWN(invalid_mprotect)
664
{
665
int ret;
666
667
if (self->stack != MAP_FAILED) {
668
ret = munmap(self->stack, self->stack_size);
669
ASSERT_EQ(ret, 0);
670
}
671
}
672
673
FIXTURE_VARIANT_ADD(invalid_mprotect, exec)
674
{
675
.flags = PROT_EXEC,
676
};
677
678
TEST_F(invalid_mprotect, do_map)
679
{
680
int ret;
681
682
ret = mprotect(self->stack, self->stack_size, variant->flags);
683
ASSERT_EQ(ret, -1);
684
}
685
686
TEST_F(invalid_mprotect, do_map_read)
687
{
688
int ret;
689
690
ret = mprotect(self->stack, self->stack_size,
691
variant->flags | PROT_READ);
692
ASSERT_EQ(ret, -1);
693
}
694
695
int main(int argc, char **argv)
696
{
697
unsigned long gcs_mode;
698
int ret;
699
700
if (!(getauxval(AT_HWCAP) & HWCAP_GCS))
701
ksft_exit_skip("SKIP GCS not supported\n");
702
703
/*
704
* Force shadow stacks on, our tests *should* be fine with or
705
* without libc support and with or without this having ended
706
* up tagged for GCS and enabled by the dynamic linker. We
707
* can't use the libc prctl() function since we can't return
708
* from enabling the stack.
709
*/
710
ret = my_syscall2(__NR_prctl, PR_GET_SHADOW_STACK_STATUS, &gcs_mode);
711
if (ret) {
712
ksft_print_msg("Failed to read GCS state: %d\n", ret);
713
return EXIT_FAILURE;
714
}
715
716
if (!(gcs_mode & PR_SHADOW_STACK_ENABLE)) {
717
gcs_mode = PR_SHADOW_STACK_ENABLE;
718
ret = my_syscall2(__NR_prctl, PR_SET_SHADOW_STACK_STATUS,
719
gcs_mode);
720
if (ret) {
721
ksft_print_msg("Failed to configure GCS: %d\n", ret);
722
return EXIT_FAILURE;
723
}
724
}
725
726
/* Avoid returning in case libc doesn't understand GCS */
727
exit(test_harness_run(argc, argv));
728
}
729
730