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