File Index
Files and directories
Sign in
FILE

pointer_masking.c

File preview with syntax highlighting.

linux-6.19.14/tools/testing/selftests/riscv/abi/pointer_masking.c C
  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
 21static int dev_zero;
 22
 23static int pipefd[2];
 24
 25static sigjmp_buf jmpbuf;
 26
 27static void sigsegv_handler(int sig)
 28{
 29	siglongjmp(jmpbuf, 1);
 30}
 31
 32static int min_pmlen;
 33static int max_pmlen;
 34
 35static inline bool valid_pmlen(int pmlen)
 36{
 37	return pmlen == 0 || pmlen == 7 || pmlen == 16;
 38}
 39
 40static 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
 67pr_set_error:
 68		ksft_test_result_skip("PMLEN=%d PR_GET_TAGGED_ADDR_CTRL\n", request);
 69pr_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
 78static 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
 93static 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
125static 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
138static void execve_child_sigsegv_handler(int sig)
139{
140	exit(42);
141}
142
143static 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
154static 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
188static bool pwrite_wrapper(int fd, void *buf, size_t count, const char *msg)
189{
190	int ret = pwrite(fd, buf, count, 0);
191
192	if (ret != count) {
193		ksft_perror(msg);
194		return false;
195	}
196	return true;
197}
198
199static void test_tagged_addr_abi_sysctl(void)
200{
201	char *err_pwrite_msg = "failed to write to /proc/sys/abi/tagged_addr_disabled\n";
202	char value;
203	int fd;
204
205	ksft_print_msg("Testing tagged address ABI sysctl\n");
206
207	fd = open("/proc/sys/abi/tagged_addr_disabled", O_WRONLY);
208	if (fd < 0) {
209		ksft_test_result_skip("failed to open sysctl file\n");
210		ksft_test_result_skip("failed to open sysctl file\n");
211		return;
212	}
213
214	value = '1';
215	if (!pwrite_wrapper(fd, &value, 1, "write '1'"))
216		ksft_test_result_fail(err_pwrite_msg);
217	else
218		ksft_test_result(set_tagged_addr_ctrl(min_pmlen, true) == -EINVAL,
219				 "sysctl disabled\n");
220
221	value = '0';
222	if (!pwrite_wrapper(fd, &value, 1, "write '0'"))
223		ksft_test_result_fail(err_pwrite_msg);
224	else
225		ksft_test_result(set_tagged_addr_ctrl(min_pmlen, true) == 0,
226				 "sysctl enabled\n");
227
228	set_tagged_addr_ctrl(0, false);
229
230	close(fd);
231}
232
233static void test_tagged_addr_abi_pmlen(int pmlen)
234{
235	int i, *p, ret;
236
237	i = ~pmlen;
238
239	if (pmlen) {
240		p = (int *)((uintptr_t)&i | 1UL << (__riscv_xlen - pmlen));
241
242		ret = set_tagged_addr_ctrl(pmlen, false);
243		if (ret)
244			return ksft_test_result_error("PMLEN=%d ABI disabled setup (%d)\n",
245						      pmlen, ret);
246
247		ret = write(pipefd[1], p, sizeof(*p));
248		if (ret >= 0 || errno != EFAULT)
249			return ksft_test_result_fail("PMLEN=%d ABI disabled write\n", pmlen);
250
251		ret = read(dev_zero, p, sizeof(*p));
252		if (ret >= 0 || errno != EFAULT)
253			return ksft_test_result_fail("PMLEN=%d ABI disabled read\n", pmlen);
254
255		if (i != ~pmlen)
256			return ksft_test_result_fail("PMLEN=%d ABI disabled value\n", pmlen);
257
258		ret = set_tagged_addr_ctrl(pmlen, true);
259		if (ret)
260			return ksft_test_result_error("PMLEN=%d ABI enabled setup (%d)\n",
261						      pmlen, ret);
262
263		ret = write(pipefd[1], p, sizeof(*p));
264		if (ret != sizeof(*p))
265			return ksft_test_result_fail("PMLEN=%d ABI enabled write\n", pmlen);
266
267		ret = read(dev_zero, p, sizeof(*p));
268		if (ret != sizeof(*p))
269			return ksft_test_result_fail("PMLEN=%d ABI enabled read\n", pmlen);
270
271		if (i)
272			return ksft_test_result_fail("PMLEN=%d ABI enabled value\n", pmlen);
273
274		i = ~pmlen;
275	} else {
276		/* The tagged address ABI cannot be enabled when PMLEN == 0. */
277		ret = set_tagged_addr_ctrl(pmlen, true);
278		if (ret != -EINVAL)
279			return ksft_test_result_error("PMLEN=%d ABI setup (%d)\n",
280						      pmlen, ret);
281	}
282
283	p = (int *)((uintptr_t)&i | 1UL << (__riscv_xlen - pmlen - 1));
284
285	ret = write(pipefd[1], p, sizeof(*p));
286	if (ret >= 0 || errno != EFAULT)
287		return ksft_test_result_fail("PMLEN=%d invalid tag write (%d)\n", pmlen, errno);
288
289	ret = read(dev_zero, p, sizeof(*p));
290	if (ret >= 0 || errno != EFAULT)
291		return ksft_test_result_fail("PMLEN=%d invalid tag read\n", pmlen);
292
293	if (i != ~pmlen)
294		return ksft_test_result_fail("PMLEN=%d invalid tag value\n", pmlen);
295
296	ksft_test_result_pass("PMLEN=%d tagged address ABI\n", pmlen);
297}
298
299static void test_tagged_addr_abi(void)
300{
301	ksft_print_msg("Testing tagged address ABI\n");
302
303	test_tagged_addr_abi_pmlen(0);
304	test_tagged_addr_abi_pmlen(min_pmlen);
305	test_tagged_addr_abi_pmlen(max_pmlen);
306}
307
308static struct test_info {
309	unsigned int nr_tests;
310	void (*test_fn)(void);
311} tests[] = {
312	{ .nr_tests = 17 * 3, test_pmlen },
313	{ .nr_tests = 3, test_dereference },
314	{ .nr_tests = 2, test_fork_exec },
315	{ .nr_tests = 2, test_tagged_addr_abi_sysctl },
316	{ .nr_tests = 3, test_tagged_addr_abi },
317};
318
319int main(int argc, char **argv)
320{
321	unsigned int plan = 0;
322	int ret;
323
324	/* Check if this is the child process after execve(). */
325	if (!argv[0][0])
326		return execve_child();
327
328	dev_zero = open("/dev/zero", O_RDWR);
329	if (dev_zero < 0)
330		return 1;
331
332	/* Write to a pipe so the kernel must dereference the buffer pointer. */
333	ret = pipe(pipefd);
334	if (ret)
335		return 1;
336
337	ksft_print_header();
338
339	for (int i = 0; i < ARRAY_SIZE(tests); i++)
340		plan += tests[i].nr_tests;
341
342	ksft_set_plan(plan);
343
344	for (int i = 0; i < ARRAY_SIZE(tests); i++)
345		tests[i].test_fn();
346
347	ksft_finished();
348}
~/ Read-only file preview.