xref: /linux/tools/testing/selftests/riscv/abi/pointer_masking.c (revision 7470b5afd150e683c7aef03961d0c4c6f500de3b)
1*7470b5afSSamuel Holland // SPDX-License-Identifier: GPL-2.0-only
2*7470b5afSSamuel Holland 
3*7470b5afSSamuel Holland #include <errno.h>
4*7470b5afSSamuel Holland #include <fcntl.h>
5*7470b5afSSamuel Holland #include <setjmp.h>
6*7470b5afSSamuel Holland #include <signal.h>
7*7470b5afSSamuel Holland #include <stdbool.h>
8*7470b5afSSamuel Holland #include <sys/prctl.h>
9*7470b5afSSamuel Holland #include <sys/wait.h>
10*7470b5afSSamuel Holland #include <unistd.h>
11*7470b5afSSamuel Holland 
12*7470b5afSSamuel Holland #include "../../kselftest.h"
13*7470b5afSSamuel Holland 
14*7470b5afSSamuel Holland #ifndef PR_PMLEN_SHIFT
15*7470b5afSSamuel Holland #define PR_PMLEN_SHIFT			24
16*7470b5afSSamuel Holland #endif
17*7470b5afSSamuel Holland #ifndef PR_PMLEN_MASK
18*7470b5afSSamuel Holland #define PR_PMLEN_MASK			(0x7fUL << PR_PMLEN_SHIFT)
19*7470b5afSSamuel Holland #endif
20*7470b5afSSamuel Holland 
21*7470b5afSSamuel Holland static int dev_zero;
22*7470b5afSSamuel Holland 
23*7470b5afSSamuel Holland static int pipefd[2];
24*7470b5afSSamuel Holland 
25*7470b5afSSamuel Holland static sigjmp_buf jmpbuf;
26*7470b5afSSamuel Holland 
27*7470b5afSSamuel Holland static void sigsegv_handler(int sig)
28*7470b5afSSamuel Holland {
29*7470b5afSSamuel Holland 	siglongjmp(jmpbuf, 1);
30*7470b5afSSamuel Holland }
31*7470b5afSSamuel Holland 
32*7470b5afSSamuel Holland static int min_pmlen;
33*7470b5afSSamuel Holland static int max_pmlen;
34*7470b5afSSamuel Holland 
35*7470b5afSSamuel Holland static inline bool valid_pmlen(int pmlen)
36*7470b5afSSamuel Holland {
37*7470b5afSSamuel Holland 	return pmlen == 0 || pmlen == 7 || pmlen == 16;
38*7470b5afSSamuel Holland }
39*7470b5afSSamuel Holland 
40*7470b5afSSamuel Holland static void test_pmlen(void)
41*7470b5afSSamuel Holland {
42*7470b5afSSamuel Holland 	ksft_print_msg("Testing available PMLEN values\n");
43*7470b5afSSamuel Holland 
44*7470b5afSSamuel Holland 	for (int request = 0; request <= 16; request++) {
45*7470b5afSSamuel Holland 		int pmlen, ret;
46*7470b5afSSamuel Holland 
47*7470b5afSSamuel Holland 		ret = prctl(PR_SET_TAGGED_ADDR_CTRL, request << PR_PMLEN_SHIFT, 0, 0, 0);
48*7470b5afSSamuel Holland 		if (ret)
49*7470b5afSSamuel Holland 			goto pr_set_error;
50*7470b5afSSamuel Holland 
51*7470b5afSSamuel Holland 		ret = prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0);
52*7470b5afSSamuel Holland 		ksft_test_result(ret >= 0, "PMLEN=%d PR_GET_TAGGED_ADDR_CTRL\n", request);
53*7470b5afSSamuel Holland 		if (ret < 0)
54*7470b5afSSamuel Holland 			goto pr_get_error;
55*7470b5afSSamuel Holland 
56*7470b5afSSamuel Holland 		pmlen = (ret & PR_PMLEN_MASK) >> PR_PMLEN_SHIFT;
57*7470b5afSSamuel Holland 		ksft_test_result(pmlen >= request, "PMLEN=%d constraint\n", request);
58*7470b5afSSamuel Holland 		ksft_test_result(valid_pmlen(pmlen), "PMLEN=%d validity\n", request);
59*7470b5afSSamuel Holland 
60*7470b5afSSamuel Holland 		if (min_pmlen == 0)
61*7470b5afSSamuel Holland 			min_pmlen = pmlen;
62*7470b5afSSamuel Holland 		if (max_pmlen < pmlen)
63*7470b5afSSamuel Holland 			max_pmlen = pmlen;
64*7470b5afSSamuel Holland 
65*7470b5afSSamuel Holland 		continue;
66*7470b5afSSamuel Holland 
67*7470b5afSSamuel Holland pr_set_error:
68*7470b5afSSamuel Holland 		ksft_test_result_skip("PMLEN=%d PR_GET_TAGGED_ADDR_CTRL\n", request);
69*7470b5afSSamuel Holland pr_get_error:
70*7470b5afSSamuel Holland 		ksft_test_result_skip("PMLEN=%d constraint\n", request);
71*7470b5afSSamuel Holland 		ksft_test_result_skip("PMLEN=%d validity\n", request);
72*7470b5afSSamuel Holland 	}
73*7470b5afSSamuel Holland 
74*7470b5afSSamuel Holland 	if (max_pmlen == 0)
75*7470b5afSSamuel Holland 		ksft_exit_fail_msg("Failed to enable pointer masking\n");
76*7470b5afSSamuel Holland }
77*7470b5afSSamuel Holland 
78*7470b5afSSamuel Holland static int set_tagged_addr_ctrl(int pmlen, bool tagged_addr_abi)
79*7470b5afSSamuel Holland {
80*7470b5afSSamuel Holland 	int arg, ret;
81*7470b5afSSamuel Holland 
82*7470b5afSSamuel Holland 	arg = pmlen << PR_PMLEN_SHIFT | tagged_addr_abi;
83*7470b5afSSamuel Holland 	ret = prctl(PR_SET_TAGGED_ADDR_CTRL, arg, 0, 0, 0);
84*7470b5afSSamuel Holland 	if (!ret) {
85*7470b5afSSamuel Holland 		ret = prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0);
86*7470b5afSSamuel Holland 		if (ret == arg)
87*7470b5afSSamuel Holland 			return 0;
88*7470b5afSSamuel Holland 	}
89*7470b5afSSamuel Holland 
90*7470b5afSSamuel Holland 	return ret < 0 ? -errno : -ENODATA;
91*7470b5afSSamuel Holland }
92*7470b5afSSamuel Holland 
93*7470b5afSSamuel Holland static void test_dereference_pmlen(int pmlen)
94*7470b5afSSamuel Holland {
95*7470b5afSSamuel Holland 	static volatile int i;
96*7470b5afSSamuel Holland 	volatile int *p;
97*7470b5afSSamuel Holland 	int ret;
98*7470b5afSSamuel Holland 
99*7470b5afSSamuel Holland 	ret = set_tagged_addr_ctrl(pmlen, false);
100*7470b5afSSamuel Holland 	if (ret)
101*7470b5afSSamuel Holland 		return ksft_test_result_error("PMLEN=%d setup (%d)\n", pmlen, ret);
102*7470b5afSSamuel Holland 
103*7470b5afSSamuel Holland 	i = pmlen;
104*7470b5afSSamuel Holland 
105*7470b5afSSamuel Holland 	if (pmlen) {
106*7470b5afSSamuel Holland 		p = (volatile int *)((uintptr_t)&i | 1UL << (__riscv_xlen - pmlen));
107*7470b5afSSamuel Holland 
108*7470b5afSSamuel Holland 		/* These dereferences should succeed. */
109*7470b5afSSamuel Holland 		if (sigsetjmp(jmpbuf, 1))
110*7470b5afSSamuel Holland 			return ksft_test_result_fail("PMLEN=%d valid tag\n", pmlen);
111*7470b5afSSamuel Holland 		if (*p != pmlen)
112*7470b5afSSamuel Holland 			return ksft_test_result_fail("PMLEN=%d bad value\n", pmlen);
113*7470b5afSSamuel Holland 		++*p;
114*7470b5afSSamuel Holland 	}
115*7470b5afSSamuel Holland 
116*7470b5afSSamuel Holland 	p = (volatile int *)((uintptr_t)&i | 1UL << (__riscv_xlen - pmlen - 1));
117*7470b5afSSamuel Holland 
118*7470b5afSSamuel Holland 	/* These dereferences should raise SIGSEGV. */
119*7470b5afSSamuel Holland 	if (sigsetjmp(jmpbuf, 1))
120*7470b5afSSamuel Holland 		return ksft_test_result_pass("PMLEN=%d dereference\n", pmlen);
121*7470b5afSSamuel Holland 	++*p;
122*7470b5afSSamuel Holland 	ksft_test_result_fail("PMLEN=%d invalid tag\n", pmlen);
123*7470b5afSSamuel Holland }
124*7470b5afSSamuel Holland 
125*7470b5afSSamuel Holland static void test_dereference(void)
126*7470b5afSSamuel Holland {
127*7470b5afSSamuel Holland 	ksft_print_msg("Testing userspace pointer dereference\n");
128*7470b5afSSamuel Holland 
129*7470b5afSSamuel Holland 	signal(SIGSEGV, sigsegv_handler);
130*7470b5afSSamuel Holland 
131*7470b5afSSamuel Holland 	test_dereference_pmlen(0);
132*7470b5afSSamuel Holland 	test_dereference_pmlen(min_pmlen);
133*7470b5afSSamuel Holland 	test_dereference_pmlen(max_pmlen);
134*7470b5afSSamuel Holland 
135*7470b5afSSamuel Holland 	signal(SIGSEGV, SIG_DFL);
136*7470b5afSSamuel Holland }
137*7470b5afSSamuel Holland 
138*7470b5afSSamuel Holland static void execve_child_sigsegv_handler(int sig)
139*7470b5afSSamuel Holland {
140*7470b5afSSamuel Holland 	exit(42);
141*7470b5afSSamuel Holland }
142*7470b5afSSamuel Holland 
143*7470b5afSSamuel Holland static int execve_child(void)
144*7470b5afSSamuel Holland {
145*7470b5afSSamuel Holland 	static volatile int i;
146*7470b5afSSamuel Holland 	volatile int *p = (volatile int *)((uintptr_t)&i | 1UL << (__riscv_xlen - 7));
147*7470b5afSSamuel Holland 
148*7470b5afSSamuel Holland 	signal(SIGSEGV, execve_child_sigsegv_handler);
149*7470b5afSSamuel Holland 
150*7470b5afSSamuel Holland 	/* This dereference should raise SIGSEGV. */
151*7470b5afSSamuel Holland 	return *p;
152*7470b5afSSamuel Holland }
153*7470b5afSSamuel Holland 
154*7470b5afSSamuel Holland static void test_fork_exec(void)
155*7470b5afSSamuel Holland {
156*7470b5afSSamuel Holland 	int ret, status;
157*7470b5afSSamuel Holland 
158*7470b5afSSamuel Holland 	ksft_print_msg("Testing fork/exec behavior\n");
159*7470b5afSSamuel Holland 
160*7470b5afSSamuel Holland 	ret = set_tagged_addr_ctrl(min_pmlen, false);
161*7470b5afSSamuel Holland 	if (ret)
162*7470b5afSSamuel Holland 		return ksft_test_result_error("setup (%d)\n", ret);
163*7470b5afSSamuel Holland 
164*7470b5afSSamuel Holland 	if (fork()) {
165*7470b5afSSamuel Holland 		wait(&status);
166*7470b5afSSamuel Holland 		ksft_test_result(WIFEXITED(status) && WEXITSTATUS(status) == 42,
167*7470b5afSSamuel Holland 				 "dereference after fork\n");
168*7470b5afSSamuel Holland 	} else {
169*7470b5afSSamuel Holland 		static volatile int i = 42;
170*7470b5afSSamuel Holland 		volatile int *p;
171*7470b5afSSamuel Holland 
172*7470b5afSSamuel Holland 		p = (volatile int *)((uintptr_t)&i | 1UL << (__riscv_xlen - min_pmlen));
173*7470b5afSSamuel Holland 
174*7470b5afSSamuel Holland 		/* This dereference should succeed. */
175*7470b5afSSamuel Holland 		exit(*p);
176*7470b5afSSamuel Holland 	}
177*7470b5afSSamuel Holland 
178*7470b5afSSamuel Holland 	if (fork()) {
179*7470b5afSSamuel Holland 		wait(&status);
180*7470b5afSSamuel Holland 		ksft_test_result(WIFEXITED(status) && WEXITSTATUS(status) == 42,
181*7470b5afSSamuel Holland 				 "dereference after fork+exec\n");
182*7470b5afSSamuel Holland 	} else {
183*7470b5afSSamuel Holland 		/* Will call execve_child(). */
184*7470b5afSSamuel Holland 		execve("/proc/self/exe", (char *const []) { "", NULL }, NULL);
185*7470b5afSSamuel Holland 	}
186*7470b5afSSamuel Holland }
187*7470b5afSSamuel Holland 
188*7470b5afSSamuel Holland static void test_tagged_addr_abi_sysctl(void)
189*7470b5afSSamuel Holland {
190*7470b5afSSamuel Holland 	char value;
191*7470b5afSSamuel Holland 	int fd;
192*7470b5afSSamuel Holland 
193*7470b5afSSamuel Holland 	ksft_print_msg("Testing tagged address ABI sysctl\n");
194*7470b5afSSamuel Holland 
195*7470b5afSSamuel Holland 	fd = open("/proc/sys/abi/tagged_addr_disabled", O_WRONLY);
196*7470b5afSSamuel Holland 	if (fd < 0) {
197*7470b5afSSamuel Holland 		ksft_test_result_skip("failed to open sysctl file\n");
198*7470b5afSSamuel Holland 		ksft_test_result_skip("failed to open sysctl file\n");
199*7470b5afSSamuel Holland 		return;
200*7470b5afSSamuel Holland 	}
201*7470b5afSSamuel Holland 
202*7470b5afSSamuel Holland 	value = '1';
203*7470b5afSSamuel Holland 	pwrite(fd, &value, 1, 0);
204*7470b5afSSamuel Holland 	ksft_test_result(set_tagged_addr_ctrl(min_pmlen, true) == -EINVAL,
205*7470b5afSSamuel Holland 			 "sysctl disabled\n");
206*7470b5afSSamuel Holland 
207*7470b5afSSamuel Holland 	value = '0';
208*7470b5afSSamuel Holland 	pwrite(fd, &value, 1, 0);
209*7470b5afSSamuel Holland 	ksft_test_result(set_tagged_addr_ctrl(min_pmlen, true) == 0,
210*7470b5afSSamuel Holland 			 "sysctl enabled\n");
211*7470b5afSSamuel Holland 
212*7470b5afSSamuel Holland 	set_tagged_addr_ctrl(0, false);
213*7470b5afSSamuel Holland 
214*7470b5afSSamuel Holland 	close(fd);
215*7470b5afSSamuel Holland }
216*7470b5afSSamuel Holland 
217*7470b5afSSamuel Holland static void test_tagged_addr_abi_pmlen(int pmlen)
218*7470b5afSSamuel Holland {
219*7470b5afSSamuel Holland 	int i, *p, ret;
220*7470b5afSSamuel Holland 
221*7470b5afSSamuel Holland 	i = ~pmlen;
222*7470b5afSSamuel Holland 
223*7470b5afSSamuel Holland 	if (pmlen) {
224*7470b5afSSamuel Holland 		p = (int *)((uintptr_t)&i | 1UL << (__riscv_xlen - pmlen));
225*7470b5afSSamuel Holland 
226*7470b5afSSamuel Holland 		ret = set_tagged_addr_ctrl(pmlen, false);
227*7470b5afSSamuel Holland 		if (ret)
228*7470b5afSSamuel Holland 			return ksft_test_result_error("PMLEN=%d ABI disabled setup (%d)\n",
229*7470b5afSSamuel Holland 						      pmlen, ret);
230*7470b5afSSamuel Holland 
231*7470b5afSSamuel Holland 		ret = write(pipefd[1], p, sizeof(*p));
232*7470b5afSSamuel Holland 		if (ret >= 0 || errno != EFAULT)
233*7470b5afSSamuel Holland 			return ksft_test_result_fail("PMLEN=%d ABI disabled write\n", pmlen);
234*7470b5afSSamuel Holland 
235*7470b5afSSamuel Holland 		ret = read(dev_zero, p, sizeof(*p));
236*7470b5afSSamuel Holland 		if (ret >= 0 || errno != EFAULT)
237*7470b5afSSamuel Holland 			return ksft_test_result_fail("PMLEN=%d ABI disabled read\n", pmlen);
238*7470b5afSSamuel Holland 
239*7470b5afSSamuel Holland 		if (i != ~pmlen)
240*7470b5afSSamuel Holland 			return ksft_test_result_fail("PMLEN=%d ABI disabled value\n", pmlen);
241*7470b5afSSamuel Holland 
242*7470b5afSSamuel Holland 		ret = set_tagged_addr_ctrl(pmlen, true);
243*7470b5afSSamuel Holland 		if (ret)
244*7470b5afSSamuel Holland 			return ksft_test_result_error("PMLEN=%d ABI enabled setup (%d)\n",
245*7470b5afSSamuel Holland 						      pmlen, ret);
246*7470b5afSSamuel Holland 
247*7470b5afSSamuel Holland 		ret = write(pipefd[1], p, sizeof(*p));
248*7470b5afSSamuel Holland 		if (ret != sizeof(*p))
249*7470b5afSSamuel Holland 			return ksft_test_result_fail("PMLEN=%d ABI enabled write\n", pmlen);
250*7470b5afSSamuel Holland 
251*7470b5afSSamuel Holland 		ret = read(dev_zero, p, sizeof(*p));
252*7470b5afSSamuel Holland 		if (ret != sizeof(*p))
253*7470b5afSSamuel Holland 			return ksft_test_result_fail("PMLEN=%d ABI enabled read\n", pmlen);
254*7470b5afSSamuel Holland 
255*7470b5afSSamuel Holland 		if (i)
256*7470b5afSSamuel Holland 			return ksft_test_result_fail("PMLEN=%d ABI enabled value\n", pmlen);
257*7470b5afSSamuel Holland 
258*7470b5afSSamuel Holland 		i = ~pmlen;
259*7470b5afSSamuel Holland 	} else {
260*7470b5afSSamuel Holland 		/* The tagged address ABI cannot be enabled when PMLEN == 0. */
261*7470b5afSSamuel Holland 		ret = set_tagged_addr_ctrl(pmlen, true);
262*7470b5afSSamuel Holland 		if (ret != -EINVAL)
263*7470b5afSSamuel Holland 			return ksft_test_result_error("PMLEN=%d ABI setup (%d)\n",
264*7470b5afSSamuel Holland 						      pmlen, ret);
265*7470b5afSSamuel Holland 	}
266*7470b5afSSamuel Holland 
267*7470b5afSSamuel Holland 	p = (int *)((uintptr_t)&i | 1UL << (__riscv_xlen - pmlen - 1));
268*7470b5afSSamuel Holland 
269*7470b5afSSamuel Holland 	ret = write(pipefd[1], p, sizeof(*p));
270*7470b5afSSamuel Holland 	if (ret >= 0 || errno != EFAULT)
271*7470b5afSSamuel Holland 		return ksft_test_result_fail("PMLEN=%d invalid tag write (%d)\n", pmlen, errno);
272*7470b5afSSamuel Holland 
273*7470b5afSSamuel Holland 	ret = read(dev_zero, p, sizeof(*p));
274*7470b5afSSamuel Holland 	if (ret >= 0 || errno != EFAULT)
275*7470b5afSSamuel Holland 		return ksft_test_result_fail("PMLEN=%d invalid tag read\n", pmlen);
276*7470b5afSSamuel Holland 
277*7470b5afSSamuel Holland 	if (i != ~pmlen)
278*7470b5afSSamuel Holland 		return ksft_test_result_fail("PMLEN=%d invalid tag value\n", pmlen);
279*7470b5afSSamuel Holland 
280*7470b5afSSamuel Holland 	ksft_test_result_pass("PMLEN=%d tagged address ABI\n", pmlen);
281*7470b5afSSamuel Holland }
282*7470b5afSSamuel Holland 
283*7470b5afSSamuel Holland static void test_tagged_addr_abi(void)
284*7470b5afSSamuel Holland {
285*7470b5afSSamuel Holland 	ksft_print_msg("Testing tagged address ABI\n");
286*7470b5afSSamuel Holland 
287*7470b5afSSamuel Holland 	test_tagged_addr_abi_pmlen(0);
288*7470b5afSSamuel Holland 	test_tagged_addr_abi_pmlen(min_pmlen);
289*7470b5afSSamuel Holland 	test_tagged_addr_abi_pmlen(max_pmlen);
290*7470b5afSSamuel Holland }
291*7470b5afSSamuel Holland 
292*7470b5afSSamuel Holland static struct test_info {
293*7470b5afSSamuel Holland 	unsigned int nr_tests;
294*7470b5afSSamuel Holland 	void (*test_fn)(void);
295*7470b5afSSamuel Holland } tests[] = {
296*7470b5afSSamuel Holland 	{ .nr_tests = 17 * 3, test_pmlen },
297*7470b5afSSamuel Holland 	{ .nr_tests = 3, test_dereference },
298*7470b5afSSamuel Holland 	{ .nr_tests = 2, test_fork_exec },
299*7470b5afSSamuel Holland 	{ .nr_tests = 2, test_tagged_addr_abi_sysctl },
300*7470b5afSSamuel Holland 	{ .nr_tests = 3, test_tagged_addr_abi },
301*7470b5afSSamuel Holland };
302*7470b5afSSamuel Holland 
303*7470b5afSSamuel Holland int main(int argc, char **argv)
304*7470b5afSSamuel Holland {
305*7470b5afSSamuel Holland 	unsigned int plan = 0;
306*7470b5afSSamuel Holland 	int ret;
307*7470b5afSSamuel Holland 
308*7470b5afSSamuel Holland 	/* Check if this is the child process after execve(). */
309*7470b5afSSamuel Holland 	if (!argv[0][0])
310*7470b5afSSamuel Holland 		return execve_child();
311*7470b5afSSamuel Holland 
312*7470b5afSSamuel Holland 	dev_zero = open("/dev/zero", O_RDWR);
313*7470b5afSSamuel Holland 	if (dev_zero < 0)
314*7470b5afSSamuel Holland 		return 1;
315*7470b5afSSamuel Holland 
316*7470b5afSSamuel Holland 	/* Write to a pipe so the kernel must dereference the buffer pointer. */
317*7470b5afSSamuel Holland 	ret = pipe(pipefd);
318*7470b5afSSamuel Holland 	if (ret)
319*7470b5afSSamuel Holland 		return 1;
320*7470b5afSSamuel Holland 
321*7470b5afSSamuel Holland 	ksft_print_header();
322*7470b5afSSamuel Holland 
323*7470b5afSSamuel Holland 	for (int i = 0; i < ARRAY_SIZE(tests); i++)
324*7470b5afSSamuel Holland 		plan += tests[i].nr_tests;
325*7470b5afSSamuel Holland 
326*7470b5afSSamuel Holland 	ksft_set_plan(plan);
327*7470b5afSSamuel Holland 
328*7470b5afSSamuel Holland 	for (int i = 0; i < ARRAY_SIZE(tests); i++)
329*7470b5afSSamuel Holland 		tests[i].test_fn();
330*7470b5afSSamuel Holland 
331*7470b5afSSamuel Holland 	ksft_finished();
332*7470b5afSSamuel Holland }
333