From 0d690970b9ebaf57df6c78319df8051c8341fb20 Mon Sep 17 00:00:00 2001
From: Larry Dewey <ldewey@suse.com>
Date: Tue, 23 Oct 2018 11:04:21 -0600
Subject: [PATCH] seccomp: secure all threads with seccomp manually.

Just before starting a child thread, a call to seccomp_start() has been
added to ensure that each thread is sandboxed properly, and that
seccomp is successfully applied to all threads when the user specifies
that sanboxing should be used either via /etc/libvirt/qemu.conf or via
the command line by passing `--seccomp on` to qemu.

[LD: BSC#1106222 CVE-2018-15746]
Signed-off-by: Larry Dewey <ldewey@suse.com>
---
 block/archipelago.c         |  9 +++++++++
 cpus.c                      | 16 ++++++++++++++++
 hw/misc/edu.c               |  8 ++++++++
 hw/usb/ccid-card-emulated.c | 12 ++++++++++++
 include/sysemu/seccomp.h    |  2 +-
 iothread.c                  |  8 ++++++++
 migration/migration.c       |  9 +++++++++
 qemu-img.c                  |  9 +++++++++
 qemu-io.c                   |  9 +++++++++
 qemu-nbd.c                  |  9 +++++++++
 qemu-seccomp.c              | 13 ++++++++++++-
 qom/object.c                |  2 +-
 stubs/qtest.c               |  6 ++++++
 tests/test-aio.c            |  8 ++++++++
 tests/test-rfifolock.c      |  8 ++++++++
 thread-pool.c               |  9 +++++++++
 ui/vnc-jobs.c               |  8 ++++++++
 util/compatfd.c             |  8 ++++++++
 util/rcu.c                  |  7 +++++++
 vl.c                        | 22 +++++++++++-----------
 20 files changed, 168 insertions(+), 14 deletions(-)

diff --git a/block/archipelago.c b/block/archipelago.c
index 855655c6bd..10c3a16ee4 100644
--- a/block/archipelago.c
+++ b/block/archipelago.c
@@ -59,6 +59,10 @@
 #include "qapi/qmp/qjson.h"
 #include "qemu/atomic.h"
 
+#ifdef CONFIG_SECCOMP
+#include "sysemu/seccomp.h"
+#endif
+
 #include <inttypes.h>
 #include <xseg/xseg.h>
 #include <xseg/protocol.h>
@@ -188,6 +192,11 @@ static int wait_reply(struct xseg *xseg, xport srcport, struct xseg_port *port,
 static void xseg_request_handler(void *state)
 {
     BDRVArchipelagoState *s = (BDRVArchipelagoState *) state;
+
+#ifdef CONFIG_SECCOMP
+    seccomp_start(!!0);
+#endif
+
     void *psd = xseg_get_signal_desc(s->xseg, s->port);
     qemu_mutex_lock(&s->request_mutex);
 
diff --git a/cpus.c b/cpus.c
index 685b416f2a..9d5d9b787f 100644
--- a/cpus.c
+++ b/cpus.c
@@ -42,6 +42,10 @@
 #include "qapi-event.h"
 #include "hw/nmi.h"
 
+#ifdef CONFIG_SECCOMP
+#include "sysemu/seccomp.h"
+#endif
+
 #ifndef _WIN32
 #include "qemu/compatfd.h"
 #endif
@@ -924,6 +928,10 @@ static void *qemu_kvm_cpu_thread_fn(void *arg)
     CPUState *cpu = arg;
     int r;
 
+#ifdef CONFIG_SECCOMP
+    seccomp_start(!!0);
+#endif
+
     qemu_mutex_lock(&qemu_global_mutex);
     qemu_thread_get_self(cpu->thread);
     cpu->thread_id = qemu_get_thread_id();
@@ -965,6 +973,10 @@ static void *qemu_dummy_cpu_thread_fn(void *arg)
     sigset_t waitset;
     int r;
 
+#ifdef CONFIG_SECCOMP
+    seccomp_start(!!0);
+#endif
+
     qemu_mutex_lock_iothread();
     qemu_thread_get_self(cpu->thread);
     cpu->thread_id = qemu_get_thread_id();
@@ -1004,6 +1016,10 @@ static void *qemu_tcg_cpu_thread_fn(void *arg)
 {
     CPUState *cpu = arg;
 
+#ifdef CONFIG_SECCOMP
+    seccomp_start(!!0);
+#endif
+
     qemu_tcg_init_cpu_signals();
     qemu_thread_get_self(cpu->thread);
 
diff --git a/hw/misc/edu.c b/hw/misc/edu.c
index f601069e82..c5cbdd9288 100644
--- a/hw/misc/edu.c
+++ b/hw/misc/edu.c
@@ -27,6 +27,10 @@
 #include "qemu/main-loop.h" /* iothread mutex */
 #include "qapi/visitor.h"
 
+#ifdef CONFIG_SECCOMP
+#include "sysemu/seccomp.h"
+#endif
+
 #define EDU(obj)        OBJECT_CHECK(EduState, obj, "edu")
 
 #define FACT_IRQ        0x00000001
@@ -286,6 +290,10 @@ static void *edu_fact_thread(void *opaque)
 {
     EduState *edu = opaque;
 
+#ifdef CONFIG_SECCOMP
+    seccomp_start(!!0);
+#endif
+
     while (1) {
         uint32_t val, ret = 1;
 
diff --git a/hw/usb/ccid-card-emulated.c b/hw/usb/ccid-card-emulated.c
index aa1c37aabd..e43c7b67aa 100644
--- a/hw/usb/ccid-card-emulated.c
+++ b/hw/usb/ccid-card-emulated.c
@@ -36,6 +36,10 @@
 #include "monitor/monitor.h"
 #include "ccid.h"
 
+#ifdef CONFIG_SECCOMP
+#include "sysemu/seccomp.h"
+#endif
+
 #define DPRINTF(card, lvl, fmt, ...) \
 do {\
     if (lvl <= card->debug) {\
@@ -232,6 +236,10 @@ static void *handle_apdu_thread(void* arg)
     VReaderStatus reader_status;
     EmulEvent *event;
 
+#ifdef CONFIG_SECCOMP
+    seccomp_start(!!0);
+#endif
+
     while (1) {
         qemu_mutex_lock(&card->handle_apdu_mutex);
         qemu_cond_wait(&card->handle_apdu_cond, &card->handle_apdu_mutex);
@@ -279,6 +287,10 @@ static void *event_thread(void *arg)
     VEvent *event = NULL;
     EmulatedState *card = arg;
 
+#ifdef CONFIG_SECCOMP
+    seccomp_start(!!0);
+#endif
+
     while (1) {
         const char *reader_name;
 
diff --git a/include/sysemu/seccomp.h b/include/sysemu/seccomp.h
index 1189fa241d..28e67ed0c7 100644
--- a/include/sysemu/seccomp.h
+++ b/include/sysemu/seccomp.h
@@ -18,5 +18,5 @@
 #include <seccomp.h>
 #include "qemu/osdep.h"
 
-int seccomp_start(void);
+int seccomp_start(_Bool enabled);
 #endif
diff --git a/iothread.c b/iothread.c
index 342a23fcb0..5269655995 100644
--- a/iothread.c
+++ b/iothread.c
@@ -19,6 +19,10 @@
 #include "qmp-commands.h"
 #include "qemu/error-report.h"
 
+#ifdef CONFIG_SECCOMP
+#include "sysemu/seccomp.h"
+#endif
+
 #define IOTHREADS_PATH "/objects"
 
 typedef ObjectClass IOThreadClass;
@@ -33,6 +37,10 @@ static void *iothread_run(void *opaque)
     IOThread *iothread = opaque;
     bool blocking;
 
+#ifdef CONFIG_SECCOMP
+    seccomp_start(!!0);
+#endif
+
     qemu_mutex_lock(&iothread->init_done_lock);
     iothread->thread_id = qemu_get_thread_id();
     qemu_cond_signal(&iothread->init_done_cond);
diff --git a/migration/migration.c b/migration/migration.c
index aedb2c0ea7..2ac1975334 100644
--- a/migration/migration.c
+++ b/migration/migration.c
@@ -26,6 +26,11 @@
 #include "qmp-commands.h"
 #include "trace.h"
 
+
+#ifdef CONFIG_SECCOMP
+#include "sysemu/seccomp.h"
+#endif
+
 #define MAX_THROTTLE  (32 << 20)      /* Migration speed throttling */
 
 /* Amount of time to allocate to each "chunk" of bandwidth-throttled
@@ -617,6 +622,10 @@ static void *migration_thread(void *opaque)
     int64_t start_time = initial_time;
     bool old_vm_running = false;
 
+#ifdef CONFIG_SECCOMP
+    seccomp_start(!!0);
+#endif
+
     qemu_savevm_state_begin(s->file, &s->params);
 
     s->setup_time = qemu_clock_get_ms(QEMU_CLOCK_HOST) - setup_start;
diff --git a/qemu-img.c b/qemu-img.c
index 56e51cf2a4..6b8bdd9d22 100644
--- a/qemu-img.c
+++ b/qemu-img.c
@@ -25,6 +25,7 @@
 #include "qapi/qmp-output-visitor.h"
 #include "qapi/qmp/qjson.h"
 #include "qemu-common.h"
+#include "qemu/compiler.h"
 #include "qemu/option.h"
 #include "qemu/error-report.h"
 #include "qemu/osdep.h"
@@ -35,9 +36,17 @@
 #include "block/qapi.h"
 #include <getopt.h>
 
+#ifdef CONFIG_SECCOMP
+#include "sysemu/seccomp.h"
+#endif
+
 #define QEMU_IMG_VERSION "qemu-img version " QEMU_VERSION QEMU_PKGVERSION \
                           ", Copyright (c) 2004-2008 Fabrice Bellard\n"
 
+#ifdef CONFIG_SECCOMP
+int seccomp_start(_Bool enabled) { return 0; }
+#endif
+
 typedef struct img_cmd_t {
     const char *name;
     int (*handler)(int argc, char **argv);
diff --git a/qemu-io.c b/qemu-io.c
index 8e41080cc9..a00a643bf8 100644
--- a/qemu-io.c
+++ b/qemu-io.c
@@ -18,11 +18,16 @@
 #include "qemu/main-loop.h"
 #include "qemu/option.h"
 #include "qemu/config-file.h"
+#include "qemu/compiler.h"
 #include "qemu/readline.h"
 #include "sysemu/block-backend.h"
 #include "block/block_int.h"
 #include "trace/control.h"
 
+#ifdef CONFIG_SECCOMP
+#include "sysemu/seccomp.h"
+#endif
+
 #define CMD_NOFILE_OK   0x01
 
 static char *progname;
@@ -35,6 +40,10 @@ static char **cmdline;
 
 static ReadLineState *readline_state;
 
+#ifdef CONFIG_SECCOMP
+int seccomp_start(_Bool enabled) { return 0; }
+#endif
+
 static int close_f(BlockBackend *blk, int argc, char **argv)
 {
     blk_unref(qemuio_blk);
diff --git a/qemu-nbd.c b/qemu-nbd.c
index 98e4907ef4..b3882b4f49 100644
--- a/qemu-nbd.c
+++ b/qemu-nbd.c
@@ -22,10 +22,15 @@
 #include "block/nbd.h"
 #include "qemu/main-loop.h"
 #include "qemu/sockets.h"
+#include "qemu/compiler.h"
 #include "qemu/error-report.h"
 #include "block/snapshot.h"
 #include "qapi/util.h"
 
+#ifdef CONFIG_SECCOMP
+#include "sysemu/seccomp.h"
+#endif
+
 #include <stdarg.h>
 #include <stdio.h>
 #include <getopt.h>
@@ -54,6 +59,10 @@ static enum { RUNNING, TERMINATE, TERMINATING, TERMINATED } state;
 static int shared = 1;
 static int nb_fds;
 
+#ifdef CONFIG_SECCOMP
+int seccomp_start(_Bool enabled) { return 0; }
+#endif
+
 static void usage(const char *name)
 {
     (printf) (
diff --git a/qemu-seccomp.c b/qemu-seccomp.c
index f9de0d3390..dae622bd58 100644
--- a/qemu-seccomp.c
+++ b/qemu-seccomp.c
@@ -240,12 +240,23 @@ static const struct QemuSeccompSyscall seccomp_whitelist[] = {
     { SCMP_SYS(mbind), 240 }
 };
 
-int seccomp_start(void)
+int seccomp_start(_Bool enabled)
 {
+    static _Bool is_enabled = !!0;
     int rc = 0;
     unsigned int i = 0;
     scmp_filter_ctx ctx;
 
+    if (!is_enabled && !enabled)
+    {
+        return rc;
+    }
+
+    if (enabled)
+    {
+        is_enabled = !!1;
+    }
+
     ctx = seccomp_init(SCMP_ACT_KILL);
     if (ctx == NULL) {
         rc = -1;
diff --git a/qom/object.c b/qom/object.c
index b8dff43297..de4fafe95f 100644
--- a/qom/object.c
+++ b/qom/object.c
@@ -647,7 +647,7 @@ static void object_class_foreach_tramp(gpointer key, gpointer value,
         return;
     }
 
-    if (data->implements_type && 
+    if (data->implements_type &&
         !object_class_dynamic_cast(k, data->implements_type)) {
         return;
     }
diff --git a/stubs/qtest.c b/stubs/qtest.c
index dc17594bb6..aae6e753a0 100644
--- a/stubs/qtest.c
+++ b/stubs/qtest.c
@@ -10,5 +10,11 @@
 
 #include "sysemu/qtest.h"
 
+/* Needed to stub for seccomp */
+
+int seccomp_start(_Bool enabled) __attribute__ ((weak));
+int seccomp_start(_Bool enabled) { return 0; }
+
+
 /* Needed for qtest_allowed() */
 bool qtest_allowed;
diff --git a/tests/test-aio.c b/tests/test-aio.c
index a7cb5c9915..26918573c3 100644
--- a/tests/test-aio.c
+++ b/tests/test-aio.c
@@ -16,6 +16,10 @@
 #include "qemu/sockets.h"
 #include "qemu/error-report.h"
 
+#ifdef CONFIG_SECCOMP
+#include "sysemu/seccomp.h"
+#endif
+
 static AioContext *ctx;
 
 typedef struct {
@@ -114,6 +118,10 @@ static void *test_acquire_thread(void *opaque)
 {
     AcquireTestData *data = opaque;
 
+#ifdef CONFIG_SECCOMP
+    seccomp_start(!!0);
+#endif
+
     /* Wait for other thread to let us start */
     qemu_mutex_lock(&data->start_lock);
     qemu_mutex_unlock(&data->start_lock);
diff --git a/tests/test-rfifolock.c b/tests/test-rfifolock.c
index 0572ebb42a..d272aca6eb 100644
--- a/tests/test-rfifolock.c
+++ b/tests/test-rfifolock.c
@@ -14,6 +14,10 @@
 #include "qemu-common.h"
 #include "qemu/rfifolock.h"
 
+#ifdef CONFIG_SECCOMP
+#include "sysemu/seccomp.h"
+#endif
+
 static void test_nesting(void)
 {
     RFifoLock lock;
@@ -48,6 +52,10 @@ static void *callback_thread(void *opaque)
 {
     CallbackTestData *data = opaque;
 
+#ifdef CONFIG_SECCOMP
+    seccomp_start(!!0);
+#endif
+
     /* The other thread holds the lock so the contention callback will be
      * invoked...
      */
diff --git a/thread-pool.c b/thread-pool.c
index d16d1137df..4a4991ef9e 100644
--- a/thread-pool.c
+++ b/thread-pool.c
@@ -22,6 +22,11 @@
 #include "trace.h"
 #include "block/thread-pool.h"
 #include "qemu/main-loop.h"
+#include "qemu/compiler.h"
+
+#ifdef CONFIG_SECCOMP
+#include "sysemu/seccomp.h"
+#endif
 
 static void do_spawn_thread(ThreadPool *pool);
 
@@ -78,6 +83,10 @@ static void *worker_thread(void *opaque)
 {
     ThreadPool *pool = opaque;
 
+#ifdef CONFIG_SECCOMP
+    seccomp_start(!!0);
+#endif
+
     qemu_mutex_lock(&pool->lock);
     pool->pending_threads--;
     do_spawn_thread(pool);
diff --git a/ui/vnc-jobs.c b/ui/vnc-jobs.c
index c8ee203495..7dec3e25a0 100644
--- a/ui/vnc-jobs.c
+++ b/ui/vnc-jobs.c
@@ -30,6 +30,10 @@
 #include "vnc-jobs.h"
 #include "qemu/sockets.h"
 
+#ifdef CONFIG_SECCOMP
+#include "sysemu/seccomp.h"
+#endif
+
 /*
  * Locking:
  *
@@ -217,6 +221,10 @@ static int vnc_worker_thread_loop(VncJobQueue *queue)
     int n_rectangles;
     int saved_offset;
 
+#ifdef CONFIG_SECCOMP
+    seccomp_start(!!0);
+#endif
+
     vnc_lock_queue(queue);
     while (QTAILQ_EMPTY(&queue->jobs) && !queue->exit) {
         qemu_cond_wait(&queue->cond, &queue->mutex);
diff --git a/util/compatfd.c b/util/compatfd.c
index 341ada638f..479440133e 100644
--- a/util/compatfd.c
+++ b/util/compatfd.c
@@ -17,6 +17,10 @@
 #include "qemu/compatfd.h"
 #include "qemu/thread.h"
 
+#ifdef CONFIG_SECCOMP
+#include "sysemu/seccomp.h"
+#endif
+
 #include <sys/syscall.h>
 
 struct sigfd_compat_info
@@ -29,6 +33,10 @@ static void *sigwait_compat(void *opaque)
 {
     struct sigfd_compat_info *info = opaque;
 
+#ifdef CONFIG_SECCOMP
+    seccomp_start(!!0);
+#endif
+
     while (1) {
         int sig;
         int err;
diff --git a/util/rcu.c b/util/rcu.c
index 7270151bef..5861c89592 100644
--- a/util/rcu.c
+++ b/util/rcu.c
@@ -36,6 +36,9 @@
 #include "qemu/atomic.h"
 #include "qemu/thread.h"
 #include "qemu/main-loop.h"
+#ifdef CONFIG_SECCOMP
+#include "sysemu/seccomp.h"
+#endif
 
 /*
  * Global grace period counter.  Bit 0 is always one in rcu_gp_ctr.
@@ -216,6 +219,10 @@ static void *call_rcu_thread(void *opaque)
 {
     struct rcu_head *node;
 
+#ifdef CONFIG_SECCOMP
+    seccomp_start(!!0);
+#endif
+
     for (;;) {
         int tries = 0;
         int n = atomic_read(&rcu_call_count);
diff --git a/vl.c b/vl.c
index 7e0e8517c4..950ac6dff5 100644
--- a/vl.c
+++ b/vl.c
@@ -966,7 +966,7 @@ static int parse_sandbox(QemuOpts *opts, void *opaque)
     /* FIXME: change this to true for 1.3 */
     if (qemu_opt_get_bool(opts, "enable", false)) {
 #ifdef CONFIG_SECCOMP
-        if (seccomp_start() < 0) {
+        if (seccomp_start(!!1) < 0) {
             qerror_report(ERROR_CLASS_GENERIC_ERROR,
                           "failed to install seccomp syscall filter in the kernel");
             return -1;
@@ -2857,6 +2857,12 @@ int main(int argc, char **argv, char **envp)
             case QEMU_OPTION_nouserconfig:
                 userconfig = false;
                 break;
+            case QEMU_OPTION_sandbox:
+                opts = qemu_opts_parse(qemu_find_opts("sandbox"), optarg, 1);
+                if (!opts) {
+                    exit(1);
+                }
+                break;
             }
         }
     }
@@ -2869,6 +2875,10 @@ int main(int argc, char **argv, char **envp)
         }
     }
 
+    if (qemu_opts_foreach(qemu_find_opts("sandbox"), parse_sandbox, NULL, 0)) {
+        exit(1);
+    }
+
     /* second pass of option parsing */
     optind = 1;
     for(;;) {
@@ -3724,12 +3734,6 @@ int main(int argc, char **argv, char **envp)
             case QEMU_OPTION_qtest_log:
                 qtest_log = optarg;
                 break;
-            case QEMU_OPTION_sandbox:
-                opts = qemu_opts_parse(qemu_find_opts("sandbox"), optarg, 1);
-                if (!opts) {
-                    exit(1);
-                }
-                break;
             case QEMU_OPTION_add_fd:
 #ifndef _WIN32
                 opts = qemu_opts_parse(qemu_find_opts("add-fd"), optarg, 0);
@@ -3797,10 +3801,6 @@ int main(int argc, char **argv, char **envp)
         exit(1);
     }
 
-    if (qemu_opts_foreach(qemu_find_opts("sandbox"), parse_sandbox, NULL, 0)) {
-        exit(1);
-    }
-
     if (qemu_opts_foreach(qemu_find_opts("name"), parse_name, NULL, 1)) {
         exit(1);
     }
