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
sigsegv_handler(int sig)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
valid_pmlen(int pmlen)35 static inline bool valid_pmlen(int pmlen)
36 {
37 return pmlen == 0 || pmlen == 7 || pmlen == 16;
38 }
39
test_pmlen(void)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
set_tagged_addr_ctrl(int pmlen,bool tagged_addr_abi)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
test_dereference_pmlen(int pmlen)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
test_dereference(void)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
execve_child_sigsegv_handler(int sig)138 static void execve_child_sigsegv_handler(int sig)
139 {
140 exit(42);
141 }
142
execve_child(void)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
test_fork_exec(void)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
pwrite_wrapper(int fd,void * buf,size_t count,const char * msg)188 static 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
test_tagged_addr_abi_sysctl(void)199 static 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
test_tagged_addr_abi_pmlen(int pmlen)233 static 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
test_tagged_addr_abi(void)299 static 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
308 static 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
main(int argc,char ** argv)319 int 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 }
349