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