File Index
Files and directories
Sign in
FILE

basic-gcs.c

File preview with syntax highlighting.

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