/* * Copyright (c) 2021 Eugene Syromyatnikov <evgsyr@gmail.com>. * All rights reserved. * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include "defs.h" #include <linux/ioctl.h> #include <linux/seccomp.h> #include "xlat/seccomp_ioctl_addfd_flags.h" #include "xlat/seccomp_ioctl_resp_flags.h" #define SECCOMP_IOCTL_NOTIF_ID_VALID_WRONG_DIR SECCOMP_IOR(2, __u64) /** Checks that ioctl code's size field contains the expected value. */ #define CHECK_IOCTL_SIZE(ioc_, sz_) \ static_assert(_IOC_SIZE(ioc_) == (sz_), \ "Unexpected size field value in " #ioc_ " (" #sz_" expected)") static void print_struct_seccomp_data(struct tcb *const tcp, const struct seccomp_data *const data) { tprint_struct_begin(); PRINT_FIELD_SYSCALL_NAME(*data, nr, data->arch); tprint_struct_next(); PRINT_FIELD_XVAL(*data, arch, audit_arch, "AUDIT_ARCH_???"); tprint_struct_next(); PRINT_FIELD_ADDR64(*data, instruction_pointer); tprint_struct_next(); PRINT_FIELD_ARRAY(*data, args, tcp, print_xint_array_member); tprint_struct_end(); } static int print_struct_seccomp_notif(struct tcb *const tcp, const kernel_ulong_t addr) { /* * NB: There is a (poorly designed) seccomp(SECCOMP_GET_NOTIF_SIZES) * operation that hints that struct seccomp_notif/seccomp_notif_data * may vary, but that in turn will change the ID of the respective * ioctl, rendering the whole idea of getting the struct size * in advance dubious at best. Let's just put some safeguards * in place for the time being, in case updated headers bring * changes in either ioctl code or struct size. */ CHECK_IOCTL_SIZE(SECCOMP_IOCTL_NOTIF_RECV, 80); CHECK_TYPE_SIZE(struct seccomp_notif, 80); struct seccomp_notif notif; if (umove_or_printaddr(tcp, addr, ¬if)) return RVAL_IOCTL_DECODED; if (entering(tcp)) { if (is_filled((const char *) ¬if, 0, sizeof(notif))) return 0; } tprint_struct_begin(); PRINT_FIELD_X(notif, id); tprint_struct_next(); PRINT_FIELD_TGID(notif, pid, tcp); tprint_struct_next(); PRINT_FIELD_X(notif, flags); tprint_struct_next(); PRINT_FIELD_OBJ_TCB_PTR(notif, data, tcp, print_struct_seccomp_data); tprint_struct_end(); if (entering(tcp)) { tprint_value_changed(); return 0; } return RVAL_IOCTL_DECODED; } static void print_struct_seccomp_notif_resp(struct tcb *const tcp, const kernel_ulong_t addr) { CHECK_IOCTL_SIZE(SECCOMP_IOCTL_NOTIF_SEND, 24); CHECK_TYPE_SIZE(struct seccomp_notif_resp, 24); struct seccomp_notif_resp resp; if (umove_or_printaddr(tcp, addr, &resp)) return; tprint_struct_begin(); PRINT_FIELD_X(resp, id); tprint_struct_next(); PRINT_FIELD_D(resp, val); tprint_struct_next(); PRINT_FIELD_ERR_D(resp, error); tprint_struct_next(); PRINT_FIELD_FLAGS(resp, flags, seccomp_ioctl_resp_flags, "SECCOMP_USER_NOTIF_FLAG_???"); tprint_struct_end(); } static void print_struct_seccomp_notif_addfd(struct tcb *const tcp, const kernel_ulong_t addr) { CHECK_IOCTL_SIZE(SECCOMP_IOCTL_NOTIF_ADDFD, 24); CHECK_TYPE_SIZE(struct seccomp_notif_addfd, 24); struct seccomp_notif_addfd addfd; if (umove_or_printaddr(tcp, addr, &addfd)) return; tprint_struct_begin(); PRINT_FIELD_X(addfd, id); tprint_struct_next(); PRINT_FIELD_FLAGS(addfd, flags, seccomp_ioctl_addfd_flags, "SECCOMP_ADDFD_FLAG_???"); tprint_struct_next(); PRINT_FIELD_FD(addfd, srcfd, tcp); tprint_struct_next(); PRINT_FIELD_D(addfd, newfd); tprint_struct_next(); PRINT_FIELD_FLAGS(addfd, newfd_flags, open_mode_flags, "O_???"); tprint_struct_end(); } int seccomp_ioctl(struct tcb *const tcp, const unsigned int code, const kernel_ulong_t arg) { switch (code) { case SECCOMP_IOCTL_NOTIF_RECV: if (entering(tcp)) tprint_arg_next(); return print_struct_seccomp_notif(tcp, arg); case SECCOMP_IOCTL_NOTIF_SEND: tprint_arg_next(); print_struct_seccomp_notif_resp(tcp, arg); return RVAL_IOCTL_DECODED; case SECCOMP_IOCTL_NOTIF_ID_VALID_WRONG_DIR: case SECCOMP_IOCTL_NOTIF_ID_VALID: tprint_arg_next(); printnum_int64(tcp, arg, "%#" PRIx64); return RVAL_IOCTL_DECODED; case SECCOMP_IOCTL_NOTIF_ADDFD: tprint_arg_next(); print_struct_seccomp_notif_addfd(tcp, arg); return RVAL_IOCTL_DECODED; default: return RVAL_DECODED; } }