xref: /linux/tools/testing/selftests/arm64/gcs/basic-gcs.c (revision b1c21075d30c40762750be0cded9822791df422b)
13d37d430SMark Brown // SPDX-License-Identifier: GPL-2.0-only
23d37d430SMark Brown /*
33d37d430SMark Brown  * Copyright (C) 2023 ARM Limited.
43d37d430SMark Brown  */
53d37d430SMark Brown 
63d37d430SMark Brown #include <limits.h>
73d37d430SMark Brown #include <stdbool.h>
83d37d430SMark Brown 
93d37d430SMark Brown #include <linux/prctl.h>
103d37d430SMark Brown 
113d37d430SMark Brown #include <sys/mman.h>
123d37d430SMark Brown #include <asm/mman.h>
133d37d430SMark Brown #include <linux/sched.h>
143d37d430SMark Brown 
153d37d430SMark Brown #include "kselftest.h"
163d37d430SMark Brown #include "gcs-util.h"
173d37d430SMark Brown 
183d37d430SMark Brown /* nolibc doesn't have sysconf(), just hard code the maximum */
193d37d430SMark Brown static size_t page_size = 65536;
203d37d430SMark Brown 
valid_gcs_function(void)213d37d430SMark Brown static  __attribute__((noinline)) void valid_gcs_function(void)
223d37d430SMark Brown {
233d37d430SMark Brown 	/* Do something the compiler can't optimise out */
243d37d430SMark Brown 	my_syscall1(__NR_prctl, PR_SVE_GET_VL);
253d37d430SMark Brown }
263d37d430SMark Brown 
gcs_set_status(unsigned long mode)273d37d430SMark Brown static inline int gcs_set_status(unsigned long mode)
283d37d430SMark Brown {
293d37d430SMark Brown 	bool enabling = mode & PR_SHADOW_STACK_ENABLE;
303d37d430SMark Brown 	int ret;
313d37d430SMark Brown 	unsigned long new_mode;
323d37d430SMark Brown 
333d37d430SMark Brown 	/*
343d37d430SMark Brown 	 * The prctl takes 1 argument but we need to ensure that the
353d37d430SMark Brown 	 * other 3 values passed in registers to the syscall are zero
363d37d430SMark Brown 	 * since the kernel validates them.
373d37d430SMark Brown 	 */
383d37d430SMark Brown 	ret = my_syscall5(__NR_prctl, PR_SET_SHADOW_STACK_STATUS, mode,
393d37d430SMark Brown 			  0, 0, 0);
403d37d430SMark Brown 
413d37d430SMark Brown 	if (ret == 0) {
423d37d430SMark Brown 		ret = my_syscall5(__NR_prctl, PR_GET_SHADOW_STACK_STATUS,
433d37d430SMark Brown 				  &new_mode, 0, 0, 0);
443d37d430SMark Brown 		if (ret == 0) {
453d37d430SMark Brown 			if (new_mode != mode) {
463d37d430SMark Brown 				ksft_print_msg("Mode set to %lx not %lx\n",
473d37d430SMark Brown 					       new_mode, mode);
483d37d430SMark Brown 				ret = -EINVAL;
493d37d430SMark Brown 			}
503d37d430SMark Brown 		} else {
513d37d430SMark Brown 			ksft_print_msg("Failed to validate mode: %d\n", ret);
523d37d430SMark Brown 		}
533d37d430SMark Brown 
543d37d430SMark Brown 		if (enabling != chkfeat_gcs()) {
553d37d430SMark Brown 			ksft_print_msg("%senabled by prctl but %senabled in CHKFEAT\n",
563d37d430SMark Brown 				       enabling ? "" : "not ",
573d37d430SMark Brown 				       chkfeat_gcs() ? "" : "not ");
583d37d430SMark Brown 			ret = -EINVAL;
593d37d430SMark Brown 		}
603d37d430SMark Brown 	}
613d37d430SMark Brown 
623d37d430SMark Brown 	return ret;
633d37d430SMark Brown }
643d37d430SMark Brown 
653d37d430SMark Brown /* Try to read the status */
read_status(void)663d37d430SMark Brown static bool read_status(void)
673d37d430SMark Brown {
683d37d430SMark Brown 	unsigned long state;
693d37d430SMark Brown 	int ret;
703d37d430SMark Brown 
713d37d430SMark Brown 	ret = my_syscall5(__NR_prctl, PR_GET_SHADOW_STACK_STATUS,
723d37d430SMark Brown 			  &state, 0, 0, 0);
733d37d430SMark Brown 	if (ret != 0) {
743d37d430SMark Brown 		ksft_print_msg("Failed to read state: %d\n", ret);
753d37d430SMark Brown 		return false;
763d37d430SMark Brown 	}
773d37d430SMark Brown 
783d37d430SMark Brown 	return state & PR_SHADOW_STACK_ENABLE;
793d37d430SMark Brown }
803d37d430SMark Brown 
813d37d430SMark Brown /* Just a straight enable */
base_enable(void)823d37d430SMark Brown static bool base_enable(void)
833d37d430SMark Brown {
843d37d430SMark Brown 	int ret;
853d37d430SMark Brown 
863d37d430SMark Brown 	ret = gcs_set_status(PR_SHADOW_STACK_ENABLE);
873d37d430SMark Brown 	if (ret) {
883d37d430SMark Brown 		ksft_print_msg("PR_SHADOW_STACK_ENABLE failed %d\n", ret);
893d37d430SMark Brown 		return false;
903d37d430SMark Brown 	}
913d37d430SMark Brown 
923d37d430SMark Brown 	return true;
933d37d430SMark Brown }
943d37d430SMark Brown 
953d37d430SMark Brown /* Check we can read GCSPR_EL0 when GCS is enabled */
read_gcspr_el0(void)963d37d430SMark Brown static bool read_gcspr_el0(void)
973d37d430SMark Brown {
983d37d430SMark Brown 	unsigned long *gcspr_el0;
993d37d430SMark Brown 
1003d37d430SMark Brown 	ksft_print_msg("GET GCSPR\n");
1013d37d430SMark Brown 	gcspr_el0 = get_gcspr();
1023d37d430SMark Brown 	ksft_print_msg("GCSPR_EL0 is %p\n", gcspr_el0);
1033d37d430SMark Brown 
1043d37d430SMark Brown 	return true;
1053d37d430SMark Brown }
1063d37d430SMark Brown 
1073d37d430SMark Brown /* Also allow writes to stack */
enable_writeable(void)1083d37d430SMark Brown static bool enable_writeable(void)
1093d37d430SMark Brown {
1103d37d430SMark Brown 	int ret;
1113d37d430SMark Brown 
1123d37d430SMark Brown 	ret = gcs_set_status(PR_SHADOW_STACK_ENABLE | PR_SHADOW_STACK_WRITE);
1133d37d430SMark Brown 	if (ret) {
1143d37d430SMark Brown 		ksft_print_msg("PR_SHADOW_STACK_ENABLE writeable failed: %d\n", ret);
1153d37d430SMark Brown 		return false;
1163d37d430SMark Brown 	}
1173d37d430SMark Brown 
1183d37d430SMark Brown 	ret = gcs_set_status(PR_SHADOW_STACK_ENABLE);
1193d37d430SMark Brown 	if (ret) {
1203d37d430SMark Brown 		ksft_print_msg("failed to restore plain enable %d\n", ret);
1213d37d430SMark Brown 		return false;
1223d37d430SMark Brown 	}
1233d37d430SMark Brown 
1243d37d430SMark Brown 	return true;
1253d37d430SMark Brown }
1263d37d430SMark Brown 
1273d37d430SMark Brown /* Also allow writes to stack */
enable_push_pop(void)1283d37d430SMark Brown static bool enable_push_pop(void)
1293d37d430SMark Brown {
1303d37d430SMark Brown 	int ret;
1313d37d430SMark Brown 
1323d37d430SMark Brown 	ret = gcs_set_status(PR_SHADOW_STACK_ENABLE | PR_SHADOW_STACK_PUSH);
1333d37d430SMark Brown 	if (ret) {
1343d37d430SMark Brown 		ksft_print_msg("PR_SHADOW_STACK_ENABLE with push failed: %d\n",
1353d37d430SMark Brown 			       ret);
1363d37d430SMark Brown 		return false;
1373d37d430SMark Brown 	}
1383d37d430SMark Brown 
1393d37d430SMark Brown 	ret = gcs_set_status(PR_SHADOW_STACK_ENABLE);
1403d37d430SMark Brown 	if (ret) {
1413d37d430SMark Brown 		ksft_print_msg("failed to restore plain enable %d\n", ret);
1423d37d430SMark Brown 		return false;
1433d37d430SMark Brown 	}
1443d37d430SMark Brown 
1453d37d430SMark Brown 	return true;
1463d37d430SMark Brown }
1473d37d430SMark Brown 
1483d37d430SMark Brown /* Enable GCS and allow everything */
enable_all(void)1493d37d430SMark Brown static bool enable_all(void)
1503d37d430SMark Brown {
1513d37d430SMark Brown 	int ret;
1523d37d430SMark Brown 
1533d37d430SMark Brown 	ret = gcs_set_status(PR_SHADOW_STACK_ENABLE | PR_SHADOW_STACK_PUSH |
1543d37d430SMark Brown 			     PR_SHADOW_STACK_WRITE);
1553d37d430SMark Brown 	if (ret) {
1563d37d430SMark Brown 		ksft_print_msg("PR_SHADOW_STACK_ENABLE with everything failed: %d\n",
1573d37d430SMark Brown 			       ret);
1583d37d430SMark Brown 		return false;
1593d37d430SMark Brown 	}
1603d37d430SMark Brown 
1613d37d430SMark Brown 	ret = gcs_set_status(PR_SHADOW_STACK_ENABLE);
1623d37d430SMark Brown 	if (ret) {
1633d37d430SMark Brown 		ksft_print_msg("failed to restore plain enable %d\n", ret);
1643d37d430SMark Brown 		return false;
1653d37d430SMark Brown 	}
1663d37d430SMark Brown 
1673d37d430SMark Brown 	return true;
1683d37d430SMark Brown }
1693d37d430SMark Brown 
enable_invalid(void)1703d37d430SMark Brown static bool enable_invalid(void)
1713d37d430SMark Brown {
1723d37d430SMark Brown 	int ret = gcs_set_status(ULONG_MAX);
1733d37d430SMark Brown 	if (ret == 0) {
1743d37d430SMark Brown 		ksft_print_msg("GCS_SET_STATUS %lx succeeded\n", ULONG_MAX);
1753d37d430SMark Brown 		return false;
1763d37d430SMark Brown 	}
1773d37d430SMark Brown 
1783d37d430SMark Brown 	return true;
1793d37d430SMark Brown }
1803d37d430SMark Brown 
1813d37d430SMark Brown /* Map a GCS */
map_guarded_stack(void)1823d37d430SMark Brown static bool map_guarded_stack(void)
1833d37d430SMark Brown {
1843d37d430SMark Brown 	int ret;
1853d37d430SMark Brown 	uint64_t *buf;
1863d37d430SMark Brown 	uint64_t expected_cap;
1873d37d430SMark Brown 	int elem;
1883d37d430SMark Brown 	bool pass = true;
1893d37d430SMark Brown 
1903d37d430SMark Brown 	buf = (void *)my_syscall3(__NR_map_shadow_stack, 0, page_size,
1913d37d430SMark Brown 				  SHADOW_STACK_SET_MARKER |
1923d37d430SMark Brown 				  SHADOW_STACK_SET_TOKEN);
1933d37d430SMark Brown 	if (buf == MAP_FAILED) {
1943d37d430SMark Brown 		ksft_print_msg("Failed to map %lu byte GCS: %d\n",
1953d37d430SMark Brown 			       page_size, errno);
1963d37d430SMark Brown 		return false;
1973d37d430SMark Brown 	}
1983d37d430SMark Brown 	ksft_print_msg("Mapped GCS at %p-%p\n", buf,
1993d37d430SMark Brown 		       (void *)((uint64_t)buf + page_size));
2003d37d430SMark Brown 
2013d37d430SMark Brown 	/* The top of the newly allocated region should be 0 */
2023d37d430SMark Brown 	elem = (page_size / sizeof(uint64_t)) - 1;
2033d37d430SMark Brown 	if (buf[elem]) {
2043d37d430SMark Brown 		ksft_print_msg("Last entry is 0x%llx not 0x0\n", buf[elem]);
2053d37d430SMark Brown 		pass = false;
2063d37d430SMark Brown 	}
2073d37d430SMark Brown 
2083d37d430SMark Brown 	/* Then a valid cap token */
2093d37d430SMark Brown 	elem--;
2103d37d430SMark Brown 	expected_cap = ((uint64_t)buf + page_size - 16);
2113d37d430SMark Brown 	expected_cap &= GCS_CAP_ADDR_MASK;
2123d37d430SMark Brown 	expected_cap |= GCS_CAP_VALID_TOKEN;
2133d37d430SMark Brown 	if (buf[elem] != expected_cap) {
2143d37d430SMark Brown 		ksft_print_msg("Cap entry is 0x%llx not 0x%llx\n",
2153d37d430SMark Brown 			       buf[elem], expected_cap);
2163d37d430SMark Brown 		pass = false;
2173d37d430SMark Brown 	}
2183d37d430SMark Brown 	ksft_print_msg("cap token is 0x%llx\n", buf[elem]);
2193d37d430SMark Brown 
2203d37d430SMark Brown 	/* The rest should be zeros */
2213d37d430SMark Brown 	for (elem = 0; elem < page_size / sizeof(uint64_t) - 2; elem++) {
2223d37d430SMark Brown 		if (!buf[elem])
2233d37d430SMark Brown 			continue;
2243d37d430SMark Brown 		ksft_print_msg("GCS slot %d is 0x%llx not 0x0\n",
2253d37d430SMark Brown 			       elem, buf[elem]);
2263d37d430SMark Brown 		pass = false;
2273d37d430SMark Brown 	}
2283d37d430SMark Brown 
2293d37d430SMark Brown 	ret = munmap(buf, page_size);
2303d37d430SMark Brown 	if (ret != 0) {
2313d37d430SMark Brown 		ksft_print_msg("Failed to unmap %ld byte GCS: %d\n",
2323d37d430SMark Brown 			       page_size, errno);
2333d37d430SMark Brown 		pass = false;
2343d37d430SMark Brown 	}
2353d37d430SMark Brown 
2363d37d430SMark Brown 	return pass;
2373d37d430SMark Brown }
2383d37d430SMark Brown 
2393d37d430SMark Brown /* A fork()ed process can run */
test_fork(void)2403d37d430SMark Brown static bool test_fork(void)
2413d37d430SMark Brown {
2423d37d430SMark Brown 	unsigned long child_mode;
2433d37d430SMark Brown 	int ret, status;
2443d37d430SMark Brown 	pid_t pid;
2453d37d430SMark Brown 	bool pass = true;
2463d37d430SMark Brown 
2473d37d430SMark Brown 	pid = fork();
2483d37d430SMark Brown 	if (pid == -1) {
2493d37d430SMark Brown 		ksft_print_msg("fork() failed: %d\n", errno);
2503d37d430SMark Brown 		pass = false;
2513d37d430SMark Brown 		goto out;
2523d37d430SMark Brown 	}
2533d37d430SMark Brown 	if (pid == 0) {
2543d37d430SMark Brown 		/* In child, make sure we can call a function, read
2553d37d430SMark Brown 		 * the GCS pointer and status and then exit */
2563d37d430SMark Brown 		valid_gcs_function();
2573d37d430SMark Brown 		get_gcspr();
2583d37d430SMark Brown 
2593d37d430SMark Brown 		ret = my_syscall5(__NR_prctl, PR_GET_SHADOW_STACK_STATUS,
2603d37d430SMark Brown 				  &child_mode, 0, 0, 0);
2613d37d430SMark Brown 		if (ret == 0 && !(child_mode & PR_SHADOW_STACK_ENABLE)) {
2623d37d430SMark Brown 			ksft_print_msg("GCS not enabled in child\n");
2633d37d430SMark Brown 			ret = -EINVAL;
2643d37d430SMark Brown 		}
2653d37d430SMark Brown 
2663d37d430SMark Brown 		exit(ret);
2673d37d430SMark Brown 	}
2683d37d430SMark Brown 
2693d37d430SMark Brown 	/*
2703d37d430SMark Brown 	 * In parent, check we can still do function calls then block
2713d37d430SMark Brown 	 * for the child.
2723d37d430SMark Brown 	 */
2733d37d430SMark Brown 	valid_gcs_function();
2743d37d430SMark Brown 
2753d37d430SMark Brown 	ksft_print_msg("Waiting for child %d\n", pid);
2763d37d430SMark Brown 
2773d37d430SMark Brown 	ret = waitpid(pid, &status, 0);
2783d37d430SMark Brown 	if (ret == -1) {
2793d37d430SMark Brown 		ksft_print_msg("Failed to wait for child: %d\n",
2803d37d430SMark Brown 			       errno);
2813d37d430SMark Brown 		return false;
2823d37d430SMark Brown 	}
2833d37d430SMark Brown 
2843d37d430SMark Brown 	if (!WIFEXITED(status)) {
2853d37d430SMark Brown 		ksft_print_msg("Child exited due to signal %d\n",
2863d37d430SMark Brown 			       WTERMSIG(status));
2873d37d430SMark Brown 		pass = false;
2883d37d430SMark Brown 	} else {
2893d37d430SMark Brown 		if (WEXITSTATUS(status)) {
2903d37d430SMark Brown 			ksft_print_msg("Child exited with status %d\n",
2913d37d430SMark Brown 				       WEXITSTATUS(status));
2923d37d430SMark Brown 			pass = false;
2933d37d430SMark Brown 		}
2943d37d430SMark Brown 	}
2953d37d430SMark Brown 
2963d37d430SMark Brown out:
2973d37d430SMark Brown 
2983d37d430SMark Brown 	return pass;
2993d37d430SMark Brown }
3003d37d430SMark Brown 
301*1536aa0fSMark Brown /* A vfork()ed process can run and exit */
test_vfork(void)302*1536aa0fSMark Brown static bool test_vfork(void)
303*1536aa0fSMark Brown {
304*1536aa0fSMark Brown 	unsigned long child_mode;
305*1536aa0fSMark Brown 	int ret, status;
306*1536aa0fSMark Brown 	pid_t pid;
307*1536aa0fSMark Brown 	bool pass = true;
308*1536aa0fSMark Brown 
309*1536aa0fSMark Brown 	pid = vfork();
310*1536aa0fSMark Brown 	if (pid == -1) {
311*1536aa0fSMark Brown 		ksft_print_msg("vfork() failed: %d\n", errno);
312*1536aa0fSMark Brown 		pass = false;
313*1536aa0fSMark Brown 		goto out;
314*1536aa0fSMark Brown 	}
315*1536aa0fSMark Brown 	if (pid == 0) {
316*1536aa0fSMark Brown 		/*
317*1536aa0fSMark Brown 		 * In child, make sure we can call a function, read
318*1536aa0fSMark Brown 		 * the GCS pointer and status and then exit.
319*1536aa0fSMark Brown 		 */
320*1536aa0fSMark Brown 		valid_gcs_function();
321*1536aa0fSMark Brown 		get_gcspr();
322*1536aa0fSMark Brown 
323*1536aa0fSMark Brown 		ret = my_syscall5(__NR_prctl, PR_GET_SHADOW_STACK_STATUS,
324*1536aa0fSMark Brown 				  &child_mode, 0, 0, 0);
325*1536aa0fSMark Brown 		if (ret == 0 && !(child_mode & PR_SHADOW_STACK_ENABLE)) {
326*1536aa0fSMark Brown 			ksft_print_msg("GCS not enabled in child\n");
327*1536aa0fSMark Brown 			ret = EXIT_FAILURE;
328*1536aa0fSMark Brown 		}
329*1536aa0fSMark Brown 
330*1536aa0fSMark Brown 		_exit(ret);
331*1536aa0fSMark Brown 	}
332*1536aa0fSMark Brown 
333*1536aa0fSMark Brown 	/*
334*1536aa0fSMark Brown 	 * In parent, check we can still do function calls then check
335*1536aa0fSMark Brown 	 * on the child.
336*1536aa0fSMark Brown 	 */
337*1536aa0fSMark Brown 	valid_gcs_function();
338*1536aa0fSMark Brown 
339*1536aa0fSMark Brown 	ksft_print_msg("Waiting for child %d\n", pid);
340*1536aa0fSMark Brown 
341*1536aa0fSMark Brown 	ret = waitpid(pid, &status, 0);
342*1536aa0fSMark Brown 	if (ret == -1) {
343*1536aa0fSMark Brown 		ksft_print_msg("Failed to wait for child: %d\n",
344*1536aa0fSMark Brown 			       errno);
345*1536aa0fSMark Brown 		return false;
346*1536aa0fSMark Brown 	}
347*1536aa0fSMark Brown 
348*1536aa0fSMark Brown 	if (!WIFEXITED(status)) {
349*1536aa0fSMark Brown 		ksft_print_msg("Child exited due to signal %d\n",
350*1536aa0fSMark Brown 			       WTERMSIG(status));
351*1536aa0fSMark Brown 		pass = false;
352*1536aa0fSMark Brown 	} else if (WEXITSTATUS(status)) {
353*1536aa0fSMark Brown 		ksft_print_msg("Child exited with status %d\n",
354*1536aa0fSMark Brown 			       WEXITSTATUS(status));
355*1536aa0fSMark Brown 		pass = false;
356*1536aa0fSMark Brown 	}
357*1536aa0fSMark Brown 
358*1536aa0fSMark Brown out:
359*1536aa0fSMark Brown 
360*1536aa0fSMark Brown 	return pass;
361*1536aa0fSMark Brown }
362*1536aa0fSMark Brown 
3633d37d430SMark Brown typedef bool (*gcs_test)(void);
3643d37d430SMark Brown 
3653d37d430SMark Brown static struct {
3663d37d430SMark Brown 	char *name;
3673d37d430SMark Brown 	gcs_test test;
3683d37d430SMark Brown 	bool needs_enable;
3693d37d430SMark Brown } tests[] = {
3703d37d430SMark Brown 	{ "read_status", read_status },
3713d37d430SMark Brown 	{ "base_enable", base_enable, true },
3723d37d430SMark Brown 	{ "read_gcspr_el0", read_gcspr_el0 },
3733d37d430SMark Brown 	{ "enable_writeable", enable_writeable, true },
3743d37d430SMark Brown 	{ "enable_push_pop", enable_push_pop, true },
3753d37d430SMark Brown 	{ "enable_all", enable_all, true },
3763d37d430SMark Brown 	{ "enable_invalid", enable_invalid, true },
3773d37d430SMark Brown 	{ "map_guarded_stack", map_guarded_stack },
3783d37d430SMark Brown 	{ "fork", test_fork },
379*1536aa0fSMark Brown 	{ "vfork", test_vfork },
3803d37d430SMark Brown };
3813d37d430SMark Brown 
main(void)3823d37d430SMark Brown int main(void)
3833d37d430SMark Brown {
3843d37d430SMark Brown 	int i, ret;
3853d37d430SMark Brown 	unsigned long gcs_mode;
3863d37d430SMark Brown 
3873d37d430SMark Brown 	ksft_print_header();
3883d37d430SMark Brown 
3893d37d430SMark Brown 	/*
3903d37d430SMark Brown 	 * We don't have getauxval() with nolibc so treat a failure to
3913d37d430SMark Brown 	 * read GCS state as a lack of support and skip.
3923d37d430SMark Brown 	 */
3933d37d430SMark Brown 	ret = my_syscall5(__NR_prctl, PR_GET_SHADOW_STACK_STATUS,
3943d37d430SMark Brown 			  &gcs_mode, 0, 0, 0);
3953d37d430SMark Brown 	if (ret != 0)
3963d37d430SMark Brown 		ksft_exit_skip("Failed to read GCS state: %d\n", ret);
3973d37d430SMark Brown 
3983d37d430SMark Brown 	if (!(gcs_mode & PR_SHADOW_STACK_ENABLE)) {
3993d37d430SMark Brown 		gcs_mode = PR_SHADOW_STACK_ENABLE;
4003d37d430SMark Brown 		ret = my_syscall5(__NR_prctl, PR_SET_SHADOW_STACK_STATUS,
4013d37d430SMark Brown 				  gcs_mode, 0, 0, 0);
4023d37d430SMark Brown 		if (ret != 0)
4033d37d430SMark Brown 			ksft_exit_fail_msg("Failed to enable GCS: %d\n", ret);
4043d37d430SMark Brown 	}
4053d37d430SMark Brown 
4063d37d430SMark Brown 	ksft_set_plan(ARRAY_SIZE(tests));
4073d37d430SMark Brown 
4083d37d430SMark Brown 	for (i = 0; i < ARRAY_SIZE(tests); i++) {
4093d37d430SMark Brown 		ksft_test_result((*tests[i].test)(), "%s\n", tests[i].name);
4103d37d430SMark Brown 	}
4113d37d430SMark Brown 
4123d37d430SMark Brown 	/* One last test: disable GCS, we can do this one time */
4133d37d430SMark Brown 	my_syscall5(__NR_prctl, PR_SET_SHADOW_STACK_STATUS, 0, 0, 0, 0);
4143d37d430SMark Brown 	if (ret != 0)
4153d37d430SMark Brown 		ksft_print_msg("Failed to disable GCS: %d\n", ret);
4163d37d430SMark Brown 
4173d37d430SMark Brown 	ksft_finished();
4183d37d430SMark Brown 
4193d37d430SMark Brown 	return 0;
4203d37d430SMark Brown }
421