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