xref: /qemu/tests/unit/test-seccomp.c (revision 5a2f693f07a1e93ada5277b2fb1530b2698be0fa)
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, &param);
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