xref: /openbmc/qemu/tests/unit/test-seccomp.c (revision ed1d873caa93fde443b14369309cdd4366d4ca08)
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é 
test_seccomp_helper(const char * args,bool killed,int errnum,int (* doit)(void))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é 
test_seccomp_killed(const char * args,int (* doit)(void))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é 
test_seccomp_errno(const char * args,int errnum,int (* doit)(void))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é 
test_seccomp_passed(const char * args,int (* doit)(void))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
doit_sys_fork(void)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é 
test_seccomp_sys_fork_on_nospawn(void)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é 
test_seccomp_sys_fork_on(void)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é 
test_seccomp_sys_fork_off(void)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é 
doit_fork(void)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é 
test_seccomp_fork_on_nospawn(void)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é 
test_seccomp_fork_on(void)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é 
test_seccomp_fork_off(void)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é 
noop(void * arg)141d2ea8dacSDaniel P. Berrangé static void *noop(void *arg)
142d2ea8dacSDaniel P. Berrangé {
143d2ea8dacSDaniel P. Berrangé     return arg;
144d2ea8dacSDaniel P. Berrangé }
145d2ea8dacSDaniel P. Berrangé 
doit_thread(void)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é 
test_seccomp_thread_on(void)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é 
test_seccomp_thread_on_nospawn(void)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é 
test_seccomp_thread_off(void)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é 
doit_sched(void)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, &param);
178d2ea8dacSDaniel P. Berrangé }
179d2ea8dacSDaniel P. Berrangé 
test_seccomp_sched_on_nores(void)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é 
test_seccomp_sched_on(void)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é 
test_seccomp_sched_off(void)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é 
can_play_with_seccomp(void)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é 
main(int argc,char ** argv)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