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