xref: /linux/tools/testing/selftests/riscv/abi/pointer_masking.c (revision f8c6263347e1740edf159584775332f4abf693a1)
17470b5afSSamuel Holland // SPDX-License-Identifier: GPL-2.0-only
27470b5afSSamuel Holland 
37470b5afSSamuel Holland #include <errno.h>
47470b5afSSamuel Holland #include <fcntl.h>
57470b5afSSamuel Holland #include <setjmp.h>
67470b5afSSamuel Holland #include <signal.h>
77470b5afSSamuel Holland #include <stdbool.h>
87470b5afSSamuel Holland #include <sys/prctl.h>
97470b5afSSamuel Holland #include <sys/wait.h>
107470b5afSSamuel Holland #include <unistd.h>
117470b5afSSamuel Holland 
127470b5afSSamuel Holland #include "../../kselftest.h"
137470b5afSSamuel Holland 
147470b5afSSamuel Holland #ifndef PR_PMLEN_SHIFT
157470b5afSSamuel Holland #define PR_PMLEN_SHIFT			24
167470b5afSSamuel Holland #endif
177470b5afSSamuel Holland #ifndef PR_PMLEN_MASK
187470b5afSSamuel Holland #define PR_PMLEN_MASK			(0x7fUL << PR_PMLEN_SHIFT)
197470b5afSSamuel Holland #endif
207470b5afSSamuel Holland 
217470b5afSSamuel Holland static int dev_zero;
227470b5afSSamuel Holland 
237470b5afSSamuel Holland static int pipefd[2];
247470b5afSSamuel Holland 
257470b5afSSamuel Holland static sigjmp_buf jmpbuf;
267470b5afSSamuel Holland 
sigsegv_handler(int sig)277470b5afSSamuel Holland static void sigsegv_handler(int sig)
287470b5afSSamuel Holland {
297470b5afSSamuel Holland 	siglongjmp(jmpbuf, 1);
307470b5afSSamuel Holland }
317470b5afSSamuel Holland 
327470b5afSSamuel Holland static int min_pmlen;
337470b5afSSamuel Holland static int max_pmlen;
347470b5afSSamuel Holland 
valid_pmlen(int pmlen)357470b5afSSamuel Holland static inline bool valid_pmlen(int pmlen)
367470b5afSSamuel Holland {
377470b5afSSamuel Holland 	return pmlen == 0 || pmlen == 7 || pmlen == 16;
387470b5afSSamuel Holland }
397470b5afSSamuel Holland 
test_pmlen(void)407470b5afSSamuel Holland static void test_pmlen(void)
417470b5afSSamuel Holland {
427470b5afSSamuel Holland 	ksft_print_msg("Testing available PMLEN values\n");
437470b5afSSamuel Holland 
447470b5afSSamuel Holland 	for (int request = 0; request <= 16; request++) {
457470b5afSSamuel Holland 		int pmlen, ret;
467470b5afSSamuel Holland 
477470b5afSSamuel Holland 		ret = prctl(PR_SET_TAGGED_ADDR_CTRL, request << PR_PMLEN_SHIFT, 0, 0, 0);
487470b5afSSamuel Holland 		if (ret)
497470b5afSSamuel Holland 			goto pr_set_error;
507470b5afSSamuel Holland 
517470b5afSSamuel Holland 		ret = prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0);
527470b5afSSamuel Holland 		ksft_test_result(ret >= 0, "PMLEN=%d PR_GET_TAGGED_ADDR_CTRL\n", request);
537470b5afSSamuel Holland 		if (ret < 0)
547470b5afSSamuel Holland 			goto pr_get_error;
557470b5afSSamuel Holland 
567470b5afSSamuel Holland 		pmlen = (ret & PR_PMLEN_MASK) >> PR_PMLEN_SHIFT;
577470b5afSSamuel Holland 		ksft_test_result(pmlen >= request, "PMLEN=%d constraint\n", request);
587470b5afSSamuel Holland 		ksft_test_result(valid_pmlen(pmlen), "PMLEN=%d validity\n", request);
597470b5afSSamuel Holland 
607470b5afSSamuel Holland 		if (min_pmlen == 0)
617470b5afSSamuel Holland 			min_pmlen = pmlen;
627470b5afSSamuel Holland 		if (max_pmlen < pmlen)
637470b5afSSamuel Holland 			max_pmlen = pmlen;
647470b5afSSamuel Holland 
657470b5afSSamuel Holland 		continue;
667470b5afSSamuel Holland 
677470b5afSSamuel Holland pr_set_error:
687470b5afSSamuel Holland 		ksft_test_result_skip("PMLEN=%d PR_GET_TAGGED_ADDR_CTRL\n", request);
697470b5afSSamuel Holland pr_get_error:
707470b5afSSamuel Holland 		ksft_test_result_skip("PMLEN=%d constraint\n", request);
717470b5afSSamuel Holland 		ksft_test_result_skip("PMLEN=%d validity\n", request);
727470b5afSSamuel Holland 	}
737470b5afSSamuel Holland 
747470b5afSSamuel Holland 	if (max_pmlen == 0)
757470b5afSSamuel Holland 		ksft_exit_fail_msg("Failed to enable pointer masking\n");
767470b5afSSamuel Holland }
777470b5afSSamuel Holland 
set_tagged_addr_ctrl(int pmlen,bool tagged_addr_abi)787470b5afSSamuel Holland static int set_tagged_addr_ctrl(int pmlen, bool tagged_addr_abi)
797470b5afSSamuel Holland {
807470b5afSSamuel Holland 	int arg, ret;
817470b5afSSamuel Holland 
827470b5afSSamuel Holland 	arg = pmlen << PR_PMLEN_SHIFT | tagged_addr_abi;
837470b5afSSamuel Holland 	ret = prctl(PR_SET_TAGGED_ADDR_CTRL, arg, 0, 0, 0);
847470b5afSSamuel Holland 	if (!ret) {
857470b5afSSamuel Holland 		ret = prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0);
867470b5afSSamuel Holland 		if (ret == arg)
877470b5afSSamuel Holland 			return 0;
887470b5afSSamuel Holland 	}
897470b5afSSamuel Holland 
907470b5afSSamuel Holland 	return ret < 0 ? -errno : -ENODATA;
917470b5afSSamuel Holland }
927470b5afSSamuel Holland 
test_dereference_pmlen(int pmlen)937470b5afSSamuel Holland static void test_dereference_pmlen(int pmlen)
947470b5afSSamuel Holland {
957470b5afSSamuel Holland 	static volatile int i;
967470b5afSSamuel Holland 	volatile int *p;
977470b5afSSamuel Holland 	int ret;
987470b5afSSamuel Holland 
997470b5afSSamuel Holland 	ret = set_tagged_addr_ctrl(pmlen, false);
1007470b5afSSamuel Holland 	if (ret)
1017470b5afSSamuel Holland 		return ksft_test_result_error("PMLEN=%d setup (%d)\n", pmlen, ret);
1027470b5afSSamuel Holland 
1037470b5afSSamuel Holland 	i = pmlen;
1047470b5afSSamuel Holland 
1057470b5afSSamuel Holland 	if (pmlen) {
1067470b5afSSamuel Holland 		p = (volatile int *)((uintptr_t)&i | 1UL << (__riscv_xlen - pmlen));
1077470b5afSSamuel Holland 
1087470b5afSSamuel Holland 		/* These dereferences should succeed. */
1097470b5afSSamuel Holland 		if (sigsetjmp(jmpbuf, 1))
1107470b5afSSamuel Holland 			return ksft_test_result_fail("PMLEN=%d valid tag\n", pmlen);
1117470b5afSSamuel Holland 		if (*p != pmlen)
1127470b5afSSamuel Holland 			return ksft_test_result_fail("PMLEN=%d bad value\n", pmlen);
1137470b5afSSamuel Holland 		++*p;
1147470b5afSSamuel Holland 	}
1157470b5afSSamuel Holland 
1167470b5afSSamuel Holland 	p = (volatile int *)((uintptr_t)&i | 1UL << (__riscv_xlen - pmlen - 1));
1177470b5afSSamuel Holland 
1187470b5afSSamuel Holland 	/* These dereferences should raise SIGSEGV. */
1197470b5afSSamuel Holland 	if (sigsetjmp(jmpbuf, 1))
1207470b5afSSamuel Holland 		return ksft_test_result_pass("PMLEN=%d dereference\n", pmlen);
1217470b5afSSamuel Holland 	++*p;
1227470b5afSSamuel Holland 	ksft_test_result_fail("PMLEN=%d invalid tag\n", pmlen);
1237470b5afSSamuel Holland }
1247470b5afSSamuel Holland 
test_dereference(void)1257470b5afSSamuel Holland static void test_dereference(void)
1267470b5afSSamuel Holland {
1277470b5afSSamuel Holland 	ksft_print_msg("Testing userspace pointer dereference\n");
1287470b5afSSamuel Holland 
1297470b5afSSamuel Holland 	signal(SIGSEGV, sigsegv_handler);
1307470b5afSSamuel Holland 
1317470b5afSSamuel Holland 	test_dereference_pmlen(0);
1327470b5afSSamuel Holland 	test_dereference_pmlen(min_pmlen);
1337470b5afSSamuel Holland 	test_dereference_pmlen(max_pmlen);
1347470b5afSSamuel Holland 
1357470b5afSSamuel Holland 	signal(SIGSEGV, SIG_DFL);
1367470b5afSSamuel Holland }
1377470b5afSSamuel Holland 
execve_child_sigsegv_handler(int sig)1387470b5afSSamuel Holland static void execve_child_sigsegv_handler(int sig)
1397470b5afSSamuel Holland {
1407470b5afSSamuel Holland 	exit(42);
1417470b5afSSamuel Holland }
1427470b5afSSamuel Holland 
execve_child(void)1437470b5afSSamuel Holland static int execve_child(void)
1447470b5afSSamuel Holland {
1457470b5afSSamuel Holland 	static volatile int i;
1467470b5afSSamuel Holland 	volatile int *p = (volatile int *)((uintptr_t)&i | 1UL << (__riscv_xlen - 7));
1477470b5afSSamuel Holland 
1487470b5afSSamuel Holland 	signal(SIGSEGV, execve_child_sigsegv_handler);
1497470b5afSSamuel Holland 
1507470b5afSSamuel Holland 	/* This dereference should raise SIGSEGV. */
1517470b5afSSamuel Holland 	return *p;
1527470b5afSSamuel Holland }
1537470b5afSSamuel Holland 
test_fork_exec(void)1547470b5afSSamuel Holland static void test_fork_exec(void)
1557470b5afSSamuel Holland {
1567470b5afSSamuel Holland 	int ret, status;
1577470b5afSSamuel Holland 
1587470b5afSSamuel Holland 	ksft_print_msg("Testing fork/exec behavior\n");
1597470b5afSSamuel Holland 
1607470b5afSSamuel Holland 	ret = set_tagged_addr_ctrl(min_pmlen, false);
1617470b5afSSamuel Holland 	if (ret)
1627470b5afSSamuel Holland 		return ksft_test_result_error("setup (%d)\n", ret);
1637470b5afSSamuel Holland 
1647470b5afSSamuel Holland 	if (fork()) {
1657470b5afSSamuel Holland 		wait(&status);
1667470b5afSSamuel Holland 		ksft_test_result(WIFEXITED(status) && WEXITSTATUS(status) == 42,
1677470b5afSSamuel Holland 				 "dereference after fork\n");
1687470b5afSSamuel Holland 	} else {
1697470b5afSSamuel Holland 		static volatile int i = 42;
1707470b5afSSamuel Holland 		volatile int *p;
1717470b5afSSamuel Holland 
1727470b5afSSamuel Holland 		p = (volatile int *)((uintptr_t)&i | 1UL << (__riscv_xlen - min_pmlen));
1737470b5afSSamuel Holland 
1747470b5afSSamuel Holland 		/* This dereference should succeed. */
1757470b5afSSamuel Holland 		exit(*p);
1767470b5afSSamuel Holland 	}
1777470b5afSSamuel Holland 
1787470b5afSSamuel Holland 	if (fork()) {
1797470b5afSSamuel Holland 		wait(&status);
1807470b5afSSamuel Holland 		ksft_test_result(WIFEXITED(status) && WEXITSTATUS(status) == 42,
1817470b5afSSamuel Holland 				 "dereference after fork+exec\n");
1827470b5afSSamuel Holland 	} else {
1837470b5afSSamuel Holland 		/* Will call execve_child(). */
1847470b5afSSamuel Holland 		execve("/proc/self/exe", (char *const []) { "", NULL }, NULL);
1857470b5afSSamuel Holland 	}
1867470b5afSSamuel Holland }
1877470b5afSSamuel Holland 
pwrite_wrapper(int fd,void * buf,size_t count,const char * msg)188*498d5b14SCharlie Jenkins static bool pwrite_wrapper(int fd, void *buf, size_t count, const char *msg)
189*498d5b14SCharlie Jenkins {
190*498d5b14SCharlie Jenkins 	int ret = pwrite(fd, buf, count, 0);
191*498d5b14SCharlie Jenkins 
192*498d5b14SCharlie Jenkins 	if (ret != count) {
193*498d5b14SCharlie Jenkins 		ksft_perror(msg);
194*498d5b14SCharlie Jenkins 		return false;
195*498d5b14SCharlie Jenkins 	}
196*498d5b14SCharlie Jenkins 	return true;
197*498d5b14SCharlie Jenkins }
198*498d5b14SCharlie Jenkins 
test_tagged_addr_abi_sysctl(void)1997470b5afSSamuel Holland static void test_tagged_addr_abi_sysctl(void)
2007470b5afSSamuel Holland {
201*498d5b14SCharlie Jenkins 	char *err_pwrite_msg = "failed to write to /proc/sys/abi/tagged_addr_disabled\n";
2027470b5afSSamuel Holland 	char value;
2037470b5afSSamuel Holland 	int fd;
2047470b5afSSamuel Holland 
2057470b5afSSamuel Holland 	ksft_print_msg("Testing tagged address ABI sysctl\n");
2067470b5afSSamuel Holland 
2077470b5afSSamuel Holland 	fd = open("/proc/sys/abi/tagged_addr_disabled", O_WRONLY);
2087470b5afSSamuel Holland 	if (fd < 0) {
2097470b5afSSamuel Holland 		ksft_test_result_skip("failed to open sysctl file\n");
2107470b5afSSamuel Holland 		ksft_test_result_skip("failed to open sysctl file\n");
2117470b5afSSamuel Holland 		return;
2127470b5afSSamuel Holland 	}
2137470b5afSSamuel Holland 
2147470b5afSSamuel Holland 	value = '1';
215*498d5b14SCharlie Jenkins 	if (!pwrite_wrapper(fd, &value, 1, "write '1'"))
216*498d5b14SCharlie Jenkins 		ksft_test_result_fail(err_pwrite_msg);
217*498d5b14SCharlie Jenkins 	else
2187470b5afSSamuel Holland 		ksft_test_result(set_tagged_addr_ctrl(min_pmlen, true) == -EINVAL,
2197470b5afSSamuel Holland 				 "sysctl disabled\n");
2207470b5afSSamuel Holland 
2217470b5afSSamuel Holland 	value = '0';
222*498d5b14SCharlie Jenkins 	if (!pwrite_wrapper(fd, &value, 1, "write '0'"))
223*498d5b14SCharlie Jenkins 		ksft_test_result_fail(err_pwrite_msg);
224*498d5b14SCharlie Jenkins 	else
2257470b5afSSamuel Holland 		ksft_test_result(set_tagged_addr_ctrl(min_pmlen, true) == 0,
2267470b5afSSamuel Holland 				 "sysctl enabled\n");
2277470b5afSSamuel Holland 
2287470b5afSSamuel Holland 	set_tagged_addr_ctrl(0, false);
2297470b5afSSamuel Holland 
2307470b5afSSamuel Holland 	close(fd);
2317470b5afSSamuel Holland }
2327470b5afSSamuel Holland 
test_tagged_addr_abi_pmlen(int pmlen)2337470b5afSSamuel Holland static void test_tagged_addr_abi_pmlen(int pmlen)
2347470b5afSSamuel Holland {
2357470b5afSSamuel Holland 	int i, *p, ret;
2367470b5afSSamuel Holland 
2377470b5afSSamuel Holland 	i = ~pmlen;
2387470b5afSSamuel Holland 
2397470b5afSSamuel Holland 	if (pmlen) {
2407470b5afSSamuel Holland 		p = (int *)((uintptr_t)&i | 1UL << (__riscv_xlen - pmlen));
2417470b5afSSamuel Holland 
2427470b5afSSamuel Holland 		ret = set_tagged_addr_ctrl(pmlen, false);
2437470b5afSSamuel Holland 		if (ret)
2447470b5afSSamuel Holland 			return ksft_test_result_error("PMLEN=%d ABI disabled setup (%d)\n",
2457470b5afSSamuel Holland 						      pmlen, ret);
2467470b5afSSamuel Holland 
2477470b5afSSamuel Holland 		ret = write(pipefd[1], p, sizeof(*p));
2487470b5afSSamuel Holland 		if (ret >= 0 || errno != EFAULT)
2497470b5afSSamuel Holland 			return ksft_test_result_fail("PMLEN=%d ABI disabled write\n", pmlen);
2507470b5afSSamuel Holland 
2517470b5afSSamuel Holland 		ret = read(dev_zero, p, sizeof(*p));
2527470b5afSSamuel Holland 		if (ret >= 0 || errno != EFAULT)
2537470b5afSSamuel Holland 			return ksft_test_result_fail("PMLEN=%d ABI disabled read\n", pmlen);
2547470b5afSSamuel Holland 
2557470b5afSSamuel Holland 		if (i != ~pmlen)
2567470b5afSSamuel Holland 			return ksft_test_result_fail("PMLEN=%d ABI disabled value\n", pmlen);
2577470b5afSSamuel Holland 
2587470b5afSSamuel Holland 		ret = set_tagged_addr_ctrl(pmlen, true);
2597470b5afSSamuel Holland 		if (ret)
2607470b5afSSamuel Holland 			return ksft_test_result_error("PMLEN=%d ABI enabled setup (%d)\n",
2617470b5afSSamuel Holland 						      pmlen, ret);
2627470b5afSSamuel Holland 
2637470b5afSSamuel Holland 		ret = write(pipefd[1], p, sizeof(*p));
2647470b5afSSamuel Holland 		if (ret != sizeof(*p))
2657470b5afSSamuel Holland 			return ksft_test_result_fail("PMLEN=%d ABI enabled write\n", pmlen);
2667470b5afSSamuel Holland 
2677470b5afSSamuel Holland 		ret = read(dev_zero, p, sizeof(*p));
2687470b5afSSamuel Holland 		if (ret != sizeof(*p))
2697470b5afSSamuel Holland 			return ksft_test_result_fail("PMLEN=%d ABI enabled read\n", pmlen);
2707470b5afSSamuel Holland 
2717470b5afSSamuel Holland 		if (i)
2727470b5afSSamuel Holland 			return ksft_test_result_fail("PMLEN=%d ABI enabled value\n", pmlen);
2737470b5afSSamuel Holland 
2747470b5afSSamuel Holland 		i = ~pmlen;
2757470b5afSSamuel Holland 	} else {
2767470b5afSSamuel Holland 		/* The tagged address ABI cannot be enabled when PMLEN == 0. */
2777470b5afSSamuel Holland 		ret = set_tagged_addr_ctrl(pmlen, true);
2787470b5afSSamuel Holland 		if (ret != -EINVAL)
2797470b5afSSamuel Holland 			return ksft_test_result_error("PMLEN=%d ABI setup (%d)\n",
2807470b5afSSamuel Holland 						      pmlen, ret);
2817470b5afSSamuel Holland 	}
2827470b5afSSamuel Holland 
2837470b5afSSamuel Holland 	p = (int *)((uintptr_t)&i | 1UL << (__riscv_xlen - pmlen - 1));
2847470b5afSSamuel Holland 
2857470b5afSSamuel Holland 	ret = write(pipefd[1], p, sizeof(*p));
2867470b5afSSamuel Holland 	if (ret >= 0 || errno != EFAULT)
2877470b5afSSamuel Holland 		return ksft_test_result_fail("PMLEN=%d invalid tag write (%d)\n", pmlen, errno);
2887470b5afSSamuel Holland 
2897470b5afSSamuel Holland 	ret = read(dev_zero, p, sizeof(*p));
2907470b5afSSamuel Holland 	if (ret >= 0 || errno != EFAULT)
2917470b5afSSamuel Holland 		return ksft_test_result_fail("PMLEN=%d invalid tag read\n", pmlen);
2927470b5afSSamuel Holland 
2937470b5afSSamuel Holland 	if (i != ~pmlen)
2947470b5afSSamuel Holland 		return ksft_test_result_fail("PMLEN=%d invalid tag value\n", pmlen);
2957470b5afSSamuel Holland 
2967470b5afSSamuel Holland 	ksft_test_result_pass("PMLEN=%d tagged address ABI\n", pmlen);
2977470b5afSSamuel Holland }
2987470b5afSSamuel Holland 
test_tagged_addr_abi(void)2997470b5afSSamuel Holland static void test_tagged_addr_abi(void)
3007470b5afSSamuel Holland {
3017470b5afSSamuel Holland 	ksft_print_msg("Testing tagged address ABI\n");
3027470b5afSSamuel Holland 
3037470b5afSSamuel Holland 	test_tagged_addr_abi_pmlen(0);
3047470b5afSSamuel Holland 	test_tagged_addr_abi_pmlen(min_pmlen);
3057470b5afSSamuel Holland 	test_tagged_addr_abi_pmlen(max_pmlen);
3067470b5afSSamuel Holland }
3077470b5afSSamuel Holland 
3087470b5afSSamuel Holland static struct test_info {
3097470b5afSSamuel Holland 	unsigned int nr_tests;
3107470b5afSSamuel Holland 	void (*test_fn)(void);
3117470b5afSSamuel Holland } tests[] = {
3127470b5afSSamuel Holland 	{ .nr_tests = 17 * 3, test_pmlen },
3137470b5afSSamuel Holland 	{ .nr_tests = 3, test_dereference },
3147470b5afSSamuel Holland 	{ .nr_tests = 2, test_fork_exec },
3157470b5afSSamuel Holland 	{ .nr_tests = 2, test_tagged_addr_abi_sysctl },
3167470b5afSSamuel Holland 	{ .nr_tests = 3, test_tagged_addr_abi },
3177470b5afSSamuel Holland };
3187470b5afSSamuel Holland 
main(int argc,char ** argv)3197470b5afSSamuel Holland int main(int argc, char **argv)
3207470b5afSSamuel Holland {
3217470b5afSSamuel Holland 	unsigned int plan = 0;
3227470b5afSSamuel Holland 	int ret;
3237470b5afSSamuel Holland 
3247470b5afSSamuel Holland 	/* Check if this is the child process after execve(). */
3257470b5afSSamuel Holland 	if (!argv[0][0])
3267470b5afSSamuel Holland 		return execve_child();
3277470b5afSSamuel Holland 
3287470b5afSSamuel Holland 	dev_zero = open("/dev/zero", O_RDWR);
3297470b5afSSamuel Holland 	if (dev_zero < 0)
3307470b5afSSamuel Holland 		return 1;
3317470b5afSSamuel Holland 
3327470b5afSSamuel Holland 	/* Write to a pipe so the kernel must dereference the buffer pointer. */
3337470b5afSSamuel Holland 	ret = pipe(pipefd);
3347470b5afSSamuel Holland 	if (ret)
3357470b5afSSamuel Holland 		return 1;
3367470b5afSSamuel Holland 
3377470b5afSSamuel Holland 	ksft_print_header();
3387470b5afSSamuel Holland 
3397470b5afSSamuel Holland 	for (int i = 0; i < ARRAY_SIZE(tests); i++)
3407470b5afSSamuel Holland 		plan += tests[i].nr_tests;
3417470b5afSSamuel Holland 
3427470b5afSSamuel Holland 	ksft_set_plan(plan);
3437470b5afSSamuel Holland 
3447470b5afSSamuel Holland 	for (int i = 0; i < ARRAY_SIZE(tests); i++)
3457470b5afSSamuel Holland 		tests[i].test_fn();
3467470b5afSSamuel Holland 
3477470b5afSSamuel Holland 	ksft_finished();
3487470b5afSSamuel Holland }
349