1 /*
2 * Check decoding of SECCOMP_IOCTL_* commands of ioctl syscall.
3 *
4 * Copyright (c) 2021 Eugene Syromyatnikov <evgsyr@gmail.com>.
5 * Copyright (c) 2021-2022 The strace developers.
6 * All rights reserved.
7 *
8 * SPDX-License-Identifier: GPL-2.0-or-later
9 */
10
11 #include "tests.h"
12
13 #include "pidns.h"
14 #include "scno.h"
15
16 #include <errno.h>
17 #include <fcntl.h>
18 #include <inttypes.h>
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <unistd.h>
23
24 #include "kernel_fcntl.h"
25
26 #include <linux/ioctl.h>
27 #include <linux/seccomp.h>
28
29 #include "cur_audit_arch.h"
30
31 #include "xlat.h"
32 #define XLAT_MACROS_ONLY
33 # include "xlat/elf_em.h"
34 #undef XLAT_MACROS_ONLY
35 #include "xlat/audit_arch.h"
36
37 #ifndef INJECT_RETVAL
38 # define INJECT_RETVAL 0
39 #endif
40 #ifndef PRINT_PATHS
41 # define PRINT_PATHS 0
42 #endif
43
44 #if INJECT_RETVAL
45 # define INJ_STR " (INJECTED)"
46 #else
47 # define INJ_STR ""
48 #endif
49
50 static const char null_path[] = "/dev/null";
51 static const char zero_path[] = "/dev/zero";
52
53 #define NULL_FD 0
54 #define ZERO_FD 42
55
56 #if PRINT_PATHS
57 # define PATH_FMT "<%s>"
58 #else
59 # define PATH_FMT "%s"
60 #endif
61
62 /**
63 * Generate an ioctl command with a different direction based
64 * on the existing one.
65 */
66 #define IOC_ANOTHER_DIR(nr_, dir_) \
67 _IOC(dir_, _IOC_TYPE(nr_), _IOC_NR(nr_), _IOC_SIZE(nr_))
68
69 static const char *errstr;
70
71 static long
72 sys_ioctl(kernel_long_t fd, kernel_ulong_t cmd, kernel_ulong_t arg)
73 {
74 const long rc = syscall(__NR_ioctl, fd, cmd, arg);
75 errstr = sprintrc(rc);
76 return rc;
77 }
78
79 int
80 main(int argc, char **argv)
81 {
82 static const struct {
83 uint32_t val;
84 const char *str;
85 } dirs[] = {
86 { ARG_STR(_IOC_NONE) },
87 { ARG_STR(_IOC_READ) },
88 { ARG_STR(_IOC_WRITE) },
89 { ARG_STR(_IOC_READ|_IOC_WRITE) },
90 };
91 static const kernel_ulong_t magic =
92 (kernel_ulong_t) 0xdeadbeefbadc0dedULL;
93 long rc;
94
95 PIDNS_TEST_INIT;
96
97 /*
98 * Start of output marker. printf is in front of ioctl() here because
99 * musl calls an ioctl before the first output to stdout, specifically,
100 * ioctl(TIOCGWINSZ) in src/stdio/__stdout_write.c:__stdout_write.
101 */
102 pidns_print_leader();
103 errno = EBADF;
104 printf("ioctl(-1, " XLAT_FMT ", NULL) = -1 EBADF (%m)\n",
105 XLAT_ARGS_U(SECCOMP_IOCTL_NOTIF_RECV));
106 fflush(NULL);
107 sys_ioctl(-1, SECCOMP_IOCTL_NOTIF_RECV, 0);
108
109
110 #if INJECT_RETVAL
111 if (argc == 1)
112 return 0;
113
114 if (argc < 3)
115 error_msg_and_fail("Usage: %s NUM_SKIP INJECT_RETVAL", argv[0]);
116
117 unsigned long num_skip = strtoul(argv[1], NULL, 0);
118 long inject_retval = strtol(argv[2], NULL, 0);
119 bool locked = false;
120
121 if (inject_retval < 0)
122 error_msg_and_fail("Expected non-negative INJECT_RETVAL, "
123 "but got %ld", inject_retval);
124
125 for (unsigned long i = 0; i < num_skip; i++) {
126 rc = sys_ioctl(-1, SECCOMP_IOCTL_NOTIF_RECV, 0);
127 pidns_print_leader();
128 printf("ioctl(-1, " XLAT_FMT ", NULL) = %s%s\n",
129 XLAT_ARGS_U(SECCOMP_IOCTL_NOTIF_RECV),
130 errstr, rc == inject_retval ? " (INJECTED)" : "");
131
132 if (rc != inject_retval)
133 continue;
134
135 locked = true;
136 break;
137 }
138
139 if (!locked) {
140 error_msg_and_fail("Have not locked on ioctl(-1"
141 ", SECCOMP_IOCTL_NOTIF_RECV, NULL) "
142 "returning %lu", inject_retval);
143 }
144 #endif /* INJECT_RETVAL */
145
146 /* Unknown seccomp ioctl */
147 for (size_t i = 0; i < ARRAY_SIZE(dirs); i++) {
148 for (unsigned int j = 0; j < 32; j += 4) {
149 sys_ioctl(-1, _IOC(dirs[i].val, '!', 4, j), magic);
150 pidns_print_leader();
151 printf("ioctl(-1, "
152 XLAT_KNOWN(%#x, "_IOC(%s, 0x21, 0x4, %#x)")
153 ", %#lx) = %s" INJ_STR "\n",
154 #if XLAT_RAW || XLAT_VERBOSE
155 (unsigned int) _IOC(dirs[i].val, '!', 4, j),
156 #endif
157 #if !XLAT_RAW
158 dirs[i].str, j,
159 #endif
160 (unsigned long) magic, errstr);
161 }
162 }
163
164
165 /* SECCOMP_IOCTL_NOTIF_RECV */
166 TAIL_ALLOC_OBJECT_CONST_PTR(struct seccomp_notif, notif);
167
168 sys_ioctl(-1, SECCOMP_IOCTL_NOTIF_RECV, 0);
169 pidns_print_leader();
170 printf("ioctl(-1, " XLAT_FMT ", NULL) = %s" INJ_STR "\n",
171 XLAT_ARGS_U(SECCOMP_IOCTL_NOTIF_RECV), errstr);
172
173 sys_ioctl(-1, SECCOMP_IOCTL_NOTIF_RECV, (uintptr_t) notif + 1);
174 pidns_print_leader();
175 printf("ioctl(-1, " XLAT_FMT ", %p) = %s" INJ_STR "\n",
176 XLAT_ARGS_U(SECCOMP_IOCTL_NOTIF_RECV),
177 (char *) notif + 1, errstr);
178
179 memset(notif, 0, sizeof(*notif));
180 rc = sys_ioctl(-1, SECCOMP_IOCTL_NOTIF_RECV, (uintptr_t) notif);
181 pidns_print_leader();
182 printf("ioctl(-1, " XLAT_FMT ", ",
183 XLAT_ARGS_U(SECCOMP_IOCTL_NOTIF_RECV));
184 if (rc >= 0) {
185 printf("{id=0, pid=0, flags=0, data={nr=0, arch="
186 XLAT_UNKNOWN(0, "AUDIT_ARCH_???")
187 ", instruction_pointer=NULL, args=[0, 0, 0, 0, 0, 0]}}");
188 } else {
189 printf("%p", notif);
190 }
191 printf(") = %s" INJ_STR "\n", errstr);
192
193 notif->id = 0xdeadc0debadc0dedULL;
194 notif->pid = getpid();
195 notif->flags = 0xdeefaced;
196 notif->data.nr = 0xbad5ca11;
197 notif->data.arch = 0xfeedface;
198 notif->data.instruction_pointer = (uintptr_t) sys_ioctl;
199 for (size_t i = 0; i < ARRAY_SIZE(notif->data.args); i++)
200 notif->data.args[i] = 0xdeadfacebadc0dedULL ^ i;
201 #define ARGS_STR "args=[0xdeadfacebadc0ded, 0xdeadfacebadc0dec" \
202 ", 0xdeadfacebadc0def, 0xdeadfacebadc0dee" \
203 ", 0xdeadfacebadc0de9, 0xdeadfacebadc0de8]" \
204 /* End of ARGS_STR definition */
205 rc = sys_ioctl(-1, SECCOMP_IOCTL_NOTIF_RECV, (uintptr_t) notif);
206 pidns_print_leader();
207 printf("ioctl(-1, " XLAT_FMT ", ",
208 XLAT_ARGS_U(SECCOMP_IOCTL_NOTIF_RECV));
209 for (size_t i = 0; i < 2; i++) {
210 if (i)
211 printf(" => ");
212 if (!i || (rc >= 0)) {
213 printf("{id=0xdeadc0debadc0ded, pid=%d%s"
214 ", flags=0xdeefaced, data={nr=3134573073, arch="
215 XLAT_UNKNOWN(0xfeedface, "AUDIT_ARCH_???")
216 ", instruction_pointer=%p, " ARGS_STR "}}",
217 getpid(), pidns_pid2str(PT_TGID), sys_ioctl);
218 } else {
219 printf("%p", notif);
220 }
221 }
222 printf(") = %s" INJ_STR "\n", errstr);
223
224 #ifdef CUR_AUDIT_ARCH
225 notif->id = 0;
226 notif->flags = 0;
227 notif->data.nr = __NR_gettid;
228 notif->data.arch = CUR_AUDIT_ARCH;
229 notif->data.instruction_pointer = 0;
230 rc = sys_ioctl(-1, SECCOMP_IOCTL_NOTIF_RECV, (uintptr_t) notif);
231 pidns_print_leader();
232 printf("ioctl(-1, " XLAT_FMT ", ",
233 XLAT_ARGS_U(SECCOMP_IOCTL_NOTIF_RECV));
234 for (size_t i = 0; i < 2; i++) {
235 if (i)
236 printf(" => ");
237 if (!i || (rc >= 0)) {
238 printf("{id=0, pid=%d%s, flags=0, data={nr=" XLAT_FMT_U
239 ", arch=%s, instruction_pointer=NULL, " ARGS_STR
240 "}}",
241 getpid(), pidns_pid2str(PT_TGID),
242 XLAT_ARGS(__NR_gettid),
243 sprintxval(audit_arch, CUR_AUDIT_ARCH,
244 "AUDIT_ARCH_???"));
245 } else {
246 printf("%p", notif);
247 }
248 }
249 printf(") = %s" INJ_STR "\n", errstr);
250 # if defined(PERS0_AUDIT_ARCH)
251 notif->data.nr = PERS0__NR_gettid;
252 notif->data.arch = PERS0_AUDIT_ARCH;
253 rc = sys_ioctl(-1, SECCOMP_IOCTL_NOTIF_RECV, (uintptr_t) notif);
254 pidns_print_leader();
255 printf("ioctl(-1, " XLAT_FMT ", ",
256 XLAT_ARGS_U(SECCOMP_IOCTL_NOTIF_RECV));
257 for (size_t i = 0; i < 2; i++) {
258 if (i)
259 printf(" => ");
260 if (!i || (rc >= 0)) {
261 printf("{id=0, pid=%d%s, flags=0, data={nr=%u"
262 NRAW(" /* gettid */") ", arch=%s"
263 ", instruction_pointer=NULL, " ARGS_STR "}}",
264 getpid(), pidns_pid2str(PT_TGID),
265 PERS0__NR_gettid,
266 sprintxval(audit_arch, PERS0_AUDIT_ARCH,
267 "AUDIT_ARCH_???"));
268 } else {
269 printf("%p", notif);
270 }
271 }
272 printf(") = %s" INJ_STR "\n", errstr);
273 # endif
274 # if defined(M32_AUDIT_ARCH)
275 notif->data.nr = M32__NR_gettid;
276 notif->data.arch = M32_AUDIT_ARCH;
277 rc = sys_ioctl(-1, SECCOMP_IOCTL_NOTIF_RECV, (uintptr_t) notif);
278 pidns_print_leader();
279 printf("ioctl(-1, " XLAT_FMT ", ",
280 XLAT_ARGS_U(SECCOMP_IOCTL_NOTIF_RECV));
281 for (size_t i = 0; i < 2; i++) {
282 if (i)
283 printf(" => ");
284 if (!i || (rc >= 0)) {
285 printf("{id=0, pid=%d%s, flags=0, data={nr=%u"
286 NRAW(" /* gettid */") ", arch=%s"
287 ", instruction_pointer=NULL, " ARGS_STR "}}",
288 getpid(), pidns_pid2str(PT_TGID), M32__NR_gettid,
289 sprintxval(audit_arch, M32_AUDIT_ARCH,
290 "AUDIT_ARCH_???"));
291 } else {
292 printf("%p", notif);
293 }
294 }
295 printf(") = %s" INJ_STR "\n", errstr);
296 # endif
297 # if defined(MX32_AUDIT_ARCH)
298 notif->data.nr = MX32__NR_gettid;
299 notif->data.arch = MX32_AUDIT_ARCH;
300 rc = sys_ioctl(-1, SECCOMP_IOCTL_NOTIF_RECV, (uintptr_t) notif);
301 pidns_print_leader();
302 printf("ioctl(-1, " XLAT_FMT ", ",
303 XLAT_ARGS_U(SECCOMP_IOCTL_NOTIF_RECV));
304 for (size_t i = 0; i < 2; i++) {
305 if (i)
306 printf(" => ");
307 if (!i || (rc >= 0)) {
308 printf("{id=0, pid=%d%s, flags=0, data={nr=%u"
309 NRAW(" /* gettid */") ", arch=%s"
310 ", instruction_pointer=NULL, " ARGS_STR "}}",
311 getpid(), pidns_pid2str(PT_TGID),
312 MX32__NR_gettid,
313 sprintxval(audit_arch, MX32_AUDIT_ARCH,
314 "AUDIT_ARCH_???"));
315 } else {
316 printf("%p", notif);
317 }
318 }
319 printf(") = %s" INJ_STR "\n", errstr);
320 # endif
321 #endif /* CUR_AUDIT_ARCH */
322
323
324 /* SECCOMP_IOCTL_NOTIF_SEND */
325 TAIL_ALLOC_OBJECT_CONST_PTR(struct seccomp_notif_resp, resp);
326
327 sys_ioctl(-1, SECCOMP_IOCTL_NOTIF_SEND, 0);
328 pidns_print_leader();
329 printf("ioctl(-1, " XLAT_FMT ", NULL) = %s" INJ_STR "\n",
330 XLAT_ARGS_U(SECCOMP_IOCTL_NOTIF_SEND), errstr);
331
332 sys_ioctl(-1, SECCOMP_IOCTL_NOTIF_SEND, (uintptr_t) resp + 1);
333 pidns_print_leader();
334 printf("ioctl(-1, " XLAT_FMT ", %p) = %s" INJ_STR "\n",
335 XLAT_ARGS_U(SECCOMP_IOCTL_NOTIF_SEND),
336 (char *) resp + 1, errstr);
337
338 memset(resp, 0, sizeof(*resp));
339 rc = sys_ioctl(-1, SECCOMP_IOCTL_NOTIF_SEND, (uintptr_t) resp);
340 pidns_print_leader();
341 printf("ioctl(-1, " XLAT_FMT ", {id=0, val=0, error=0, flags=0}) = %s"
342 INJ_STR "\n", XLAT_ARGS_U(SECCOMP_IOCTL_NOTIF_SEND), errstr);
343
344 resp->id = 0xdeadc0debadc0dedULL;
345 resp->val = 0xdadfacedbeefdeedULL;
346 resp->error = 0xbadc0ded;
347 resp->flags = 0xfacecafe;
348 rc = sys_ioctl(-1, SECCOMP_IOCTL_NOTIF_SEND, (uintptr_t) resp);
349 pidns_print_leader();
350 printf("ioctl(-1, " XLAT_FMT ", {id=0xdeadc0debadc0ded"
351 ", val=-2675229516524167443, error=-1159983635, flags=0xfacecafe"
352 NRAW(" /* SECCOMP_USER_NOTIF_FLAG_??? */") "}) = %s"
353 INJ_STR "\n", XLAT_ARGS_U(SECCOMP_IOCTL_NOTIF_SEND), errstr);
354
355 resp->error = -ENOSR;
356 resp->flags = 1;
357 rc = sys_ioctl(-1, SECCOMP_IOCTL_NOTIF_SEND, (uintptr_t) resp);
358 pidns_print_leader();
359 printf("ioctl(-1, " XLAT_FMT ", {id=0xdeadc0debadc0ded"
360 ", val=-2675229516524167443, error=" XLAT_FMT_D
361 ", flags=" XLAT_KNOWN(0x1, "SECCOMP_USER_NOTIF_FLAG_CONTINUE")
362 "}) = %s" INJ_STR "\n",
363 XLAT_ARGS_U(SECCOMP_IOCTL_NOTIF_SEND),
364 XLAT_SEL(-ENOSR, "-ENOSR"), errstr);
365
366
367 /* SECCOMP_IOCTL_NOTIF_ID_VALID */
368 static const struct {
369 uint32_t id;
370 const char *str;
371 } id_valid_cmds[] = {
372 { ARG_STR(SECCOMP_IOCTL_NOTIF_ID_VALID) },
373 { IOC_ANOTHER_DIR(SECCOMP_IOCTL_NOTIF_ID_VALID, _IOC_READ),
374 "SECCOMP_IOCTL_NOTIF_ID_VALID_WRONG_DIR" },
375 };
376 TAIL_ALLOC_OBJECT_CONST_PTR(uint64_t, id);
377
378 for (size_t i = 0; i < ARRAY_SIZE(id_valid_cmds); i++) {
379
380 sys_ioctl(-1, id_valid_cmds[i].id, 0);
381 pidns_print_leader();
382 printf("ioctl(-1, " XLAT_FMT ", NULL) = %s" INJ_STR "\n",
383 XLAT_SEL(id_valid_cmds[i].id, id_valid_cmds[i].str),
384 errstr);
385
386 sys_ioctl(-1, id_valid_cmds[i].id, (uintptr_t) id + 1);
387 pidns_print_leader();
388 printf("ioctl(-1, " XLAT_FMT ", %p) = %s" INJ_STR "\n",
389 XLAT_SEL(id_valid_cmds[i].id, id_valid_cmds[i].str),
390 (char *) id + 1, errstr);
391
392 memset(id, 0, sizeof(*id));
393 rc = sys_ioctl(-1, id_valid_cmds[i].id, (uintptr_t) id);
394 pidns_print_leader();
395 printf("ioctl(-1, " XLAT_FMT ", [0]) = %s" INJ_STR "\n",
396 XLAT_SEL(id_valid_cmds[i].id, id_valid_cmds[i].str),
397 errstr);
398
399 *id = 0xdecaffedfacefeedULL;
400 rc = sys_ioctl(-1, id_valid_cmds[i].id, (uintptr_t) id);
401 pidns_print_leader();
402 printf("ioctl(-1, " XLAT_FMT ", [0xdecaffedfacefeed]) = %s"
403 INJ_STR "\n",
404 XLAT_SEL(id_valid_cmds[i].id, id_valid_cmds[i].str),
405 errstr);
406 }
407
408
409 /* SECCOMP_IOCTL_NOTIF_ADDFD */
410 TAIL_ALLOC_OBJECT_CONST_PTR(struct seccomp_notif_addfd, addfd);
411
412 close(0);
413 int fd = open(null_path, O_RDONLY);
414 if (fd < 0)
415 perror_msg_and_fail("open(\"%s\")", null_path);
416 if (fd != NULL_FD) {
417 if (dup2(fd, NULL_FD) < 0)
418 perror_msg_and_fail("dup2(fd, NULL_FD)");
419 close(fd);
420 }
421
422 fd = open(zero_path, O_RDONLY);
423 if (fd < 0)
424 perror_msg_and_fail("open(\"%s\")", zero_path);
425 if (fd != ZERO_FD) {
426 if (dup2(fd, ZERO_FD) < 0)
427 perror_msg_and_fail("dup2(fd, ZERO_FD)");
428 close(fd);
429 }
430
431 sys_ioctl(-1, SECCOMP_IOCTL_NOTIF_ADDFD, 0);
432 pidns_print_leader();
433 printf("ioctl(-1, " XLAT_FMT ", NULL) = %s" INJ_STR "\n",
434 XLAT_ARGS_U(SECCOMP_IOCTL_NOTIF_ADDFD), errstr);
435
436 sys_ioctl(-1, SECCOMP_IOCTL_NOTIF_ADDFD, (uintptr_t) addfd + 1);
437 pidns_print_leader();
438 printf("ioctl(-1, " XLAT_FMT ", %p) = %s" INJ_STR "\n",
439 XLAT_ARGS_U(SECCOMP_IOCTL_NOTIF_ADDFD),
440 (char *) addfd + 1, errstr);
441
442 memset(addfd, 0, sizeof(*addfd));
443 rc = sys_ioctl(-1, SECCOMP_IOCTL_NOTIF_ADDFD, (uintptr_t) addfd);
444 pidns_print_leader();
445 printf("ioctl(-1, " XLAT_FMT ", {id=0, flags=0, srcfd=0" PATH_FMT
446 ", newfd=0, newfd_flags=0}) = %s" INJ_STR "\n",
447 XLAT_ARGS_U(SECCOMP_IOCTL_NOTIF_ADDFD),
448 PRINT_PATHS ? null_path : "", errstr);
449
450 addfd->id = 0xdeadc0debadfacedULL;
451 addfd->flags = 0xbadc0dec;
452 addfd->srcfd = 0xdeadface;
453 addfd->newfd = 0xbeeffeed;
454 addfd->newfd_flags = O_CLOEXEC|O_DSYNC;
455 rc = sys_ioctl(-1, SECCOMP_IOCTL_NOTIF_ADDFD, (uintptr_t) addfd);
456 pidns_print_leader();
457 printf("ioctl(-1, " XLAT_FMT ", {id=0xdeadc0debadfaced"
458 ", flags=0xbadc0dec" NRAW(" /* SECCOMP_ADDFD_FLAG_??? */")
459 ", srcfd=-559023410, newfd=-1091567891, newfd_flags=" XLAT_FMT
460 "}) = %s" INJ_STR "\n",
461 XLAT_ARGS_U(SECCOMP_IOCTL_NOTIF_ADDFD),
462 XLAT_ARGS(O_DSYNC|O_CLOEXEC), errstr);
463
464 addfd->flags = 3;
465 addfd->srcfd = ZERO_FD;
466 addfd->newfd = 0xbeeffeed;
467 addfd->newfd_flags = O_DIRECT;
468 rc = sys_ioctl(-1, SECCOMP_IOCTL_NOTIF_ADDFD, (uintptr_t) addfd);
469 pidns_print_leader();
470 printf("ioctl(-1, " XLAT_FMT ", {id=0xdeadc0debadfaced"
471 ", flags=" XLAT_KNOWN(0x3, "SECCOMP_ADDFD_FLAG_SETFD"
472 "|SECCOMP_ADDFD_FLAG_SEND")
473 ", srcfd=%d" PATH_FMT ", newfd=-1091567891, newfd_flags="
474 XLAT_FMT "}) = %s" INJ_STR "\n",
475 XLAT_ARGS_U(SECCOMP_IOCTL_NOTIF_ADDFD),
476 ZERO_FD, PRINT_PATHS ? zero_path : "",
477 XLAT_ARGS(O_DIRECT), errstr);
478
479 pidns_print_leader();
480 puts("+++ exited with 0 +++");
481 return 0;
482 }