File Index
Files and directories
Sign in
FILE

libc-gcs.c

File preview with syntax highlighting.

linux-6.19.14/tools/testing/selftests/arm64/gcs/libc-gcs.c C
  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
 46static 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*/
 56TEST(can_call_function)
 57{
 58	gcs_recurse(0);
 59}
 60
 61static 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 */
 90TEST(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 */
110TEST(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 */
136TEST(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
296error:
297	kill(child, SIGKILL);
298	ASSERT_FALSE(true);
299}
300
301FIXTURE(map_gcs)
302{
303	unsigned long *stack;
304};
305
306FIXTURE_VARIANT(map_gcs)
307{
308	size_t stack_size;
309	unsigned long flags;
310};
311
312FIXTURE_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
318FIXTURE_VARIANT_ADD(map_gcs, s2k_cap)
319{
320	.stack_size = 2 * 1024,
321	.flags = SHADOW_STACK_SET_TOKEN,
322};
323
324FIXTURE_VARIANT_ADD(map_gcs, s2k_marker)
325{
326	.stack_size = 2 * 1024,
327	.flags = SHADOW_STACK_SET_MARKER,
328};
329
330FIXTURE_VARIANT_ADD(map_gcs, s2k)
331{
332	.stack_size = 2 * 1024,
333	.flags = 0,
334};
335
336FIXTURE_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
342FIXTURE_VARIANT_ADD(map_gcs, s4k_cap)
343{
344	.stack_size = 4 * 1024,
345	.flags = SHADOW_STACK_SET_TOKEN,
346};
347
348FIXTURE_VARIANT_ADD(map_gcs, s3k_marker)
349{
350	.stack_size = 4 * 1024,
351	.flags = SHADOW_STACK_SET_MARKER,
352};
353
354FIXTURE_VARIANT_ADD(map_gcs, s4k)
355{
356	.stack_size = 4 * 1024,
357	.flags = 0,
358};
359
360FIXTURE_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
366FIXTURE_VARIANT_ADD(map_gcs, s16k_cap)
367{
368	.stack_size = 16 * 1024,
369	.flags = SHADOW_STACK_SET_TOKEN,
370};
371
372FIXTURE_VARIANT_ADD(map_gcs, s16k_marker)
373{
374	.stack_size = 16 * 1024,
375	.flags = SHADOW_STACK_SET_MARKER,
376};
377
378FIXTURE_VARIANT_ADD(map_gcs, s16k)
379{
380	.stack_size = 16 * 1024,
381	.flags = 0,
382};
383
384FIXTURE_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
390FIXTURE_VARIANT_ADD(map_gcs, s64k_cap)
391{
392	.stack_size = 64 * 1024,
393	.flags = SHADOW_STACK_SET_TOKEN,
394};
395
396FIXTURE_VARIANT_ADD(map_gcs, s64k_marker)
397{
398	.stack_size = 64 * 1024,
399	.flags = SHADOW_STACK_SET_MARKER,
400};
401
402FIXTURE_VARIANT_ADD(map_gcs, s64k)
403{
404	.stack_size = 64 * 1024,
405	.flags = 0,
406};
407
408FIXTURE_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
414FIXTURE_VARIANT_ADD(map_gcs, s128k_cap)
415{
416	.stack_size = 128 * 1024,
417	.flags = SHADOW_STACK_SET_TOKEN,
418};
419
420FIXTURE_VARIANT_ADD(map_gcs, s128k_marker)
421{
422	.stack_size = 128 * 1024,
423	.flags = SHADOW_STACK_SET_MARKER,
424};
425
426FIXTURE_VARIANT_ADD(map_gcs, s128k)
427{
428	.stack_size = 128 * 1024,
429	.flags = 0,
430};
431
432FIXTURE_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
442FIXTURE_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 */
453TEST_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 */
477TEST_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 */
491TEST_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 */
497TEST_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 */
548TEST_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
599FIXTURE(map_invalid_gcs)
600{
601};
602
603FIXTURE_VARIANT(map_invalid_gcs)
604{
605	size_t stack_size;
606};
607
608FIXTURE_SETUP(map_invalid_gcs)
609{
610}
611
612FIXTURE_TEARDOWN(map_invalid_gcs)
613{
614}
615
616/* GCS must be larger than 16 bytes */
617FIXTURE_VARIANT_ADD(map_invalid_gcs, too_small)
618{
619	.stack_size = 8,
620};
621
622/* GCS size must be 16 byte aligned */
623FIXTURE_VARIANT_ADD(map_invalid_gcs, unligned_1)  { .stack_size = 1024 + 1  };
624FIXTURE_VARIANT_ADD(map_invalid_gcs, unligned_2)  { .stack_size = 1024 + 2  };
625FIXTURE_VARIANT_ADD(map_invalid_gcs, unligned_3)  { .stack_size = 1024 + 3  };
626FIXTURE_VARIANT_ADD(map_invalid_gcs, unligned_4)  { .stack_size = 1024 + 4  };
627FIXTURE_VARIANT_ADD(map_invalid_gcs, unligned_5)  { .stack_size = 1024 + 5  };
628FIXTURE_VARIANT_ADD(map_invalid_gcs, unligned_6)  { .stack_size = 1024 + 6  };
629FIXTURE_VARIANT_ADD(map_invalid_gcs, unligned_7)  { .stack_size = 1024 + 7  };
630
631TEST_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
642FIXTURE(invalid_mprotect)
643{
644	unsigned long *stack;
645	size_t stack_size;
646};
647
648FIXTURE_VARIANT(invalid_mprotect)
649{
650	unsigned long flags;
651};
652
653FIXTURE_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
663FIXTURE_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
673FIXTURE_VARIANT_ADD(invalid_mprotect, exec)
674{
675	.flags = PROT_EXEC,
676};
677
678TEST_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
686TEST_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
695int 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}
~/ Read-only file preview.