qemu/tests/unit/test-seccomp.c
<<
>>
Prefs
   1/*
   2 * QEMU seccomp test suite
   3 *
   4 * Copyright (c) 2021 Red Hat, Inc.
   5 *
   6 * This library is free software; you can redistribute it and/or
   7 * modify it under the terms of the GNU Lesser General Public
   8 * License as published by the Free Software Foundation; either
   9 * version 2.1 of the License, or (at your option) any later version.
  10 *
  11 * This library is distributed in the hope that it will be useful,
  12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14 * Lesser General Public License for more details.
  15 *
  16 * You should have received a copy of the GNU Lesser General Public
  17 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
  18 *
  19 */
  20
  21#include "qemu/osdep.h"
  22#include "qemu/config-file.h"
  23#include "qemu/option.h"
  24#include "sysemu/seccomp.h"
  25#include "qapi/error.h"
  26#include "qemu/module.h"
  27
  28#include <unistd.h>
  29#include <sys/syscall.h>
  30
  31static void test_seccomp_helper(const char *args, bool killed,
  32                                int errnum, int (*doit)(void))
  33{
  34    if (g_test_subprocess()) {
  35        QemuOptsList *olist;
  36        QemuOpts *opts;
  37        int ret;
  38
  39        module_call_init(MODULE_INIT_OPTS);
  40        olist = qemu_find_opts("sandbox");
  41        g_assert(olist != NULL);
  42
  43        opts = qemu_opts_parse_noisily(olist, args, true);
  44        g_assert(opts != NULL);
  45
  46        parse_sandbox(NULL, opts, &error_abort);
  47
  48        /* Running in a child process */
  49        ret = doit();
  50
  51        if (errnum != 0) {
  52            g_assert(ret != 0);
  53            g_assert(errno == errnum);
  54        } else {
  55            g_assert(ret == 0);
  56        }
  57
  58        _exit(0);
  59    } else {
  60        /* Running in main test process, spawning the child */
  61        g_test_trap_subprocess(NULL, 0, 0);
  62        if (killed) {
  63            g_test_trap_assert_failed();
  64        } else {
  65            g_test_trap_assert_passed();
  66        }
  67    }
  68}
  69
  70
  71static void test_seccomp_killed(const char *args, int (*doit)(void))
  72{
  73    test_seccomp_helper(args, true, 0, doit);
  74}
  75
  76static void test_seccomp_errno(const char *args, int errnum, int (*doit)(void))
  77{
  78    test_seccomp_helper(args, false, errnum, doit);
  79}
  80
  81static void test_seccomp_passed(const char *args, int (*doit)(void))
  82{
  83    test_seccomp_helper(args, false, 0, doit);
  84}
  85
  86#ifdef SYS_fork
  87static int doit_sys_fork(void)
  88{
  89    int ret = syscall(SYS_fork);
  90    if (ret < 0) {
  91        return ret;
  92    }
  93    if (ret == 0) {
  94        _exit(0);
  95    }
  96    return 0;
  97}
  98
  99static void test_seccomp_sys_fork_on_nospawn(void)
 100{
 101    test_seccomp_killed("on,spawn=deny", doit_sys_fork);
 102}
 103
 104static void test_seccomp_sys_fork_on(void)
 105{
 106    test_seccomp_passed("on", doit_sys_fork);
 107}
 108
 109static void test_seccomp_sys_fork_off(void)
 110{
 111    test_seccomp_passed("off", doit_sys_fork);
 112}
 113#endif
 114
 115static int doit_fork(void)
 116{
 117    int ret = fork();
 118    if (ret < 0) {
 119        return ret;
 120    }
 121    if (ret == 0) {
 122        _exit(0);
 123    }
 124    return 0;
 125}
 126
 127static void test_seccomp_fork_on_nospawn(void)
 128{
 129    test_seccomp_killed("on,spawn=deny", doit_fork);
 130}
 131
 132static void test_seccomp_fork_on(void)
 133{
 134    test_seccomp_passed("on", doit_fork);
 135}
 136
 137static void test_seccomp_fork_off(void)
 138{
 139    test_seccomp_passed("off", doit_fork);
 140}
 141
 142static void *noop(void *arg)
 143{
 144    return arg;
 145}
 146
 147static int doit_thread(void)
 148{
 149    pthread_t th;
 150    int ret = pthread_create(&th, NULL, noop, NULL);
 151    if (ret != 0) {
 152        errno = ret;
 153        return -1;
 154    } else {
 155        pthread_join(th, NULL);
 156        return 0;
 157    }
 158}
 159
 160static void test_seccomp_thread_on(void)
 161{
 162    test_seccomp_passed("on", doit_thread);
 163}
 164
 165static void test_seccomp_thread_on_nospawn(void)
 166{
 167    test_seccomp_passed("on,spawn=deny", doit_thread);
 168}
 169
 170static void test_seccomp_thread_off(void)
 171{
 172    test_seccomp_passed("off", doit_thread);
 173}
 174
 175static int doit_sched(void)
 176{
 177    struct sched_param param = { .sched_priority = 0 };
 178    return sched_setscheduler(getpid(), SCHED_OTHER, &param);
 179}
 180
 181static void test_seccomp_sched_on_nores(void)
 182{
 183    test_seccomp_errno("on,resourcecontrol=deny", EPERM, doit_sched);
 184}
 185
 186static void test_seccomp_sched_on(void)
 187{
 188    test_seccomp_passed("on", doit_sched);
 189}
 190
 191static void test_seccomp_sched_off(void)
 192{
 193    test_seccomp_passed("off", doit_sched);
 194}
 195
 196static bool can_play_with_seccomp(void)
 197{
 198    g_autofree char *status = NULL;
 199    g_auto(GStrv) lines = NULL;
 200    size_t i;
 201
 202    if (!g_file_get_contents("/proc/self/status", &status, NULL, NULL)) {
 203        return false;
 204    }
 205
 206    lines = g_strsplit(status, "\n", 0);
 207
 208    for (i = 0; lines[i] != NULL; i++) {
 209        if (g_str_has_prefix(lines[i], "Seccomp:")) {
 210            /*
 211             * "Seccomp: 1" or "Seccomp: 2" indicate we're already
 212             * confined, probably as we're inside a container. In
 213             * this case our tests might get unexpected results,
 214             * so we can't run reliably
 215             */
 216            if (!strchr(lines[i], '0')) {
 217                return false;
 218            }
 219
 220            return true;
 221        }
 222    }
 223
 224    /* Doesn't look like seccomp is enabled in the kernel */
 225    return false;
 226}
 227
 228int main(int argc, char **argv)
 229{
 230    g_test_init(&argc, &argv, NULL);
 231    if (can_play_with_seccomp()) {
 232#ifdef SYS_fork
 233        g_test_add_func("/softmmu/seccomp/sys-fork/on",
 234                        test_seccomp_sys_fork_on);
 235        g_test_add_func("/softmmu/seccomp/sys-fork/on-nospawn",
 236                        test_seccomp_sys_fork_on_nospawn);
 237        g_test_add_func("/softmmu/seccomp/sys-fork/off",
 238                        test_seccomp_sys_fork_off);
 239#endif
 240
 241        g_test_add_func("/softmmu/seccomp/fork/on",
 242                        test_seccomp_fork_on);
 243        g_test_add_func("/softmmu/seccomp/fork/on-nospawn",
 244                        test_seccomp_fork_on_nospawn);
 245        g_test_add_func("/softmmu/seccomp/fork/off",
 246                        test_seccomp_fork_off);
 247
 248        g_test_add_func("/softmmu/seccomp/thread/on",
 249                        test_seccomp_thread_on);
 250        g_test_add_func("/softmmu/seccomp/thread/on-nospawn",
 251                        test_seccomp_thread_on_nospawn);
 252        g_test_add_func("/softmmu/seccomp/thread/off",
 253                        test_seccomp_thread_off);
 254
 255        if (doit_sched() == 0) {
 256            /*
 257             * musl doesn't impl sched_setscheduler, hence
 258             * we check above if it works first
 259             */
 260            g_test_add_func("/softmmu/seccomp/sched/on",
 261                            test_seccomp_sched_on);
 262            g_test_add_func("/softmmu/seccomp/sched/on-nores",
 263                            test_seccomp_sched_on_nores);
 264            g_test_add_func("/softmmu/seccomp/sched/off",
 265                            test_seccomp_sched_off);
 266        }
 267    }
 268    return g_test_run();
 269}
 270