This patch is meant for developer debug purposes only. It adds support that displays the offending system call number if QEMU is being killed by seccomp. The offending system call may need to be added to the appropriate system call white list in qemu-seccomp.c to prevent seccomp from killing QEMU.
When the seccomp filter is configured with SCMP_ACT_TRAP, the kernel sends a SIGSYS every time an illegal syscall is called. The role of the debug mode is to handle the SIGSYS, determine the illegal syscall, and print the syscall number to stderr. v3: New in v3. Signed-off-by: Eduardo Otubo <ot...@linux.vnet.ibm.com> Signed-off-by: Corey Bryant <cor...@linux.vnet.ibm.com> --- Makefile.objs | 3 ++ configure | 16 +++++++++ qemu-seccomp-debug.c | 93 ++++++++++++++++++++++++++++++++++++++++++++++++++ qemu-seccomp-debug.h | 40 ++++++++++++++++++++++ qemu-seccomp.c | 4 +-- qemu-seccomp.h | 13 +++++++ qemu-thread-posix.c | 3 ++ vl.c | 10 ++++++ 8 files changed, 180 insertions(+), 2 deletions(-) create mode 100644 qemu-seccomp-debug.c create mode 100644 qemu-seccomp-debug.h diff --git a/Makefile.objs b/Makefile.objs index 682b1e6..4ece4d8 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -104,6 +104,9 @@ common-obj-$(CONFIG_SLIRP) += slirp/ ###################################################################### # libseccomp common-obj-y += qemu-seccomp.o +ifeq ($(CONFIG_SECCOMP_DEBUG),y) +common-obj-y += qemu-seccomp-debug.o +endif ###################################################################### # libuser diff --git a/configure b/configure index d28f8d5..e2417fe 100755 --- a/configure +++ b/configure @@ -222,6 +222,7 @@ want_tools="yes" libiscsi="" coroutine="" seccomp="yes" +seccomp_debug="no" glusterfs="" # parse CC options first @@ -867,6 +868,10 @@ for opt do ;; --disable-seccomp) seccomp="no" ;; + --enable-seccomp-debug) seccomp_debug="yes" + ;; + --disable-seccomp-debug) seccomp_debug="no" + ;; --disable-glusterfs) glusterfs="no" ;; --enable-glusterfs) glusterfs="yes" @@ -876,6 +881,10 @@ for opt do esac done +if test "$seccomp" = "no"; then + seccomp_debug="no"; +fi + case "$cpu" in sparc) LDFLAGS="-m32 $LDFLAGS" @@ -1115,6 +1124,8 @@ echo " --disable-guest-agent disable building of the QEMU Guest Agent" echo " --enable-guest-agent enable building of the QEMU Guest Agent" echo " --disable-seccomp disable seccomp support" echo " --enable-seccomp enables seccomp support" +echo " --disable-seccomp-debug disable seccomp debug support" +echo " --enable-seccomp-debug enables seccomp debug support" echo " --with-coroutine=BACKEND coroutine backend. Supported options:" echo " gthread, ucontext, sigaltstack, windows" echo " --enable-glusterfs enable GlusterFS backend" @@ -3230,6 +3241,7 @@ echo "OpenGL support $opengl" echo "libiscsi support $libiscsi" echo "build guest agent $guest_agent" echo "seccomp support $seccomp" +echo "seccomp debug $seccomp_debug" echo "coroutine backend $coroutine_backend" echo "GlusterFS support $glusterfs" @@ -3534,6 +3546,10 @@ if test "$seccomp" = "yes"; then echo "CONFIG_SECCOMP=y" >> $config_host_mak fi +if test "$seccomp_debug" = "yes"; then + echo "CONFIG_SECCOMP_DEBUG=y" >> $config_host_mak +fi + # XXX: suppress that if [ "$bsd" = "yes" ] ; then echo "CONFIG_BSD=y" >> $config_host_mak diff --git a/qemu-seccomp-debug.c b/qemu-seccomp-debug.c new file mode 100644 index 0000000..4b64e8c --- /dev/null +++ b/qemu-seccomp-debug.c @@ -0,0 +1,93 @@ +/* + * QEMU seccomp mode 2 support with libseccomp + * Debug system calls helper functions + * + * Copyright IBM, Corp. 2012 + * + * Authors: + * Eduardo Otubo <eot...@br.ibm.com> + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + * + * Contributions after 2012-01-13 are licensed under the terms of the + * GNU GPL, version 2 or (at your option) any later version. + */ + +#include "qemu-seccomp-debug.h" +#include <asm-generic/unistd.h> + +#define safe_warn(data, len) write(STDERR_FILENO, (const void *) data, len) + +static int count_digits(int number) +{ + int digits = 0; + while (number) { + number /= 10; + digits++; + } + + return digits; +} + +static char *sput_i(int integer, char *string) +{ + if (integer / 10 != 0) { + string = sput_i(integer / 10, string); + } + *string++ = (char) ('0' + integer % 10); + return string; +} + +static void int_to_asc(int integer, char *string) +{ + *sput_i(integer, string) = '\n'; +} + +static void syscall_debug(int nr, siginfo_t *info, void *void_context) +{ + ucontext_t *ctx = (ucontext_t *) (void_context); + char errormsg[] = "seccomp: illegal syscall trapped: "; + char syscall_char[count_digits(__NR_syscalls) + 1]; + int syscall_num = 0; + int i; + + for (i = 0; i < count_digits(__NR_syscalls) + 1; i++) { + syscall_char[i] = ' '; + } + if (info->si_code != SYS_SECCOMP) { + return; + } + if (!ctx) { + return; + } + syscall_num = ctx->uc_mcontext.gregs[REG_SYSCALL]; + int_to_asc(syscall_num, syscall_char); + if ((safe_warn(errormsg, sizeof(errormsg)-1) < 0) || + (safe_warn(syscall_char, sizeof(syscall_char)) < 0)) { + return; + } + return; +} + +int seccomp_install_syscall_debug(void) +{ + struct sigaction act; + sigset_t mask; + + memset(&act, 0, sizeof(act)); + sigemptyset(&mask); + sigaddset(&mask, SIGSYS); + + act.sa_sigaction = &syscall_debug; + act.sa_flags = SA_SIGINFO; + if (sigaction(SIGSYS, &act, NULL) < 0) { + perror("seccomp: sigaction returned with errors\n"); + return -1; + } + if (pthread_sigmask(SIG_UNBLOCK, &mask, NULL)) { + perror("seccomp: pthread_sigmask returned with errors\n"); + return -1; + } + return 0; +} diff --git a/qemu-seccomp-debug.h b/qemu-seccomp-debug.h new file mode 100644 index 0000000..c41538e --- /dev/null +++ b/qemu-seccomp-debug.h @@ -0,0 +1,40 @@ +/* + * QEMU seccomp mode 2 support with libseccomp + * Trap system calls helper functions + * + * Copyright IBM, Corp. 2012 + * + * Authors: + * Eduardo Otubo <eot...@br.ibm.com> + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + * + * Contributions after 2012-01-13 are licensed under the terms of the + * GNU GPL, version 2 or (at your option) any later version. + */ +#ifndef QEMU_SECCOMP_TRAP_H +#define QEMU_SECCOMP_TRAP_H + +#include <signal.h> +#include <string.h> +#include <stdio.h> +#include <unistd.h> + +#if defined(__i386__) +#define REG_SYSCALL REG_EAX +#include <asm/unistd_32.h> +#elif defined(__x86_64__) +#define REG_SYSCALL REG_RAX +#include <asm/unistd_64.h> +#else +#error Unsupported platform +#endif + +#ifndef SYS_SECCOMP +#define SYS_SECCOMP 1 +#endif + +int seccomp_install_syscall_debug(void); + +#endif diff --git a/qemu-seccomp.c b/qemu-seccomp.c index d5a3b0f..d2177f8 100644 --- a/qemu-seccomp.c +++ b/qemu-seccomp.c @@ -454,7 +454,7 @@ static int process_list(scmp_filter_ctx *ctx, unsigned int i = 0; for (i = 0; i < list_size; i++) { - rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, list[i].num, 0); + rc = seccomp_rule_add(ctx, SECCOMP_ALLOW, list[i].num, 0); if (rc < 0) { return rc; } @@ -474,7 +474,7 @@ int seccomp_install_filter(int state) #ifdef CONFIG_SECCOMP scmp_filter_ctx ctx = NULL; - ctx = seccomp_init(SCMP_ACT_KILL); + ctx = seccomp_init(SECCOMP_DENY); if (ctx == NULL) { rc = -1; goto seccomp_return; diff --git a/qemu-seccomp.h b/qemu-seccomp.h index 029c111..3fd6ad3 100644 --- a/qemu-seccomp.h +++ b/qemu-seccomp.h @@ -15,10 +15,23 @@ #ifndef QEMU_SECCOMP_H #define QEMU_SECCOMP_H +#include <stdint.h> #ifdef CONFIG_SECCOMP #include <seccomp.h> #endif +#define SECCOMP_ALLOW SCMP_ACT_ALLOW + +#ifdef CONFIG_SECCOMP +#ifdef CONFIG_SECCOMP_DEBUG +#define SECCOMP_DENY SCMP_ACT_TRAP +#else +#define SECCOMP_DENY SCMP_ACT_KILL +#endif +#else +#define SECCOMP_DENY 0 +#endif + enum seccomp_states { SECCOMP_OFF, SECCOMP_ON, diff --git a/qemu-thread-posix.c b/qemu-thread-posix.c index 4ef9c7b..3c67732 100644 --- a/qemu-thread-posix.c +++ b/qemu-thread-posix.c @@ -288,6 +288,9 @@ void qemu_thread_create(QemuThread *thread, /* Leave signal handling to the iothread. */ sigfillset(&set); +#ifdef CONFIG_SECCOMP_DEBUG + sigdelset(&set, SIGSYS); +#endif pthread_sigmask(SIG_SETMASK, &set, &oldset); err = pthread_create(&thread->thread, &attr, start_routine, arg); if (err) diff --git a/vl.c b/vl.c index 80b1fff..8f970e4 100644 --- a/vl.c +++ b/vl.c @@ -65,6 +65,9 @@ #endif #include "qemu-seccomp.h" +#ifdef CONFIG_SECCOMP_DEBUG +#include "qemu-seccomp-debug.h" +#endif #ifdef __sun__ #include <sys/stat.h> @@ -3483,6 +3486,13 @@ int main(int argc, char **argv, char **envp) } if (seccomp_get_state() == SECCOMP_ON) { +#ifdef CONFIG_SECCOMP_DEBUG + if (seccomp_install_syscall_debug() < 0) { + fprintf(stderr, + "failed to install seccomp syscall debug support\n"); + exit(1); + } +#endif if (seccomp_install_filter(SECCOMP_INIT) < 0) { fprintf(stderr, "failed to install seccomp syscall " "initialization filter\n"); -- 1.7.10.4