/* * Check decoding of SECCOMP_IOCTL_* commands of ioctl syscall. * * Copyright (c) 2021 Eugene Syromyatnikov . * All rights reserved. * * SPDX-License-Identifier: GPL-2.0-or-later */ #include "tests.h" #include "pidns.h" #include "scno.h" #include #include #include #include #include #include #include #include "kernel_fcntl.h" #include #include #include "cur_audit_arch.h" #include "xlat.h" #define XLAT_MACROS_ONLY # include "xlat/elf_em.h" #undef XLAT_MACROS_ONLY #include "xlat/audit_arch.h" #ifndef INJECT_RETVAL # define INJECT_RETVAL 0 #endif #ifndef PRINT_PATHS # define PRINT_PATHS 0 #endif #if INJECT_RETVAL # define INJ_STR " (INJECTED)" #else # define INJ_STR "" #endif static const char null_path[] = "/dev/null"; static const char zero_path[] = "/dev/zero"; #define NULL_FD 0 #define ZERO_FD 42 #if PRINT_PATHS # define PATH_FMT "<%s>" #else # define PATH_FMT "%s" #endif /* A hack for handling different types of _IOC() on various platforms */ #if XLAT_RAW # define XLAT_ARGS_U(a_) (unsigned int) (a_) #elif XLAT_VERBOSE # define XLAT_ARGS_U(a_) (unsigned int) (a_), #a_ #else # define XLAT_ARGS_U(a_) #a_ #endif /** * Generate an ioctl command with a different direction based * on the existing one. */ #define IOC_ANOTHER_DIR(nr_, dir_) \ _IOC(dir_, _IOC_TYPE(nr_), _IOC_NR(nr_), _IOC_SIZE(nr_)) static const char *errstr; static long sys_ioctl(kernel_long_t fd, kernel_ulong_t cmd, kernel_ulong_t arg) { const long rc = syscall(__NR_ioctl, fd, cmd, arg); errstr = sprintrc(rc); return rc; } int main(int argc, char **argv) { static const struct { uint32_t val; const char *str; } dirs[] = { { ARG_STR(_IOC_NONE) }, { ARG_STR(_IOC_READ) }, { ARG_STR(_IOC_WRITE) }, { ARG_STR(_IOC_READ|_IOC_WRITE) }, }; static const kernel_ulong_t magic = (kernel_ulong_t) 0xdeadbeefbadc0dedULL; long rc; PIDNS_TEST_INIT; /* * Start of output marker. printf is in front of ioctl() here because * musl calls an ioctl before the first output to stdout, specifically, * ioctl(TIOCGWINSZ) in src/stdio/__stdout_write.c:__stdout_write. */ pidns_print_leader(); errno = EBADF; printf("ioctl(-1, " XLAT_FMT ", NULL) = -1 EBADF (%m)\n", XLAT_ARGS_U(SECCOMP_IOCTL_NOTIF_RECV)); fflush(NULL); sys_ioctl(-1, SECCOMP_IOCTL_NOTIF_RECV, 0); #if INJECT_RETVAL if (argc == 1) return 0; if (argc < 3) error_msg_and_fail("Usage: %s NUM_SKIP INJECT_RETVAL", argv[0]); unsigned long num_skip = strtoul(argv[1], NULL, 0); long inject_retval = strtol(argv[2], NULL, 0); bool locked = false; if (inject_retval < 0) error_msg_and_fail("Expected non-negative INJECT_RETVAL, " "but got %ld", inject_retval); for (unsigned long i = 0; i < num_skip; i++) { rc = sys_ioctl(-1, SECCOMP_IOCTL_NOTIF_RECV, 0); pidns_print_leader(); printf("ioctl(-1, " XLAT_FMT ", NULL) = %s%s\n", XLAT_ARGS_U(SECCOMP_IOCTL_NOTIF_RECV), errstr, rc == inject_retval ? " (INJECTED)" : ""); if (rc != inject_retval) continue; locked = true; break; } if (!locked) { error_msg_and_fail("Have not locked on ioctl(-1" ", SECCOMP_IOCTL_NOTIF_RECV, NULL) " "returning %lu", inject_retval); } #endif /* INJECT_RETVAL */ /* Unknown seccomp ioctl */ for (size_t i = 0; i < ARRAY_SIZE(dirs); i++) { for (unsigned int j = 0; j < 32; j += 4) { sys_ioctl(-1, _IOC(dirs[i].val, '!', 4, j), magic); pidns_print_leader(); printf("ioctl(-1, " XLAT_KNOWN(%#x, "_IOC(%s, 0x21, 0x4, %#x)") ", %#lx) = %s" INJ_STR "\n", #if XLAT_RAW || XLAT_VERBOSE (unsigned int) _IOC(dirs[i].val, '!', 4, j), #endif #if !XLAT_RAW dirs[i].str, j, #endif (unsigned long) magic, errstr); } } /* SECCOMP_IOCTL_NOTIF_RECV */ TAIL_ALLOC_OBJECT_CONST_PTR(struct seccomp_notif, notif); sys_ioctl(-1, SECCOMP_IOCTL_NOTIF_RECV, 0); pidns_print_leader(); printf("ioctl(-1, " XLAT_FMT ", NULL) = %s" INJ_STR "\n", XLAT_ARGS_U(SECCOMP_IOCTL_NOTIF_RECV), errstr); sys_ioctl(-1, SECCOMP_IOCTL_NOTIF_RECV, (uintptr_t) notif + 1); pidns_print_leader(); printf("ioctl(-1, " XLAT_FMT ", %p) = %s" INJ_STR "\n", XLAT_ARGS_U(SECCOMP_IOCTL_NOTIF_RECV), (char *) notif + 1, errstr); memset(notif, 0, sizeof(*notif)); rc = sys_ioctl(-1, SECCOMP_IOCTL_NOTIF_RECV, (uintptr_t) notif); pidns_print_leader(); printf("ioctl(-1, " XLAT_FMT ", ", XLAT_ARGS_U(SECCOMP_IOCTL_NOTIF_RECV)); if (rc >= 0) { printf("{id=0, pid=0, flags=0, data={nr=0, arch=" XLAT_UNKNOWN(0, "AUDIT_ARCH_???") ", instruction_pointer=NULL, args=[0, 0, 0, 0, 0, 0]}}"); } else { printf("%p", notif); } printf(") = %s" INJ_STR "\n", errstr); notif->id = 0xdeadc0debadc0dedULL; notif->pid = getpid(); notif->flags = 0xdeefaced; notif->data.nr = 0xbad5ca11; notif->data.arch = 0xfeedface; notif->data.instruction_pointer = (uintptr_t) sys_ioctl; for (size_t i = 0; i < ARRAY_SIZE(notif->data.args); i++) notif->data.args[i] = 0xdeadfacebadc0dedULL ^ i; #define ARGS_STR "args=[0xdeadfacebadc0ded, 0xdeadfacebadc0dec" \ ", 0xdeadfacebadc0def, 0xdeadfacebadc0dee" \ ", 0xdeadfacebadc0de9, 0xdeadfacebadc0de8]" \ /* End of ARGS_STR definition */ rc = sys_ioctl(-1, SECCOMP_IOCTL_NOTIF_RECV, (uintptr_t) notif); pidns_print_leader(); printf("ioctl(-1, " XLAT_FMT ", ", XLAT_ARGS_U(SECCOMP_IOCTL_NOTIF_RECV)); for (size_t i = 0; i < 2; i++) { if (i) printf(" => "); if (!i || (rc >= 0)) { printf("{id=0xdeadc0debadc0ded, pid=%d%s" ", flags=0xdeefaced, data={nr=3134573073, arch=" XLAT_UNKNOWN(0xfeedface, "AUDIT_ARCH_???") ", instruction_pointer=%p, " ARGS_STR "}}", getpid(), pidns_pid2str(PT_TGID), sys_ioctl); } else { printf("%p", notif); } } printf(") = %s" INJ_STR "\n", errstr); #ifdef CUR_AUDIT_ARCH notif->id = 0; notif->flags = 0; notif->data.nr = __NR_gettid; notif->data.arch = CUR_AUDIT_ARCH; notif->data.instruction_pointer = 0; rc = sys_ioctl(-1, SECCOMP_IOCTL_NOTIF_RECV, (uintptr_t) notif); pidns_print_leader(); printf("ioctl(-1, " XLAT_FMT ", ", XLAT_ARGS_U(SECCOMP_IOCTL_NOTIF_RECV)); for (size_t i = 0; i < 2; i++) { if (i) printf(" => "); if (!i || (rc >= 0)) { printf("{id=0, pid=%d%s, flags=0, data={nr=" XLAT_FMT_U ", arch=%s, instruction_pointer=NULL, " ARGS_STR "}}", getpid(), pidns_pid2str(PT_TGID), XLAT_ARGS(__NR_gettid), sprintxval(audit_arch, CUR_AUDIT_ARCH, "AUDIT_ARCH_???")); } else { printf("%p", notif); } } printf(") = %s" INJ_STR "\n", errstr); # if defined(PERS0_AUDIT_ARCH) notif->data.nr = PERS0__NR_gettid; notif->data.arch = PERS0_AUDIT_ARCH; rc = sys_ioctl(-1, SECCOMP_IOCTL_NOTIF_RECV, (uintptr_t) notif); pidns_print_leader(); printf("ioctl(-1, " XLAT_FMT ", ", XLAT_ARGS_U(SECCOMP_IOCTL_NOTIF_RECV)); for (size_t i = 0; i < 2; i++) { if (i) printf(" => "); if (!i || (rc >= 0)) { printf("{id=0, pid=%d%s, flags=0, data={nr=%u" NRAW(" /* gettid */") ", arch=%s" ", instruction_pointer=NULL, " ARGS_STR "}}", getpid(), pidns_pid2str(PT_TGID), PERS0__NR_gettid, sprintxval(audit_arch, PERS0_AUDIT_ARCH, "AUDIT_ARCH_???")); } else { printf("%p", notif); } } printf(") = %s" INJ_STR "\n", errstr); # endif # if defined(M32_AUDIT_ARCH) notif->data.nr = M32__NR_gettid; notif->data.arch = M32_AUDIT_ARCH; rc = sys_ioctl(-1, SECCOMP_IOCTL_NOTIF_RECV, (uintptr_t) notif); pidns_print_leader(); printf("ioctl(-1, " XLAT_FMT ", ", XLAT_ARGS_U(SECCOMP_IOCTL_NOTIF_RECV)); for (size_t i = 0; i < 2; i++) { if (i) printf(" => "); if (!i || (rc >= 0)) { printf("{id=0, pid=%d%s, flags=0, data={nr=%u" NRAW(" /* gettid */") ", arch=%s" ", instruction_pointer=NULL, " ARGS_STR "}}", getpid(), pidns_pid2str(PT_TGID), M32__NR_gettid, sprintxval(audit_arch, M32_AUDIT_ARCH, "AUDIT_ARCH_???")); } else { printf("%p", notif); } } printf(") = %s" INJ_STR "\n", errstr); # endif # if defined(MX32_AUDIT_ARCH) notif->data.nr = MX32__NR_gettid; notif->data.arch = MX32_AUDIT_ARCH; rc = sys_ioctl(-1, SECCOMP_IOCTL_NOTIF_RECV, (uintptr_t) notif); pidns_print_leader(); printf("ioctl(-1, " XLAT_FMT ", ", XLAT_ARGS_U(SECCOMP_IOCTL_NOTIF_RECV)); for (size_t i = 0; i < 2; i++) { if (i) printf(" => "); if (!i || (rc >= 0)) { printf("{id=0, pid=%d%s, flags=0, data={nr=%u" NRAW(" /* gettid */") ", arch=%s" ", instruction_pointer=NULL, " ARGS_STR "}}", getpid(), pidns_pid2str(PT_TGID), MX32__NR_gettid, sprintxval(audit_arch, MX32_AUDIT_ARCH, "AUDIT_ARCH_???")); } else { printf("%p", notif); } } printf(") = %s" INJ_STR "\n", errstr); # endif #endif /* CUR_AUDIT_ARCH */ /* SECCOMP_IOCTL_NOTIF_SEND */ TAIL_ALLOC_OBJECT_CONST_PTR(struct seccomp_notif_resp, resp); sys_ioctl(-1, SECCOMP_IOCTL_NOTIF_SEND, 0); pidns_print_leader(); printf("ioctl(-1, " XLAT_FMT ", NULL) = %s" INJ_STR "\n", XLAT_ARGS_U(SECCOMP_IOCTL_NOTIF_SEND), errstr); sys_ioctl(-1, SECCOMP_IOCTL_NOTIF_SEND, (uintptr_t) resp + 1); pidns_print_leader(); printf("ioctl(-1, " XLAT_FMT ", %p) = %s" INJ_STR "\n", XLAT_ARGS_U(SECCOMP_IOCTL_NOTIF_SEND), (char *) resp + 1, errstr); memset(resp, 0, sizeof(*resp)); rc = sys_ioctl(-1, SECCOMP_IOCTL_NOTIF_SEND, (uintptr_t) resp); pidns_print_leader(); printf("ioctl(-1, " XLAT_FMT ", {id=0, val=0, error=0, flags=0}) = %s" INJ_STR "\n", XLAT_ARGS_U(SECCOMP_IOCTL_NOTIF_SEND), errstr); resp->id = 0xdeadc0debadc0dedULL; resp->val = 0xdadfacedbeefdeedULL; resp->error = 0xbadc0ded; resp->flags = 0xfacecafe; rc = sys_ioctl(-1, SECCOMP_IOCTL_NOTIF_SEND, (uintptr_t) resp); pidns_print_leader(); printf("ioctl(-1, " XLAT_FMT ", {id=0xdeadc0debadc0ded" ", val=-2675229516524167443, error=-1159983635, flags=0xfacecafe" NRAW(" /* SECCOMP_USER_NOTIF_FLAG_??? */") "}) = %s" INJ_STR "\n", XLAT_ARGS_U(SECCOMP_IOCTL_NOTIF_SEND), errstr); resp->error = -ENOSR; resp->flags = 1; rc = sys_ioctl(-1, SECCOMP_IOCTL_NOTIF_SEND, (uintptr_t) resp); pidns_print_leader(); printf("ioctl(-1, " XLAT_FMT ", {id=0xdeadc0debadc0ded" ", val=-2675229516524167443, error=" XLAT_FMT_D ", flags=" XLAT_KNOWN(0x1, "SECCOMP_USER_NOTIF_FLAG_CONTINUE") "}) = %s" INJ_STR "\n", XLAT_ARGS_U(SECCOMP_IOCTL_NOTIF_SEND), XLAT_SEL(-ENOSR, "-ENOSR"), errstr); /* SECCOMP_IOCTL_NOTIF_ID_VALID */ static const struct { uint32_t id; const char *str; } id_valid_cmds[] = { { ARG_STR(SECCOMP_IOCTL_NOTIF_ID_VALID) }, { IOC_ANOTHER_DIR(SECCOMP_IOCTL_NOTIF_ID_VALID, _IOC_READ), "SECCOMP_IOCTL_NOTIF_ID_VALID_WRONG_DIR" }, }; TAIL_ALLOC_OBJECT_CONST_PTR(uint64_t, id); for (size_t i = 0; i < ARRAY_SIZE(id_valid_cmds); i++) { sys_ioctl(-1, id_valid_cmds[i].id, 0); pidns_print_leader(); printf("ioctl(-1, " XLAT_FMT ", NULL) = %s" INJ_STR "\n", XLAT_SEL(id_valid_cmds[i].id, id_valid_cmds[i].str), errstr); sys_ioctl(-1, id_valid_cmds[i].id, (uintptr_t) id + 1); pidns_print_leader(); printf("ioctl(-1, " XLAT_FMT ", %p) = %s" INJ_STR "\n", XLAT_SEL(id_valid_cmds[i].id, id_valid_cmds[i].str), (char *) id + 1, errstr); memset(id, 0, sizeof(*id)); rc = sys_ioctl(-1, id_valid_cmds[i].id, (uintptr_t) id); pidns_print_leader(); printf("ioctl(-1, " XLAT_FMT ", [0]) = %s" INJ_STR "\n", XLAT_SEL(id_valid_cmds[i].id, id_valid_cmds[i].str), errstr); *id = 0xdecaffedfacefeedULL; rc = sys_ioctl(-1, id_valid_cmds[i].id, (uintptr_t) id); pidns_print_leader(); printf("ioctl(-1, " XLAT_FMT ", [0xdecaffedfacefeed]) = %s" INJ_STR "\n", XLAT_SEL(id_valid_cmds[i].id, id_valid_cmds[i].str), errstr); } /* SECCOMP_IOCTL_NOTIF_ADDFD */ TAIL_ALLOC_OBJECT_CONST_PTR(struct seccomp_notif_addfd, addfd); close(0); int fd = open(null_path, O_RDONLY); if (fd < 0) perror_msg_and_fail("open(\"%s\")", null_path); if (fd != NULL_FD) { if (dup2(fd, NULL_FD) < 0) perror_msg_and_fail("dup2(fd, NULL_FD)"); close(fd); } fd = open(zero_path, O_RDONLY); if (fd < 0) perror_msg_and_fail("open(\"%s\")", zero_path); if (fd != ZERO_FD) { if (dup2(fd, ZERO_FD) < 0) perror_msg_and_fail("dup2(fd, ZERO_FD)"); close(fd); } sys_ioctl(-1, SECCOMP_IOCTL_NOTIF_ADDFD, 0); pidns_print_leader(); printf("ioctl(-1, " XLAT_FMT ", NULL) = %s" INJ_STR "\n", XLAT_ARGS_U(SECCOMP_IOCTL_NOTIF_ADDFD), errstr); sys_ioctl(-1, SECCOMP_IOCTL_NOTIF_ADDFD, (uintptr_t) addfd + 1); pidns_print_leader(); printf("ioctl(-1, " XLAT_FMT ", %p) = %s" INJ_STR "\n", XLAT_ARGS_U(SECCOMP_IOCTL_NOTIF_ADDFD), (char *) addfd + 1, errstr); memset(addfd, 0, sizeof(*addfd)); rc = sys_ioctl(-1, SECCOMP_IOCTL_NOTIF_ADDFD, (uintptr_t) addfd); pidns_print_leader(); printf("ioctl(-1, " XLAT_FMT ", {id=0, flags=0, srcfd=0" PATH_FMT ", newfd=0, newfd_flags=0}) = %s" INJ_STR "\n", XLAT_ARGS_U(SECCOMP_IOCTL_NOTIF_ADDFD), PRINT_PATHS ? null_path : "", errstr); addfd->id = 0xdeadc0debadfacedULL; addfd->flags = 0xbadc0dec; addfd->srcfd = 0xdeadface; addfd->newfd = 0xbeeffeed; addfd->newfd_flags = O_CLOEXEC|O_DSYNC; rc = sys_ioctl(-1, SECCOMP_IOCTL_NOTIF_ADDFD, (uintptr_t) addfd); pidns_print_leader(); printf("ioctl(-1, " XLAT_FMT ", {id=0xdeadc0debadfaced" ", flags=0xbadc0dec" NRAW(" /* SECCOMP_ADDFD_FLAG_??? */") ", srcfd=-559023410, newfd=-1091567891, newfd_flags=" XLAT_FMT "}) = %s" INJ_STR "\n", XLAT_ARGS_U(SECCOMP_IOCTL_NOTIF_ADDFD), XLAT_ARGS(O_DSYNC|O_CLOEXEC), errstr); addfd->flags = 3; addfd->srcfd = ZERO_FD; addfd->newfd = 0xbeeffeed; addfd->newfd_flags = O_DIRECT; rc = sys_ioctl(-1, SECCOMP_IOCTL_NOTIF_ADDFD, (uintptr_t) addfd); pidns_print_leader(); printf("ioctl(-1, " XLAT_FMT ", {id=0xdeadc0debadfaced" ", flags=" XLAT_KNOWN(0x3, "SECCOMP_ADDFD_FLAG_SETFD" "|SECCOMP_ADDFD_FLAG_SEND") ", srcfd=%d" PATH_FMT ", newfd=-1091567891, newfd_flags=" XLAT_FMT "}) = %s" INJ_STR "\n", XLAT_ARGS_U(SECCOMP_IOCTL_NOTIF_ADDFD), ZERO_FD, PRINT_PATHS ? zero_path : "", XLAT_ARGS(O_DIRECT), errstr); pidns_print_leader(); puts("+++ exited with 0 +++"); return 0; }