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