qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

test-seccomp.c (6795B)


      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 
     31 static 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 
     71 static void test_seccomp_killed(const char *args, int (*doit)(void))
     72 {
     73     test_seccomp_helper(args, true, 0, doit);
     74 }
     75 
     76 static void test_seccomp_errno(const char *args, int errnum, int (*doit)(void))
     77 {
     78     test_seccomp_helper(args, false, errnum, doit);
     79 }
     80 
     81 static 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
     87 static 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 
     99 static void test_seccomp_sys_fork_on_nospawn(void)
    100 {
    101     test_seccomp_killed("on,spawn=deny", doit_sys_fork);
    102 }
    103 
    104 static void test_seccomp_sys_fork_on(void)
    105 {
    106     test_seccomp_passed("on", doit_sys_fork);
    107 }
    108 
    109 static void test_seccomp_sys_fork_off(void)
    110 {
    111     test_seccomp_passed("off", doit_sys_fork);
    112 }
    113 #endif
    114 
    115 static 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 
    127 static void test_seccomp_fork_on_nospawn(void)
    128 {
    129     test_seccomp_killed("on,spawn=deny", doit_fork);
    130 }
    131 
    132 static void test_seccomp_fork_on(void)
    133 {
    134     test_seccomp_passed("on", doit_fork);
    135 }
    136 
    137 static void test_seccomp_fork_off(void)
    138 {
    139     test_seccomp_passed("off", doit_fork);
    140 }
    141 
    142 static void *noop(void *arg)
    143 {
    144     return arg;
    145 }
    146 
    147 static 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 
    160 static void test_seccomp_thread_on(void)
    161 {
    162     test_seccomp_passed("on", doit_thread);
    163 }
    164 
    165 static void test_seccomp_thread_on_nospawn(void)
    166 {
    167     test_seccomp_passed("on,spawn=deny", doit_thread);
    168 }
    169 
    170 static void test_seccomp_thread_off(void)
    171 {
    172     test_seccomp_passed("off", doit_thread);
    173 }
    174 
    175 static int doit_sched(void)
    176 {
    177     struct sched_param param = { .sched_priority = 0 };
    178     return sched_setscheduler(getpid(), SCHED_OTHER, &param);
    179 }
    180 
    181 static void test_seccomp_sched_on_nores(void)
    182 {
    183     test_seccomp_errno("on,resourcecontrol=deny", EPERM, doit_sched);
    184 }
    185 
    186 static void test_seccomp_sched_on(void)
    187 {
    188     test_seccomp_passed("on", doit_sched);
    189 }
    190 
    191 static void test_seccomp_sched_off(void)
    192 {
    193     test_seccomp_passed("off", doit_sched);
    194 }
    195 
    196 static 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 
    228 int 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 }