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 <unistd.h> 29d2ea8dacSDaniel P. Berrangé #include <sys/syscall.h> 30d2ea8dacSDaniel P. Berrangé 31d2ea8dacSDaniel P. Berrangé static void test_seccomp_helper(const char *args, bool killed, 32d2ea8dacSDaniel P. Berrangé int errnum, int (*doit)(void)) 33d2ea8dacSDaniel P. Berrangé { 34d2ea8dacSDaniel P. Berrangé if (g_test_subprocess()) { 35d2ea8dacSDaniel P. Berrangé QemuOptsList *olist; 36d2ea8dacSDaniel P. Berrangé QemuOpts *opts; 37d2ea8dacSDaniel P. Berrangé int ret; 38d2ea8dacSDaniel P. Berrangé 39d2ea8dacSDaniel P. Berrangé module_call_init(MODULE_INIT_OPTS); 40d2ea8dacSDaniel P. Berrangé olist = qemu_find_opts("sandbox"); 41d2ea8dacSDaniel P. Berrangé g_assert(olist != NULL); 42d2ea8dacSDaniel P. Berrangé 43d2ea8dacSDaniel P. Berrangé opts = qemu_opts_parse_noisily(olist, args, true); 44d2ea8dacSDaniel P. Berrangé g_assert(opts != NULL); 45d2ea8dacSDaniel P. Berrangé 46d2ea8dacSDaniel P. Berrangé parse_sandbox(NULL, opts, &error_abort); 47d2ea8dacSDaniel P. Berrangé 48d2ea8dacSDaniel P. Berrangé /* Running in a child process */ 49d2ea8dacSDaniel P. Berrangé ret = doit(); 50d2ea8dacSDaniel P. Berrangé 51d2ea8dacSDaniel P. Berrangé if (errnum != 0) { 52d2ea8dacSDaniel P. Berrangé g_assert(ret != 0); 53d2ea8dacSDaniel P. Berrangé g_assert(errno == errnum); 54d2ea8dacSDaniel P. Berrangé } else { 55d2ea8dacSDaniel P. Berrangé g_assert(ret == 0); 56d2ea8dacSDaniel P. Berrangé } 57d2ea8dacSDaniel P. Berrangé 58d2ea8dacSDaniel P. Berrangé _exit(0); 59d2ea8dacSDaniel P. Berrangé } else { 60d2ea8dacSDaniel P. Berrangé /* Running in main test process, spawning the child */ 61d2ea8dacSDaniel P. Berrangé g_test_trap_subprocess(NULL, 0, 0); 62d2ea8dacSDaniel P. Berrangé if (killed) { 63d2ea8dacSDaniel P. Berrangé g_test_trap_assert_failed(); 64d2ea8dacSDaniel P. Berrangé } else { 65d2ea8dacSDaniel P. Berrangé g_test_trap_assert_passed(); 66d2ea8dacSDaniel P. Berrangé } 67d2ea8dacSDaniel P. Berrangé } 68d2ea8dacSDaniel P. Berrangé } 69d2ea8dacSDaniel P. Berrangé 70d2ea8dacSDaniel P. Berrangé 71d2ea8dacSDaniel P. Berrangé static void test_seccomp_killed(const char *args, int (*doit)(void)) 72d2ea8dacSDaniel P. Berrangé { 73d2ea8dacSDaniel P. Berrangé test_seccomp_helper(args, true, 0, doit); 74d2ea8dacSDaniel P. Berrangé } 75d2ea8dacSDaniel P. Berrangé 76d2ea8dacSDaniel P. Berrangé static void test_seccomp_errno(const char *args, int errnum, int (*doit)(void)) 77d2ea8dacSDaniel P. Berrangé { 78d2ea8dacSDaniel P. Berrangé test_seccomp_helper(args, false, errnum, doit); 79d2ea8dacSDaniel P. Berrangé } 80d2ea8dacSDaniel P. Berrangé 81d2ea8dacSDaniel P. Berrangé static void test_seccomp_passed(const char *args, int (*doit)(void)) 82d2ea8dacSDaniel P. Berrangé { 83d2ea8dacSDaniel P. Berrangé test_seccomp_helper(args, false, 0, doit); 84d2ea8dacSDaniel P. Berrangé } 85d2ea8dacSDaniel P. Berrangé 86d2ea8dacSDaniel P. Berrangé #ifdef SYS_fork 87d2ea8dacSDaniel P. Berrangé static int doit_sys_fork(void) 88d2ea8dacSDaniel P. Berrangé { 89d2ea8dacSDaniel P. Berrangé int ret = syscall(SYS_fork); 90d2ea8dacSDaniel P. Berrangé if (ret < 0) { 91d2ea8dacSDaniel P. Berrangé return ret; 92d2ea8dacSDaniel P. Berrangé } 93d2ea8dacSDaniel P. Berrangé if (ret == 0) { 94d2ea8dacSDaniel P. Berrangé _exit(0); 95d2ea8dacSDaniel P. Berrangé } 96d2ea8dacSDaniel P. Berrangé return 0; 97d2ea8dacSDaniel P. Berrangé } 98d2ea8dacSDaniel P. Berrangé 99d2ea8dacSDaniel P. Berrangé static void test_seccomp_sys_fork_on_nospawn(void) 100d2ea8dacSDaniel P. Berrangé { 101d2ea8dacSDaniel P. Berrangé test_seccomp_killed("on,spawn=deny", doit_sys_fork); 102d2ea8dacSDaniel P. Berrangé } 103d2ea8dacSDaniel P. Berrangé 104d2ea8dacSDaniel P. Berrangé static void test_seccomp_sys_fork_on(void) 105d2ea8dacSDaniel P. Berrangé { 106d2ea8dacSDaniel P. Berrangé test_seccomp_passed("on", doit_sys_fork); 107d2ea8dacSDaniel P. Berrangé } 108d2ea8dacSDaniel P. Berrangé 109d2ea8dacSDaniel P. Berrangé static void test_seccomp_sys_fork_off(void) 110d2ea8dacSDaniel P. Berrangé { 111d2ea8dacSDaniel P. Berrangé test_seccomp_passed("off", doit_sys_fork); 112d2ea8dacSDaniel P. Berrangé } 113d2ea8dacSDaniel P. Berrangé #endif 114d2ea8dacSDaniel P. Berrangé 115d2ea8dacSDaniel P. Berrangé static int doit_fork(void) 116d2ea8dacSDaniel P. Berrangé { 117d2ea8dacSDaniel P. Berrangé int ret = fork(); 118d2ea8dacSDaniel P. Berrangé if (ret < 0) { 119d2ea8dacSDaniel P. Berrangé return ret; 120d2ea8dacSDaniel P. Berrangé } 121d2ea8dacSDaniel P. Berrangé if (ret == 0) { 122d2ea8dacSDaniel P. Berrangé _exit(0); 123d2ea8dacSDaniel P. Berrangé } 124d2ea8dacSDaniel P. Berrangé return 0; 125d2ea8dacSDaniel P. Berrangé } 126d2ea8dacSDaniel P. Berrangé 127d2ea8dacSDaniel P. Berrangé static void test_seccomp_fork_on_nospawn(void) 128d2ea8dacSDaniel P. Berrangé { 129*5a2f693fSDaniel P. Berrangé test_seccomp_killed("on,spawn=deny", doit_fork); 130d2ea8dacSDaniel P. Berrangé } 131d2ea8dacSDaniel P. Berrangé 132d2ea8dacSDaniel P. Berrangé static void test_seccomp_fork_on(void) 133d2ea8dacSDaniel P. Berrangé { 134d2ea8dacSDaniel P. Berrangé test_seccomp_passed("on", doit_fork); 135d2ea8dacSDaniel P. Berrangé } 136d2ea8dacSDaniel P. Berrangé 137d2ea8dacSDaniel P. Berrangé static void test_seccomp_fork_off(void) 138d2ea8dacSDaniel P. Berrangé { 139d2ea8dacSDaniel P. Berrangé test_seccomp_passed("off", doit_fork); 140d2ea8dacSDaniel P. Berrangé } 141d2ea8dacSDaniel P. Berrangé 142d2ea8dacSDaniel P. Berrangé static void *noop(void *arg) 143d2ea8dacSDaniel P. Berrangé { 144d2ea8dacSDaniel P. Berrangé return arg; 145d2ea8dacSDaniel P. Berrangé } 146d2ea8dacSDaniel P. Berrangé 147d2ea8dacSDaniel P. Berrangé static int doit_thread(void) 148d2ea8dacSDaniel P. Berrangé { 149d2ea8dacSDaniel P. Berrangé pthread_t th; 150d2ea8dacSDaniel P. Berrangé int ret = pthread_create(&th, NULL, noop, NULL); 151d2ea8dacSDaniel P. Berrangé if (ret != 0) { 152d2ea8dacSDaniel P. Berrangé errno = ret; 153d2ea8dacSDaniel P. Berrangé return -1; 154d2ea8dacSDaniel P. Berrangé } else { 155d2ea8dacSDaniel P. Berrangé pthread_join(th, NULL); 156d2ea8dacSDaniel P. Berrangé return 0; 157d2ea8dacSDaniel P. Berrangé } 158d2ea8dacSDaniel P. Berrangé } 159d2ea8dacSDaniel P. Berrangé 160d2ea8dacSDaniel P. Berrangé static void test_seccomp_thread_on(void) 161d2ea8dacSDaniel P. Berrangé { 162d2ea8dacSDaniel P. Berrangé test_seccomp_passed("on", doit_thread); 163d2ea8dacSDaniel P. Berrangé } 164d2ea8dacSDaniel P. Berrangé 165d2ea8dacSDaniel P. Berrangé static void test_seccomp_thread_on_nospawn(void) 166d2ea8dacSDaniel P. Berrangé { 167d2ea8dacSDaniel P. Berrangé test_seccomp_passed("on,spawn=deny", doit_thread); 168d2ea8dacSDaniel P. Berrangé } 169d2ea8dacSDaniel P. Berrangé 170d2ea8dacSDaniel P. Berrangé static void test_seccomp_thread_off(void) 171d2ea8dacSDaniel P. Berrangé { 172d2ea8dacSDaniel P. Berrangé test_seccomp_passed("off", doit_thread); 173d2ea8dacSDaniel P. Berrangé } 174d2ea8dacSDaniel P. Berrangé 175d2ea8dacSDaniel P. Berrangé static int doit_sched(void) 176d2ea8dacSDaniel P. Berrangé { 177d2ea8dacSDaniel P. Berrangé struct sched_param param = { .sched_priority = 0 }; 178d2ea8dacSDaniel P. Berrangé return sched_setscheduler(getpid(), SCHED_OTHER, ¶m); 179d2ea8dacSDaniel P. Berrangé } 180d2ea8dacSDaniel P. Berrangé 181d2ea8dacSDaniel P. Berrangé static void test_seccomp_sched_on_nores(void) 182d2ea8dacSDaniel P. Berrangé { 183d2ea8dacSDaniel P. Berrangé test_seccomp_errno("on,resourcecontrol=deny", EPERM, doit_sched); 184d2ea8dacSDaniel P. Berrangé } 185d2ea8dacSDaniel P. Berrangé 186d2ea8dacSDaniel P. Berrangé static void test_seccomp_sched_on(void) 187d2ea8dacSDaniel P. Berrangé { 188d2ea8dacSDaniel P. Berrangé test_seccomp_passed("on", doit_sched); 189d2ea8dacSDaniel P. Berrangé } 190d2ea8dacSDaniel P. Berrangé 191d2ea8dacSDaniel P. Berrangé static void test_seccomp_sched_off(void) 192d2ea8dacSDaniel P. Berrangé { 193d2ea8dacSDaniel P. Berrangé test_seccomp_passed("off", doit_sched); 194d2ea8dacSDaniel P. Berrangé } 195d2ea8dacSDaniel P. Berrangé 196d2ea8dacSDaniel P. Berrangé static bool can_play_with_seccomp(void) 197d2ea8dacSDaniel P. Berrangé { 198d2ea8dacSDaniel P. Berrangé g_autofree char *status = NULL; 199d2ea8dacSDaniel P. Berrangé g_auto(GStrv) lines = NULL; 200d2ea8dacSDaniel P. Berrangé size_t i; 201d2ea8dacSDaniel P. Berrangé 202d2ea8dacSDaniel P. Berrangé if (!g_file_get_contents("/proc/self/status", &status, NULL, NULL)) { 203d2ea8dacSDaniel P. Berrangé return false; 204d2ea8dacSDaniel P. Berrangé } 205d2ea8dacSDaniel P. Berrangé 206d2ea8dacSDaniel P. Berrangé lines = g_strsplit(status, "\n", 0); 207d2ea8dacSDaniel P. Berrangé 208d2ea8dacSDaniel P. Berrangé for (i = 0; lines[i] != NULL; i++) { 209d2ea8dacSDaniel P. Berrangé if (g_str_has_prefix(lines[i], "Seccomp:")) { 210d2ea8dacSDaniel P. Berrangé /* 211d2ea8dacSDaniel P. Berrangé * "Seccomp: 1" or "Seccomp: 2" indicate we're already 212d2ea8dacSDaniel P. Berrangé * confined, probably as we're inside a container. In 213d2ea8dacSDaniel P. Berrangé * this case our tests might get unexpected results, 214d2ea8dacSDaniel P. Berrangé * so we can't run reliably 215d2ea8dacSDaniel P. Berrangé */ 216d2ea8dacSDaniel P. Berrangé if (!strchr(lines[i], '0')) { 217d2ea8dacSDaniel P. Berrangé return false; 218d2ea8dacSDaniel P. Berrangé } 219d2ea8dacSDaniel P. Berrangé 220d2ea8dacSDaniel P. Berrangé return true; 221d2ea8dacSDaniel P. Berrangé } 222d2ea8dacSDaniel P. Berrangé } 223d2ea8dacSDaniel P. Berrangé 224d2ea8dacSDaniel P. Berrangé /* Doesn't look like seccomp is enabled in the kernel */ 225d2ea8dacSDaniel P. Berrangé return false; 226d2ea8dacSDaniel P. Berrangé } 227d2ea8dacSDaniel P. Berrangé 228d2ea8dacSDaniel P. Berrangé int main(int argc, char **argv) 229d2ea8dacSDaniel P. Berrangé { 230d2ea8dacSDaniel P. Berrangé g_test_init(&argc, &argv, NULL); 231d2ea8dacSDaniel P. Berrangé if (can_play_with_seccomp()) { 232d2ea8dacSDaniel P. Berrangé #ifdef SYS_fork 233d2ea8dacSDaniel P. Berrangé g_test_add_func("/softmmu/seccomp/sys-fork/on", 234d2ea8dacSDaniel P. Berrangé test_seccomp_sys_fork_on); 235d2ea8dacSDaniel P. Berrangé g_test_add_func("/softmmu/seccomp/sys-fork/on-nospawn", 236d2ea8dacSDaniel P. Berrangé test_seccomp_sys_fork_on_nospawn); 237d2ea8dacSDaniel P. Berrangé g_test_add_func("/softmmu/seccomp/sys-fork/off", 238d2ea8dacSDaniel P. Berrangé test_seccomp_sys_fork_off); 239d2ea8dacSDaniel P. Berrangé #endif 240d2ea8dacSDaniel P. Berrangé 241d2ea8dacSDaniel P. Berrangé g_test_add_func("/softmmu/seccomp/fork/on", 242d2ea8dacSDaniel P. Berrangé test_seccomp_fork_on); 243d2ea8dacSDaniel P. Berrangé g_test_add_func("/softmmu/seccomp/fork/on-nospawn", 244d2ea8dacSDaniel P. Berrangé test_seccomp_fork_on_nospawn); 245d2ea8dacSDaniel P. Berrangé g_test_add_func("/softmmu/seccomp/fork/off", 246d2ea8dacSDaniel P. Berrangé test_seccomp_fork_off); 247d2ea8dacSDaniel P. Berrangé 248d2ea8dacSDaniel P. Berrangé g_test_add_func("/softmmu/seccomp/thread/on", 249d2ea8dacSDaniel P. Berrangé test_seccomp_thread_on); 250d2ea8dacSDaniel P. Berrangé g_test_add_func("/softmmu/seccomp/thread/on-nospawn", 251d2ea8dacSDaniel P. Berrangé test_seccomp_thread_on_nospawn); 252d2ea8dacSDaniel P. Berrangé g_test_add_func("/softmmu/seccomp/thread/off", 253d2ea8dacSDaniel P. Berrangé test_seccomp_thread_off); 254d2ea8dacSDaniel P. Berrangé 255d2ea8dacSDaniel P. Berrangé if (doit_sched() == 0) { 256d2ea8dacSDaniel P. Berrangé /* 257d2ea8dacSDaniel P. Berrangé * musl doesn't impl sched_setscheduler, hence 258d2ea8dacSDaniel P. Berrangé * we check above if it works first 259d2ea8dacSDaniel P. Berrangé */ 260d2ea8dacSDaniel P. Berrangé g_test_add_func("/softmmu/seccomp/sched/on", 261d2ea8dacSDaniel P. Berrangé test_seccomp_sched_on); 262d2ea8dacSDaniel P. Berrangé g_test_add_func("/softmmu/seccomp/sched/on-nores", 263d2ea8dacSDaniel P. Berrangé test_seccomp_sched_on_nores); 264d2ea8dacSDaniel P. Berrangé g_test_add_func("/softmmu/seccomp/sched/off", 265d2ea8dacSDaniel P. Berrangé test_seccomp_sched_off); 266d2ea8dacSDaniel P. Berrangé } 267d2ea8dacSDaniel P. Berrangé } 268d2ea8dacSDaniel P. Berrangé return g_test_run(); 269d2ea8dacSDaniel P. Berrangé } 270