blob: a9ea1b6404cfd589339f64a72cea2bef331923a8 [file] [log] [blame]
Elly Jonescd7a9042011-07-22 13:56:51 -04001/* Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
2 * Use of this source code is governed by a BSD-style license that can be
Will Drewry32ac9f52011-08-18 21:36:27 -05003 * found in the LICENSE file.
4 */
Elly Jonescd7a9042011-07-22 13:56:51 -04005
6#define _BSD_SOURCE
7#define _GNU_SOURCE
Will Drewry32ac9f52011-08-18 21:36:27 -05008#include <ctype.h>
Elly Jonescd7a9042011-07-22 13:56:51 -04009#include <errno.h>
10#include <grp.h>
11#include <inttypes.h>
12#include <linux/capability.h>
13#include <linux/securebits.h>
14#include <pwd.h>
15#include <sched.h>
16#include <signal.h>
Will Drewry2f54b6a2011-09-16 13:45:31 -050017#include <stdarg.h>
Elly Jonescd7a9042011-07-22 13:56:51 -040018#include <stdio.h>
19#include <stdlib.h>
20#include <string.h>
21#include <syscall.h>
22#include <sys/capability.h>
23#include <sys/mount.h>
24#include <sys/prctl.h>
25#include <sys/wait.h>
26#include <syslog.h>
27#include <unistd.h>
28
29#include "libminijail.h"
Will Drewry32ac9f52011-08-18 21:36:27 -050030#include "libsyscalls.h"
Elly Jonescd7a9042011-07-22 13:56:51 -040031#include "libminijail-private.h"
32
Will Drewry32ac9f52011-08-18 21:36:27 -050033/* Until these are reliably available in linux/prctl.h */
34#ifndef PR_SET_SECCOMP_FILTER
35# define PR_SECCOMP_FILTER_SYSCALL 0
36# define PR_SECCOMP_FILTER_EVENT 1
37# define PR_GET_SECCOMP_FILTER 35
38# define PR_SET_SECCOMP_FILTER 36
39# define PR_CLEAR_SECCOMP_FILTER 37
40#endif
41
42struct seccomp_filter {
43 int nr;
44 char *filter;
45 struct seccomp_filter *next, *prev;
46};
47
Elly Jonescd7a9042011-07-22 13:56:51 -040048struct minijail {
49 struct {
50 int uid : 1;
51 int gid : 1;
52 int caps : 1;
53 int vfs : 1;
54 int pids : 1;
55 int seccomp : 1;
56 int readonly : 1;
57 int usergroups : 1;
58 int ptrace : 1;
Will Drewry32ac9f52011-08-18 21:36:27 -050059 int seccomp_filter : 1;
Elly Jonescd7a9042011-07-22 13:56:51 -040060 } flags;
61 uid_t uid;
62 gid_t gid;
63 gid_t usergid;
64 const char *user;
65 uint64_t caps;
66 pid_t initpid;
Will Drewry32ac9f52011-08-18 21:36:27 -050067 struct seccomp_filter *filters;
Elly Jonescd7a9042011-07-22 13:56:51 -040068};
69
Will Drewry32ac9f52011-08-18 21:36:27 -050070#define die(_msg, ...) do { \
71 syslog(LOG_ERR, "libminijail: " _msg, ## __VA_ARGS__); \
72 abort(); \
73} while (0)
Elly Jonescd7a9042011-07-22 13:56:51 -040074
Will Drewry32ac9f52011-08-18 21:36:27 -050075#define pdie(_msg, ...) \
76 die(_msg ": %s", ## __VA_ARGS__, strerror(errno))
77
78#define warn(_msg, ...) \
79 syslog(LOG_WARNING, "libminijail: " _msg, ## __VA_ARGS__)
Elly Jonescd7a9042011-07-22 13:56:51 -040080
81struct minijail *minijail_new(void) {
82 struct minijail *j = malloc(sizeof(*j));
83 if (j)
84 memset(j, 0, sizeof(*j));
85 return j;
86}
87
88void minijail_change_uid(struct minijail *j, uid_t uid) {
89 if (uid == 0)
90 die("useless change to uid 0");
91 j->uid = uid;
92 j->flags.uid = 1;
93}
94
95void minijail_change_gid(struct minijail *j, gid_t gid) {
96 if (gid == 0)
97 die("useless change to gid 0");
98 j->gid = gid;
99 j->flags.gid = 1;
100}
101
102int minijail_change_user(struct minijail *j, const char *user) {
103 /* In principle this should use getpwnam(), but:
104 * 1) getpwnam_r() isn't actually reentrant anyway, since it uses a
105 * statically-allocated file descriptor internally
106 * 2) fgetpwnam() (by analogy with fgetpwent) would solve (1) except that it
107 * doesn't exist
108 * 3) sysconf() (see getpwnam_r(3)) is allowed to return a size that is not
109 * large enough, which means having to loop on growing the buffer we pass
110 * in
111 */
112 struct passwd *pw = getpwnam(user);
113 if (!pw)
114 return errno;
115 minijail_change_uid(j, pw->pw_uid);
116 j->user = user;
117 j->usergid = pw->pw_gid;
118 return 0;
119}
120
121int minijail_change_group(struct minijail *j, const char *group) {
122 /* In principle this should use getgrnam(), but:
123 * 1) getgrnam_r() isn't actually reentrant anyway, since it uses a
124 * statically-allocated file descriptor internally
125 * 2) fgetgrnam() (by analogy with fgetgrent) would solve (1) except that it
126 * doesn't exist
127 * 3) sysconf() (see getgrnam_r(3)) is allowed to return a size that is not
128 * large enough, which means having to loop on growing the buffer we pass
129 * in
130 */
131 struct group *gr = getgrnam(group);
132 if (!gr)
133 return errno;
134 minijail_change_gid(j, gr->gr_gid);
135 return 0;
136}
137
138void minijail_use_seccomp(struct minijail *j) {
139 j->flags.seccomp = 1;
140}
141
Will Drewry32ac9f52011-08-18 21:36:27 -0500142void minijail_use_seccomp_filter(struct minijail *j) {
143 j->flags.seccomp_filter = 1;
144}
145
Elly Jonescd7a9042011-07-22 13:56:51 -0400146void minijail_use_caps(struct minijail *j, uint64_t capmask) {
147 j->caps = capmask;
148 j->flags.caps = 1;
149}
150
151void minijail_namespace_vfs(struct minijail *j) {
152 j->flags.vfs = 1;
153}
154
155void minijail_namespace_pids(struct minijail *j) {
156 j->flags.pids = 1;
157}
158
159void minijail_remount_readonly(struct minijail *j) {
160 j->flags.vfs = 1;
161 j->flags.readonly = 1;
162}
163
164void minijail_inherit_usergroups(struct minijail *j) {
165 j->flags.usergroups = 1;
166}
167
168void minijail_disable_ptrace(struct minijail *j) {
169 j->flags.ptrace = 1;
170}
171
Will Drewry32ac9f52011-08-18 21:36:27 -0500172int minijail_add_seccomp_filter(struct minijail *j, int nr,
173 const char *filter) {
174 struct seccomp_filter *sf;
175 if (!filter || nr < 0)
176 return -EINVAL;
177
178 sf = malloc(sizeof(*sf));
179 if (!sf)
180 return -ENOMEM;
181 sf->nr = nr;
182 sf->filter = strndup(filter, MINIJAIL_MAX_SECCOMP_FILTER_LINE);
183 if (!sf->filter) {
184 free(sf);
185 return -ENOMEM;
186 }
187
188 if (!j->filters) {
189 j->filters = sf;
190 sf->next = sf;
191 sf->prev = sf;
192 return 0;
193 }
194 sf->next = j->filters;
195 sf->prev = j->filters->prev;
196 sf->prev->next = sf;
197 j->filters->prev = sf;
198 return 0;
199}
200
201int minijail_lookup_syscall(const char *name) {
202 const struct syscall_entry *entry = syscall_table;
203 for (; entry->name && entry->nr >= 0; ++entry)
204 if (!strcmp(entry->name, name))
205 return entry->nr;
206 return -1;
207}
208
209static char *strip(char *s) {
210 char *end;
211 while (*s && isblank(*s))
212 s++;
213 end = s + strlen(s) - 1;
214 while (*end && (isblank(*end) || *end == '\n'))
215 end--;
216 *(end+1) = '\0';
217 return s;
218}
219
220void minijail_parse_seccomp_filters(struct minijail *j, const char *path) {
221 FILE *file = fopen(path, "r");
222 char line[MINIJAIL_MAX_SECCOMP_FILTER_LINE];
223 int count = 1;
224 if (!file)
225 pdie("failed to open seccomp filters file");
226
227 /* Format is simple:
228 * syscall_name<COLON><FILTER STRING>[\n|EOF]
229 * #...comment...
230 * <empty line?
231 */
232 while (fgets(line, sizeof(line), file)) {
233 char *filter = line;
234 char *name = strsep(&filter, ":");
235 char *name_end = NULL;
236 int nr = -1;
237
238 if (!name)
239 die("invalid filter on line %d", count);
240
241 name = strip(name);
242
243 if (!filter) {
244 if (strlen(name))
245 die("invalid filter on line %d", count);
246 /* Allow empty lines */
247 continue;
248 }
249
250 /* Allow comment lines */
251 if (*name == '#')
252 continue;
253
254 filter = strip(filter);
255
256 /* Take direct syscall numbers */
257 nr = strtol(name, &name_end, 0);
258 /* Or fail-over to using names */
259 if (*name_end != '\0')
260 nr = minijail_lookup_syscall(name);
261 if (nr < 0)
262 die("syscall '%s' unknown", name);
263
264 if (minijail_add_seccomp_filter(j, nr, filter))
265 pdie("failed to add filter for syscall '%s'", name);
266 }
267 fclose(file);
268}
269
Elly Jonescd7a9042011-07-22 13:56:51 -0400270static int remount_readonly(void) {
271 const char *kProcPath = "/proc";
272 const unsigned int kSafeFlags = MS_NODEV | MS_NOEXEC | MS_NOSUID;
273 /* Right now, we're holding a reference to our parent's old mount of /proc in
274 * our namespace, which means using MS_REMOUNT here would mutate our parent's
275 * mount as well, even though we're in a VFS namespace (!). Instead, remove
276 * their mount from our namespace and make our own. */
277 if (umount(kProcPath))
278 return errno;
279 if (mount("", kProcPath, "proc", kSafeFlags | MS_RDONLY, ""))
280 return errno;
281 return 0;
282}
283
284static void drop_caps(const struct minijail *j) {
285 cap_t caps = cap_get_proc();
286 cap_value_t raise_flag[1];
287 unsigned int i;
288 if (!caps)
289 die("can't get process caps");
290 if (cap_clear_flag(caps, CAP_INHERITABLE))
291 die("can't clear inheritable caps");
292 if (cap_clear_flag(caps, CAP_EFFECTIVE))
293 die("can't clear effective caps");
294 if (cap_clear_flag(caps, CAP_PERMITTED))
295 die("can't clear permitted caps");
296 for (i = 0; i < sizeof(j->caps) * 8 && cap_valid((int)i); ++i) {
297 if (i != CAP_SETPCAP && !(j->caps & (1 << i)))
298 continue;
299 raise_flag[0] = i;
300 if (cap_set_flag(caps, CAP_EFFECTIVE, 1, raise_flag, CAP_SET))
301 die("can't add effective cap");
302 if (cap_set_flag(caps, CAP_PERMITTED, 1, raise_flag, CAP_SET))
303 die("can't add permitted cap");
304 if (cap_set_flag(caps, CAP_INHERITABLE, 1, raise_flag, CAP_SET))
305 die("can't add inheritable cap");
306 }
307 if (cap_set_proc(caps))
308 die("can't apply cleaned capset");
309 cap_free(caps);
310 for (i = 0; i < sizeof(j->caps) * 8 && cap_valid((int)i); ++i) {
311 if (j->caps & (1 << i))
312 continue;
313 if (prctl(PR_CAPBSET_DROP, i))
314 pdie("prctl(PR_CAPBSET_DROP)");
315 }
316}
317
Will Drewry32ac9f52011-08-18 21:36:27 -0500318static int setup_seccomp_filters(const struct minijail *j) {
319 const struct seccomp_filter *sf = j->filters;
320 int ret = 0;
321 int broaden = 0;
322
323 /* No filters installed isn't necessarily an error. */
324 if (!sf)
325 return ret;
326
327 do {
328 errno = 0;
329 ret = prctl(PR_SET_SECCOMP_FILTER, PR_SECCOMP_FILTER_SYSCALL,
330 sf->nr, broaden ? "1" : sf->filter);
331 if (ret) {
332 switch (errno) {
333 case ENOSYS:
334 /* TODO(wad) make this a config option */
335 if (broaden)
336 die("CONFIG_SECCOMP_FILTER is not supported by your kernel");
337 warn("missing CONFIG_FTRACE_SYSCALLS; relaxing the filter for %d",
338 sf->nr);
339 broaden = 1;
340 continue;
341 case E2BIG:
342 warn("seccomp filter too long: %d", sf->nr);
343 pdie("filter too long");
344 case ENOSPC:
345 pdie("too many seccomp filters");
346 case EPERM:
347 warn("syscall filter disallowed for %d", sf->nr);
348 pdie("failed to install seccomp filter");
349 case EINVAL:
350 warn("seccomp filter or call method is invalid. %d:'%s'",
351 sf->nr, sf->filter);
352 default:
353 pdie("failed to install seccomp filter");
354 }
355 }
356 sf = sf->next;
357 broaden = 0;
358 } while (sf != j->filters);
359 return ret;
360}
361
Elly Jonescd7a9042011-07-22 13:56:51 -0400362void minijail_enter(const struct minijail *j) {
Will Drewry32ac9f52011-08-18 21:36:27 -0500363 int ret;
Elly Jonescd7a9042011-07-22 13:56:51 -0400364 if (j->flags.pids)
365 die("tried to enter a pid-namespaced jail; try minijail_run()?");
366
Will Drewry32ac9f52011-08-18 21:36:27 -0500367 ret = setup_seccomp_filters(j);
368 if (j->flags.seccomp_filter && ret)
369 die("failed to configure seccomp filters");
370
Elly Jonescd7a9042011-07-22 13:56:51 -0400371 if (j->flags.usergroups && !j->user)
372 die("usergroup inheritance without username");
373
374 /* We can't recover from failures if we've dropped privileges partially,
375 * so we don't even try. If any of our operations fail, we abort() the
376 * entire process. */
377 if (j->flags.vfs && unshare(CLONE_NEWNS))
378 pdie("unshare");
379
380 if (j->flags.readonly && remount_readonly())
381 pdie("remount");
382
383 if (j->flags.caps) {
384 /* POSIX capabilities are a bit tricky. If we drop our capability to change
385 * uids, our attempt to use setuid() below will fail. Hang on to root caps
386 * across setuid(), then lock securebits. */
387 if (prctl(PR_SET_KEEPCAPS, 1))
388 pdie("prctl(PR_SET_KEEPCAPS)");
389 if (prctl(PR_SET_SECUREBITS, SECURE_ALL_BITS | SECURE_ALL_LOCKS))
390 pdie("prctl(PR_SET_SECUREBITS)");
391 }
392
Will Drewry32ac9f52011-08-18 21:36:27 -0500393 if (j->flags.usergroups && initgroups(j->user, j->usergid)) {
Elly Jonescd7a9042011-07-22 13:56:51 -0400394 pdie("initgroups");
Will Drewry32ac9f52011-08-18 21:36:27 -0500395 } else if (!j->flags.usergroups && setgroups(0, NULL)) {
Elly Jonescd7a9042011-07-22 13:56:51 -0400396 pdie("setgroups");
Will Drewry32ac9f52011-08-18 21:36:27 -0500397 }
Elly Jonescd7a9042011-07-22 13:56:51 -0400398
399 if (j->flags.gid && setresgid(j->gid, j->gid, j->gid))
400 pdie("setresgid");
401
402 if (j->flags.uid && setresuid(j->uid, j->uid, j->uid))
403 pdie("setresuid");
404
405 if (j->flags.caps)
406 drop_caps(j);
407
408 /* seccomp has to come last since it cuts off all the other
409 * privilege-dropping syscalls :) */
Will Drewry32ac9f52011-08-18 21:36:27 -0500410 if (j->flags.seccomp_filter && prctl(PR_SET_SECCOMP, 13))
411 pdie("prctl(PR_SET_SECCOMP, 13)");
412
Elly Jonescd7a9042011-07-22 13:56:51 -0400413 if (j->flags.seccomp && prctl(PR_SET_SECCOMP, 1))
414 pdie("prctl(PR_SET_SECCOMP)");
415}
416
417static int init_exitstatus = 0;
418
419static void init_term(int __attribute__((unused)) sig) {
420 _exit(init_exitstatus);
421}
422
423static int init(pid_t rootpid) {
424 pid_t pid;
425 int status;
426 signal(SIGTERM, init_term); /* so that we exit with the right status */
427 while ((pid = wait(&status)) > 0) {
428 /* This loop will only end when either there are no processes left inside
429 * our pid namespace or we get a signal. */
430 if (pid == rootpid)
431 init_exitstatus = status;
432 }
433 if (!WIFEXITED(init_exitstatus))
434 _exit(MINIJAIL_ERR_INIT);
435 _exit(WEXITSTATUS(init_exitstatus));
436}
437
Will Drewry2f54b6a2011-09-16 13:45:31 -0500438static int write_cmd(int fd, const char *fmt, ...) {
439 char cmd[MINIJAIL_MAX_ARG_LINE];
440 ssize_t written;
441 int r;
442 va_list ap;
443
444 va_start(ap, fmt);
445 r = vsnprintf(cmd, sizeof(cmd), fmt, ap);
446 va_end(ap);
447
448 if (r <= 0)
449 return -EFAULT;
450 if ((size_t) r >= sizeof(cmd))
451 return -E2BIG;
452
453 written = write(fd, cmd, r);
454 if (written != r)
455 return -EFAULT;
456 return 0;
457}
458
Elly Jonescd7a9042011-07-22 13:56:51 -0400459/** @brief Move any commands that need to be done post-exec into an environment
460 * variable
461 * @param j Jail to move commands from.
462 *
463 * Serializes post-exec() commands into a string, removes them from the jail,
464 * and adds them to the environment; they will be deserialized later (see
465 * __minijail_preloaded) and executed inside the execve()'d process.
466 */
Will Drewry2f54b6a2011-09-16 13:45:31 -0500467static int send_commands_to_child(struct minijail *j, int fd) {
468 if (j->flags.caps && write_cmd(fd, "caps=%" PRIx64 "\n", j->caps))
469 return -EFAULT;
470 if (j->flags.uid && write_cmd(fd, "uid=%d\n", j->uid))
471 return -EFAULT;
472 if (j->flags.ptrace && write_cmd(fd, "ptrace\n"))
473 return -EFAULT;
474 if (j->flags.seccomp && write_cmd(fd, "seccomp\n"))
475 return -EFAULT;
Elly Jonescd7a9042011-07-22 13:56:51 -0400476
Will Drewry32ac9f52011-08-18 21:36:27 -0500477 if (j->flags.seccomp_filter)
478 warn("TODO(wad) seccomp_filter is installed in the parent which "
479 "requires overly permissive rules for execve(2)ing.");
480
Will Drewry2f54b6a2011-09-16 13:45:31 -0500481 return write_cmd(fd, "eom\n");
482}
Elly Jonescd7a9042011-07-22 13:56:51 -0400483
Will Drewry2f54b6a2011-09-16 13:45:31 -0500484static int setup_preload(void) {
485 char *oldenv = getenv(kLdPreloadEnvVar) ? : "";
486 char *newenv = malloc(strlen(oldenv) + 2 + strlen(PRELOADPATH));
487 if (!newenv)
Elly Jonescd7a9042011-07-22 13:56:51 -0400488 return -ENOMEM;
Elly Jonescd7a9042011-07-22 13:56:51 -0400489
490 /* Only insert a separating space if we have something to separate... */
491 sprintf(newenv, "%s%s%s", oldenv, strlen(oldenv) ? " " : "", PRELOADPATH);
492
493 /* setenv() makes a copy of the string we give it */
Ben Chan541c7e52011-08-26 14:55:53 -0700494 setenv(kLdPreloadEnvVar, newenv, 1);
Elly Jonescd7a9042011-07-22 13:56:51 -0400495 free(newenv);
Elly Jonescd7a9042011-07-22 13:56:51 -0400496 return 0;
497}
498
499int minijail_run(struct minijail *j, const char *filename, char *const argv[]) {
500 unsigned int pidns = j->flags.pids ? CLONE_NEWPID : 0;
Ben Chan541c7e52011-08-26 14:55:53 -0700501 char *oldenv, *oldenv_copy = NULL;
Elly Jonescd7a9042011-07-22 13:56:51 -0400502 pid_t r;
Will Drewry2f54b6a2011-09-16 13:45:31 -0500503 int pipe_fds[2];
504 char fd_buf[11];
Ben Chan541c7e52011-08-26 14:55:53 -0700505
506 oldenv = getenv(kLdPreloadEnvVar);
507 if (oldenv) {
508 oldenv_copy = strdup(oldenv);
509 if (!oldenv_copy)
510 return -ENOMEM;
511 }
Will Drewry2f54b6a2011-09-16 13:45:31 -0500512 r = setup_preload();
513 if (r)
Elly Jonescd7a9042011-07-22 13:56:51 -0400514 return r;
Will Drewry2f54b6a2011-09-16 13:45:31 -0500515
516 /* Before we fork(2) and execve(2) the child process, we need to open
517 * a pipe(2) to send the minijail configuration over.
518 */
519 r = pipe(pipe_fds);
520 if (r)
521 return r;
522 r = snprintf(fd_buf, sizeof(fd_buf), "%d", pipe_fds[0]);
523 if (r <= 0)
524 return -EINVAL;
525 setenv(kFdEnvVar, fd_buf, 1);
Elly Jonescd7a9042011-07-22 13:56:51 -0400526
527 r = syscall(SYS_clone, pidns | SIGCHLD, NULL);
528 if (r > 0) {
Ben Chan541c7e52011-08-26 14:55:53 -0700529 if (oldenv_copy) {
530 setenv(kLdPreloadEnvVar, oldenv_copy, 1);
531 free(oldenv_copy);
532 } else {
533 unsetenv(kLdPreloadEnvVar);
534 }
Will Drewry2f54b6a2011-09-16 13:45:31 -0500535 unsetenv(kFdEnvVar);
Elly Jonescd7a9042011-07-22 13:56:51 -0400536 j->initpid = r;
Will Drewry2f54b6a2011-09-16 13:45:31 -0500537 close(pipe_fds[0]);
538 r = send_commands_to_child(j, pipe_fds[1]);
539 close(pipe_fds[1]);
540 if (r) {
541 kill(j->initpid, SIGKILL);
542 die("failed to send marshalled minijail");
543 }
Elly Jonescd7a9042011-07-22 13:56:51 -0400544 return 0;
545 }
Ben Chan541c7e52011-08-26 14:55:53 -0700546
547 free(oldenv_copy);
548
Elly Jonescd7a9042011-07-22 13:56:51 -0400549 if (r < 0)
550 return r;
551
Will Drewry2f54b6a2011-09-16 13:45:31 -0500552 j->flags.uid = 0;
553 /* TODO(wad) gid should be sent over preload and not done in advance.
554 * j->flags.gid = 0;
555 */
556 j->flags.usergroups = 0;
557 j->flags.caps = 0;
558 j->flags.ptrace = 0;
559 j->flags.seccomp = 0;
560
Elly Jonescd7a9042011-07-22 13:56:51 -0400561 j->flags.pids = 0;
562
563 /* Jail this process and its descendants... */
564 minijail_enter(j);
565
566 if (pidns) {
567 /* pid namespace: this process will become init inside the new namespace, so
568 * fork off a child to actually run the program (we don't want all programs
569 * we might exec to have to know how to be init). */
570 r = fork();
571 if (r < 0)
572 _exit(r);
573 else if (r > 0)
574 init(r); /* never returns */
575 }
576
Will Drewry2f54b6a2011-09-16 13:45:31 -0500577
Elly Jonescd7a9042011-07-22 13:56:51 -0400578 /* If we aren't pid-namespaced:
579 * calling process
580 * -> execve()-ing process
581 * If we are:
582 * calling process
583 * -> init()-ing process
584 * -> execve()-ing process
585 */
586 _exit(execve(filename, argv, environ));
587}
588
589int minijail_kill(struct minijail *j) {
590 int st;
591 if (kill(j->initpid, SIGTERM))
592 return errno;
593 if (waitpid(j->initpid, &st, 0) < 0)
594 return errno;
595 return st;
596}
597
598int minijail_wait(struct minijail *j) {
599 int st;
600 if (waitpid(j->initpid, &st, 0) < 0)
601 return errno;
602 if (!WIFEXITED(st))
603 return MINIJAIL_ERR_JAIL;
604 return WEXITSTATUS(st);
605}
606
607void minijail_destroy(struct minijail *j) {
Will Drewry32ac9f52011-08-18 21:36:27 -0500608 struct seccomp_filter *f = j->filters;
609 /* Unlink the tail and head */
610 if (f)
611 f->prev->next = NULL;
612 while (f) {
613 struct seccomp_filter *next = f->next;
614 free(f->filter);
615 free(f);
616 f = next;
617 }
Elly Jonescd7a9042011-07-22 13:56:51 -0400618 free(j);
619}