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, ¶m); 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 } 270