diff options
author | android-build-team Robot <android-build-team-robot@google.com> | 2020-04-28 20:26:06 +0000 |
---|---|---|
committer | android-build-team Robot <android-build-team-robot@google.com> | 2020-04-28 20:26:06 +0000 |
commit | 176b03abf8e6baf728dcbdbba6f43c21b6b40ae1 (patch) | |
tree | 78e86640c6d23a6fb5e69a659077d9f5d56d5514 | |
parent | 346049c5ce31736f66837d3aadc6514b5569793a (diff) | |
parent | 68abb5c01306acf16296fc5edfc44bc30145311e (diff) | |
download | platform_external_minijail-android10-mainline-tzdata-release.tar.gz platform_external_minijail-android10-mainline-tzdata-release.tar.bz2 platform_external_minijail-android10-mainline-tzdata-release.zip |
Snap for 6439596 from 68abb5c01306acf16296fc5edfc44bc30145311e to qt-aml-tzdata-releaseandroid-mainline-10.0.0_r11android10-mainline-tzdata-release
Change-Id: I6562d49398a4e1a8a5423ab3fde42505c602f40f
-rw-r--r-- | Makefile | 6 | ||||
-rw-r--r-- | bpf.h | 4 | ||||
-rw-r--r-- | examples/cat.policy | 2 | ||||
-rw-r--r-- | gen_constants-inl.h | 4 | ||||
-rw-r--r-- | libminijail.c | 241 | ||||
-rw-r--r-- | libminijail.h | 38 | ||||
-rw-r--r-- | libminijail_unittest.cc | 262 | ||||
-rw-r--r-- | minijail0.1 | 19 | ||||
-rw-r--r-- | minijail0_cli.c | 53 | ||||
-rw-r--r-- | minijail0_cli_unittest.cc | 4 | ||||
-rw-r--r-- | parse_seccomp_policy.cc | 8 | ||||
-rw-r--r-- | syscall_filter.c | 95 | ||||
-rw-r--r-- | syscall_filter.h | 19 | ||||
-rw-r--r-- | syscall_filter_unittest.cc | 198 | ||||
-rw-r--r-- | syscall_filter_unittest_macros.h | 14 | ||||
-rw-r--r-- | system.c | 67 | ||||
-rw-r--r-- | system.h | 5 | ||||
-rw-r--r-- | system_unittest.cc | 15 | ||||
-rw-r--r-- | util.h | 33 |
19 files changed, 192 insertions, 895 deletions
@@ -46,10 +46,8 @@ ifeq ($(USE_SYSTEM_GTEST),no) GTEST_CXXFLAGS := -std=gnu++14 GTEST_LIBS := gtest.a else -GTEST_CXXFLAGS := $(shell gtest-config --cxxflags 2>/dev/null || \ - echo "-pthread") -GTEST_LIBS := $(shell gtest-config --libs 2>/dev/null || \ - echo "-lgtest -pthread -lpthread") +GTEST_CXXFLAGS := $(shell gtest-config --cxxflags) +GTEST_LIBS := $(shell gtest-config --libs) endif CORE_OBJECT_FILES := libminijail.o syscall_filter.o signal_handler.o \ @@ -48,7 +48,6 @@ enum { #define SECCOMP_RET_KILL 0x00000000U /* kill the task immediately */ #define SECCOMP_RET_TRAP 0x00030000U /* return SIGSYS */ #define SECCOMP_RET_ERRNO 0x00050000U /* return -1 and set errno */ -#define SECCOMP_RET_LOG 0x7ffc0000U /* allow after logging */ #define SECCOMP_RET_ALLOW 0x7fff0000U /* allow */ #define SECCOMP_RET_DATA 0x0000ffffU /* mask for return value */ @@ -173,9 +172,6 @@ static inline size_t set_bpf_instr(struct sock_filter *instr, set_bpf_stmt((_block), BPF_RET+BPF_K, \ SECCOMP_RET_ERRNO | ((_errno) & SECCOMP_RET_DATA)) -#define set_bpf_ret_log(_block) \ - set_bpf_stmt((_block), BPF_RET+BPF_K, SECCOMP_RET_LOG) - #define set_bpf_ret_allow(_block) \ set_bpf_stmt((_block), BPF_RET+BPF_K, SECCOMP_RET_ALLOW) diff --git a/examples/cat.policy b/examples/cat.policy index 25b4c29b..dea3dd37 100644 --- a/examples/cat.policy +++ b/examples/cat.policy @@ -5,12 +5,10 @@ read: 1 write: 1 -restart_syscall: 1 rt_sigreturn: 1 exit_group: 1 open: 1 -openat: 1 close: 1 fstat: 1 # Enforce W^X. diff --git a/gen_constants-inl.h b/gen_constants-inl.h index d2c1ec08..0ea710d3 100644 --- a/gen_constants-inl.h +++ b/gen_constants-inl.h @@ -3,9 +3,7 @@ #endif // __i386__ || __x86_64__ #include <errno.h> #include <fcntl.h> -#include <linux/fd.h> #include <linux/fs.h> -#include <linux/loop.h> #include <linux/mman.h> #include <linux/net.h> #include <linux/prctl.h> @@ -13,8 +11,8 @@ #include <linux/serial.h> #include <linux/sockios.h> #include <linux/termios.h> -#include <signal.h> #include <stddef.h> +#include <signal.h> #include <sys/mman.h> #include <sys/resource.h> #include <sys/socket.h> diff --git a/libminijail.c b/libminijail.c index 6d6b2dc4..482d6bd9 100644 --- a/libminijail.c +++ b/libminijail.c @@ -163,7 +163,6 @@ struct minijail { int close_open_fds : 1; int new_session_keyring : 1; int forward_signals : 1; - int setsid : 1; } flags; uid_t uid; gid_t gid; @@ -238,7 +237,6 @@ void minijail_preenter(struct minijail *j) j->flags.pid_file = 0; j->flags.cgroups = 0; j->flags.forward_signals = 0; - j->flags.setsid = 0; j->remount_mode = 0; } @@ -382,17 +380,6 @@ void API minijail_set_seccomp_filter_tsync(struct minijail *j) die("minijail_set_seccomp_filter_tsync() must be called " "before minijail_parse_seccomp_filters()"); } - - if (j->flags.seccomp_filter_logging && !seccomp_ret_log_available()) { - /* - * If SECCOMP_RET_LOG is not available, we don't want to use - * SECCOMP_RET_TRAP to both kill the entire process and report - * failing syscalls, since it will be brittle. Just bail. - */ - die("SECCOMP_RET_LOG not available, cannot use logging with " - "thread sync at the same time"); - } - j->flags.seccomp_filter_tsync = 1; } @@ -402,23 +389,11 @@ void API minijail_log_seccomp_filter_failures(struct minijail *j) die("minijail_log_seccomp_filter_failures() must be called " "before minijail_parse_seccomp_filters()"); } - - if (j->flags.seccomp_filter_tsync && !seccomp_ret_log_available()) { - /* - * If SECCOMP_RET_LOG is not available, we don't want to use - * SECCOMP_RET_TRAP to both kill the entire process and report - * failing syscalls, since it will be brittle. Just bail. - */ - die("SECCOMP_RET_LOG not available, cannot use thread sync with " - "logging at the same time"); - } - - if (debug_logging_allowed()) { - j->flags.seccomp_filter_logging = 1; - } else { - warn("non-debug build: ignoring request to enable seccomp " - "logging"); - } +#ifdef ALLOW_DEBUG_LOGGING + j->flags.seccomp_filter_logging = 1; +#else + warn("non-debug build: ignoring request to enable seccomp logging"); +#endif } void API minijail_use_caps(struct minijail *j, uint64_t capmask) @@ -758,11 +733,6 @@ int API minijail_forward_signals(struct minijail *j) return 0; } -int API minijail_create_session(struct minijail *j) { - j->flags.setsid = 1; - return 0; -} - int API minijail_mount_with_data(struct minijail *j, const char *src, const char *dest, const char *type, unsigned long flags, const char *data) @@ -955,17 +925,12 @@ static int seccomp_should_use_filters(struct minijail *j) } static int set_seccomp_filters_internal(struct minijail *j, - const struct sock_fprog *filter, - bool owned) + struct sock_fprog *filter, bool owned) { struct sock_fprog *fprog; if (owned) { - /* - * If |owned| is true, it's OK to cast away the const-ness since - * we'll own the pointer going forward. - */ - fprog = (struct sock_fprog *)filter; + fprog = filter; } else { fprog = malloc(sizeof(struct sock_fprog)); if (!fprog) @@ -990,48 +955,45 @@ static int set_seccomp_filters_internal(struct minijail *j, return 0; } -static int parse_seccomp_filters(struct minijail *j, const char *filename, - FILE *policy_file) +void API minijail_set_seccomp_filters(struct minijail *j, + const struct sock_fprog *filter) { - struct sock_fprog *fprog = malloc(sizeof(struct sock_fprog)); - if (!fprog) - return -ENOMEM; + if (!seccomp_should_use_filters(j)) + return; - struct filter_options filteropts; + if (j->flags.seccomp_filter_logging) { + die("minijail_log_seccomp_filter_failures() is incompatible " + "with minijail_set_seccomp_filters()"); + } /* - * Figure out filter options. - * Allow logging? + * set_seccomp_filters_internal() can only fail with ENOMEM. + * Furthermore, since we won't own the incoming filter, it will not be + * modified. */ - filteropts.allow_logging = - debug_logging_allowed() && j->flags.seccomp_filter_logging; - - /* What to do on a blocked system call? */ - if (filteropts.allow_logging) { - if (seccomp_ret_log_available()) - filteropts.action = ACTION_RET_LOG; - else - filteropts.action = ACTION_RET_TRAP; - } else { - if (j->flags.seccomp_filter_tsync) - filteropts.action = ACTION_RET_TRAP; - else - filteropts.action = ACTION_RET_KILL; + if (set_seccomp_filters_internal(j, (struct sock_fprog *)filter, + false) < 0) { + die("failed to copy seccomp filter"); } +} - /* - * If SECCOMP_RET_LOG is not available, need to allow extra syscalls - * for logging. - */ - filteropts.allow_syscalls_for_logging = - filteropts.allow_logging && !seccomp_ret_log_available(); +static int parse_seccomp_filters(struct minijail *j, const char *filename, + FILE *policy_file) +{ + struct sock_fprog *fprog = malloc(sizeof(struct sock_fprog)); + if (!fprog) + return -ENOMEM; + int use_ret_trap = + j->flags.seccomp_filter_tsync || j->flags.seccomp_filter_logging; + int allow_logging = j->flags.seccomp_filter_logging; - if (compile_filter(filename, policy_file, fprog, &filteropts)) { + if (compile_filter(filename, policy_file, fprog, use_ret_trap, + allow_logging)) { free(fprog); return -1; } - return set_seccomp_filters_internal(j, fprog, true /* owned */); + return set_seccomp_filters_internal(j, fprog, true); } void API minijail_parse_seccomp_filters(struct minijail *j, const char *path) @@ -1079,27 +1041,6 @@ void API minijail_parse_seccomp_filters_from_fd(struct minijail *j, int fd) fclose(file); } -void API minijail_set_seccomp_filters(struct minijail *j, - const struct sock_fprog *filter) -{ - if (!seccomp_should_use_filters(j)) - return; - - if (j->flags.seccomp_filter_logging) { - die("minijail_log_seccomp_filter_failures() is incompatible " - "with minijail_set_seccomp_filters()"); - } - - /* - * set_seccomp_filters_internal() can only fail with ENOMEM. - * Furthermore, since we won't own the incoming filter, it will not be - * modified. - */ - if (set_seccomp_filters_internal(j, filter, false /* owned */) < 0) { - die("failed to set seccomp filter"); - } -} - int API minijail_use_alt_syscall(struct minijail *j, const char *table) { j->alt_syscall_table = strdup(table); @@ -1542,9 +1483,7 @@ static int mount_one(const struct minijail *j, struct mountpoint *m, /* We assume |dest| has a leading "/". */ if (dev_path && strncmp("/dev/", m->dest, 5) == 0) { - /* - * Since the temp path is rooted at /dev, skip that dest part. - */ + /* Since the temp path is rooted at /dev, skip that dest part. */ if (asprintf(&dest, "%s%s", dev_path, m->dest + 4) < 0) return -ENOMEM; } else { @@ -1794,17 +1733,14 @@ static void write_ugid_maps_or_die(const struct minijail *j) if (j->uidmap && write_proc_file(j->initpid, j->uidmap, "uid_map") != 0) kill_child_and_die(j, "failed to write uid_map"); if (j->gidmap && j->flags.disable_setgroups) { - /* - * Older kernels might not have the /proc/<pid>/setgroups files. - */ + /* Older kernels might not have the /proc/<pid>/setgroups files. */ int ret = write_proc_file(j->initpid, "deny", "setgroups"); if (ret != 0) { if (ret == -ENOENT) { /* See http://man7.org/linux/man-pages/man7/user_namespaces.7.html. */ warn("could not disable setgroups(2)"); } else - kill_child_and_die( - j, "failed to disable setgroups(2)"); + kill_child_and_die(j, "failed to disable setgroups(2)"); } } if (j->gidmap && write_proc_file(j->initpid, j->gidmap, "gid_map") != 0) @@ -2016,16 +1952,13 @@ static void set_seccomp_filter(const struct minijail *j) if (j->flags.seccomp_filter) { if (j->flags.seccomp_filter_logging) { + /* + * If logging seccomp filter failures, + * install the SIGSYS handler first. + */ + if (install_sigsys_handler()) + pdie("failed to install SIGSYS handler"); warn("logging seccomp filter failures"); - if (!seccomp_ret_log_available()) { - /* - * If SECCOMP_RET_LOG is not available, - * install the SIGSYS handler first. - */ - if (install_sigsys_handler()) - pdie( - "failed to install SIGSYS handler"); - } } else if (j->flags.seccomp_filter_tsync) { /* * If setting thread sync, @@ -2164,15 +2097,12 @@ void API minijail_enter(const struct minijail *j) pdie("unshare(CLONE_NEWNS) failed"); /* * By default, remount all filesystems as private, unless - * - Passed a specific remount mode, in which case remount with - * that, - * - Asked not to remount at all, in which case skip the - * mount(2) call. + * - Passed a specific remount mode, in which case remount with that, + * - Asked not to remount at all, in which case skip the mount(2) call. * https://www.kernel.org/doc/Documentation/filesystems/sharedsubtree.txt */ if (j->remount_mode) { - if (mount(NULL, "/", NULL, MS_REC | j->remount_mode, - NULL)) + if (mount(NULL, "/", NULL, MS_REC | j->remount_mode, NULL)) pdie("mount(NULL, /, NULL, MS_REC | MS_PRIVATE," " NULL) failed"); } @@ -2743,12 +2673,11 @@ static int minijail_run_internal(struct minijail *j, } /* - * If the parent process needs to configure the child's runtime - * environment after forking, create a pipe(2) to block the child until - * configuration is done. + * If we want to set up a new uid/gid map in the user namespace, + * or if we need to add the child process to cgroups, create the pipe(2) + * to sync between parent and child. */ - if (j->flags.forward_signals || j->flags.pid_file || j->flags.cgroups || - j->rlimit_count || j->flags.userns) { + if (j->flags.userns || j->flags.cgroups) { sync_child = 1; if (pipe(child_sync_pipe_fds)) return -EFAULT; @@ -2808,11 +2737,7 @@ static int minijail_run_internal(struct minijail *j, if (use_preload) { free(oldenv_copy); } - if (pid_namespace && errno == EPERM) { - warn("clone(CLONE_NEWPID) failed with EPERM, maybe " - "this process is not running with CAP_SYS_ADMIN?"); - } - pdie("failed to fork child"); + die("failed to fork child"); } if (child_pid) { @@ -2952,17 +2877,6 @@ static int minijail_run_internal(struct minijail *j, inheritable_fds[size++] = stderr_fds[0]; inheritable_fds[size++] = stderr_fds[1]; } - - /* - * Preserve namespace file descriptors over the close_open_fds() - * call. These are closed in minijail_enter() so they won't leak - * into the child process. - */ - if (j->flags.enter_vfs) - minijail_preserve_fd(j, j->mountns_fd, j->mountns_fd); - if (j->flags.enter_net) - minijail_preserve_fd(j, j->netns_fd, j->netns_fd); - for (i = 0; i < j->preserved_fd_count; i++) { /* * Preserve all parent_fds. They will be dup2(2)-ed in @@ -2989,8 +2903,8 @@ static int minijail_run_internal(struct minijail *j, * set up the read end of the pipe. */ if (status_out->pstdin_fd) { - if (dupe_and_close_fd(stdin_fds, 0 /* read end */, - STDIN_FILENO) < 0) + if (setup_and_dupe_pipe_end(stdin_fds, 0 /* read end */, + STDIN_FILENO) < 0) die("failed to set up stdin pipe"); } @@ -2999,8 +2913,8 @@ static int minijail_run_internal(struct minijail *j, * set up the write end of the pipe. */ if (status_out->pstdout_fd) { - if (dupe_and_close_fd(stdout_fds, 1 /* write end */, - STDOUT_FILENO) < 0) + if (setup_and_dupe_pipe_end(stdout_fds, 1 /* write end */, + STDOUT_FILENO) < 0) die("failed to set up stdout pipe"); } @@ -3009,21 +2923,21 @@ static int minijail_run_internal(struct minijail *j, * set up the write end of the pipe. */ if (status_out->pstderr_fd) { - if (dupe_and_close_fd(stderr_fds, 1 /* write end */, - STDERR_FILENO) < 0) + if (setup_and_dupe_pipe_end(stderr_fds, 1 /* write end */, + STDERR_FILENO) < 0) die("failed to set up stderr pipe"); } /* - * If any of stdin, stdout, or stderr are TTYs, or setsid flag is - * set, create a new session. This prevents the jailed process from - * using the TIOCSTI ioctl to push characters into the parent process - * terminal's input buffer, therefore escaping the jail. + * If any of stdin, stdout, or stderr are TTYs, create a new session. + * This prevents the jailed process from using the TIOCSTI ioctl + * to push characters into the parent process terminal's input buffer, + * therefore escaping the jail. * * Since it has just forked, the child will not be a process group * leader, and this call to setsid() should always succeed. */ - if (j->flags.setsid || isatty(STDIN_FILENO) || isatty(STDOUT_FILENO) || + if (isatty(STDIN_FILENO) || isatty(STDOUT_FILENO) || isatty(STDERR_FILENO)) { if (setsid() < 0) { pdie("setsid() failed"); @@ -3102,37 +3016,28 @@ static int minijail_run_internal(struct minijail *j, * -> init()-ing process * -> execve()-ing process */ - execve(config->filename, config->argv, child_env); - - ret = (errno == ENOENT ? MINIJAIL_ERR_NO_COMMAND : MINIJAIL_ERR_NO_ACCESS); - pwarn("execve(%s) failed", config->filename); + ret = execve(config->filename, config->argv, child_env); + if (ret == -1) { + pwarn("execve(%s) failed", config->filename); + } _exit(ret); } int API minijail_kill(struct minijail *j) { - if (j->initpid <= 0) - return -ECHILD; - + int st; if (kill(j->initpid, SIGTERM)) return -errno; - - return minijail_wait(j); + if (waitpid(j->initpid, &st, 0) < 0) + return -errno; + return st; } int API minijail_wait(struct minijail *j) { - if (j->initpid <= 0) - return -ECHILD; - int st; - while (true) { - const int ret = waitpid(j->initpid, &st, 0); - if (ret >= 0) - break; - if (errno != EINTR) - return -errno; - } + if (waitpid(j->initpid, &st, 0) < 0) + return -errno; if (!WIFEXITED(st)) { int error_status = st; @@ -3150,7 +3055,7 @@ int API minijail_wait(struct minijail *j) if (signum == SIGSYS) { error_status = MINIJAIL_ERR_JAIL; } else { - error_status = MINIJAIL_ERR_SIG_BASE + signum; + error_status = 128 + signum; } } return error_status; diff --git a/libminijail.h b/libminijail.h index 3da845c3..0853c179 100644 --- a/libminijail.h +++ b/libminijail.h @@ -23,22 +23,9 @@ extern "C" { #endif -/* Possible exit status codes returned by minijail_wait(). */ enum { - /* Command can be found but cannot be run */ - MINIJAIL_ERR_NO_ACCESS = 126, - - /* Command cannot be found */ - MINIJAIL_ERR_NO_COMMAND = 127, - - /* (MINIJAIL_ERR_SIG_BASE + n) if process killed by signal n != SIGSYS */ - MINIJAIL_ERR_SIG_BASE = 128, - MINIJAIL_ERR_PRELOAD = 252, - - /* Process killed by SIGSYS */ MINIJAIL_ERR_JAIL = 253, - MINIJAIL_ERR_INIT = 254, }; @@ -173,9 +160,6 @@ int minijail_add_to_cgroup(struct minijail *j, const char *path); */ int minijail_forward_signals(struct minijail *j); -/* The jailed child process should call setsid() to create a new session. */ -int minijail_create_session(struct minijail *j); - /* * minijail_enter_chroot: enables chroot() restriction for @j * @j minijail to apply restriction to @@ -404,28 +388,14 @@ int minijail_run_env_pid_pipes_no_preload(struct minijail *j, pid_t minijail_fork(struct minijail *j); /* - * Send SIGTERM to the process in the minijail and wait for it to terminate. - * - * Return the same nonnegative exit status as minijail_wait(), or a negative - * error code (eg -ESRCH if the process has already been waited for). - * - * This is most useful if the minijail has been created with PID namespacing - * since, in this case, all processes inside it are atomically killed. + * Kill the specified minijail. The minijail must have been created with pid + * namespacing; if it was, all processes inside it are atomically killed. */ int minijail_kill(struct minijail *j); /* - * Wait for the first process spawned in the specified minijail to exit, and - * return its exit status. A process can only be waited once. - * - * Return: - * A negative error code if the process cannot be waited for (eg -ECHILD if no - * process has been started or if the process has already been waited for). - * MINIJAIL_ERR_NO_COMMAND if command cannot be found. - * MINIJAIL_ERR_NO_ACCESS if command cannot be run. - * MINIJAIL_ERR_JAIL if process was killed by SIGSYS. - * (MINIJAIL_ERR_SIG_BASE + n) if process was killed by signal n != SIGSYS. - * (n & 0xFF) if process finished by returning code n. + * Wait for all processes in the specified minijail to exit. Returns the exit + * status of the _first_ process spawned in the jail. */ int minijail_wait(struct minijail *j); diff --git a/libminijail_unittest.cc b/libminijail_unittest.cc index d8ffc381..25aa76e5 100644 --- a/libminijail_unittest.cc +++ b/libminijail_unittest.cc @@ -217,189 +217,19 @@ TEST_F(MarshalTest, 0xff) { EXPECT_EQ(-EINVAL, minijail_unmarshal(j_, buf_, sizeof(buf_))); } -TEST(KillTest, running_process) { - const ScopedMinijail j(minijail_new()); - char* const argv[] = {"sh", "-c", "sleep 1000", nullptr}; - EXPECT_EQ(minijail_run(j.get(), kShellPath, argv), 0); - EXPECT_EQ(minijail_kill(j.get()), 128 + SIGTERM); - EXPECT_EQ(minijail_kill(j.get()), -ESRCH); -} - -TEST(KillTest, process_already_awaited) { - const ScopedMinijail j(minijail_new()); - char* const argv[] = {"sh", "-c", "sleep 1; exit 42", nullptr}; - EXPECT_EQ(minijail_run(j.get(), kShellPath, argv), 0); - EXPECT_EQ(minijail_wait(j.get()), 42); - EXPECT_EQ(minijail_kill(j.get()), -ESRCH); -} - -TEST(KillTest, process_already_finished_but_not_awaited) { - int fds[2]; - const ScopedMinijail j(minijail_new()); - char* const argv[] = {"sh", "-c", "exit 42", nullptr}; - ASSERT_EQ(pipe(fds), 0); - EXPECT_EQ(minijail_run(j.get(), kShellPath, argv), 0); - ASSERT_EQ(close(fds[1]), 0); - // Wait for process to finish. - char buf[PIPE_BUF]; - EXPECT_EQ(read(fds[0], buf, PIPE_BUF), 0); - EXPECT_EQ(minijail_kill(j.get()), 42); - EXPECT_EQ(minijail_wait(j.get()), -ECHILD); -} - -TEST(KillTest, process_not_started) { - const ScopedMinijail j(minijail_new()); - EXPECT_EQ(minijail_kill(j.get()), -ECHILD); -} - -TEST(WaitTest, return_zero) { - const ScopedMinijail j(minijail_new()); - char* const argv[] = {"sh", "-c", "exit 0", nullptr}; - EXPECT_EQ(minijail_run(j.get(), kShellPath, argv), 0); - EXPECT_EQ(minijail_wait(j.get()), 0); -} - -TEST(WaitTest, return_max) { - const ScopedMinijail j(minijail_new()); - char* const argv[] = {"sh", "-c", "exit 255", nullptr}; - EXPECT_EQ(minijail_run(j.get(), kShellPath, argv), 0); - EXPECT_EQ(minijail_wait(j.get()), 255); -} - -TEST(WaitTest, return_modulo) { - const ScopedMinijail j(minijail_new()); - char* const argv[] = {"sh", "-c", "exit 256", nullptr}; - EXPECT_EQ(minijail_run(j.get(), kShellPath, argv), 0); - EXPECT_EQ(minijail_wait(j.get()), 0); -} - -TEST(WaitTest, killed_by_sigkill) { - const ScopedMinijail j(minijail_new()); - char* const argv[] = {"sh", "-c", "kill -KILL $$; sleep 1000", nullptr}; - EXPECT_EQ(minijail_run(j.get(), kShellPath, argv), 0); - EXPECT_EQ(minijail_wait(j.get()), MINIJAIL_ERR_SIG_BASE + SIGKILL); -} - -TEST(WaitTest, killed_by_sigsys) { - const ScopedMinijail j(minijail_new()); - char* const argv[] = {"sh", "-c", "kill -SYS $$; sleep 1000", nullptr}; - EXPECT_EQ(minijail_run(j.get(), kShellPath, argv), 0); - EXPECT_EQ(minijail_wait(j.get()), MINIJAIL_ERR_JAIL); -} - -TEST(WaitTest, command_not_found) { - const ScopedMinijail j(minijail_new()); - char* const argv[] = {"whatever", nullptr}; - EXPECT_EQ(minijail_run(j.get(), "command that cannot be found", argv), 0); - EXPECT_EQ(minijail_wait(j.get()), MINIJAIL_ERR_NO_COMMAND); -} - -TEST(WaitTest, command_not_run) { - const ScopedMinijail j(minijail_new()); - char* const argv[] = {"whatever", nullptr}; - EXPECT_EQ(minijail_run(j.get(), "/dev/null", argv), 0); - EXPECT_EQ(minijail_wait(j.get()), MINIJAIL_ERR_NO_ACCESS); -} - -TEST(WaitTest, no_process) { - const ScopedMinijail j(minijail_new()); - EXPECT_EQ(minijail_wait(j.get()), -ECHILD); -} - -TEST(WaitTest, can_wait_only_once) { - const ScopedMinijail j(minijail_new()); - char* const argv[] = {"sh", "-c", "exit 0", nullptr}; - EXPECT_EQ(minijail_run(j.get(), kShellPath, argv), 0); - EXPECT_EQ(minijail_wait(j.get()), 0); - EXPECT_EQ(minijail_wait(j.get()), -ECHILD); -} - -TEST(Test, close_original_pipes_after_dup2) { - // Pipe used by child process to signal that it continued after reading from - // stdin. - int to_wait[2]; - ASSERT_EQ(pipe(to_wait), 0); - - const ScopedMinijail j(minijail_new()); - char* program; - ASSERT_GT(asprintf(&program, R"( - echo Hi >&1; - echo There >&2; - exec 1>&-; - exec 2>&-; - read line1; - read line2; - echo "$line1$line2 and Goodbye" >&%d; - exit 42; - )", to_wait[1]), 0); - char* const argv[] = {"sh", "-c", program, nullptr}; - - int in = -1; - int out = -1; - int err = -1; - EXPECT_EQ(minijail_run_pid_pipes_no_preload(j.get(), kShellPath, argv, - nullptr, &in, &out, &err), - 0); - free(program); - - EXPECT_GT(in, 0); - EXPECT_GT(out, 0); - EXPECT_GT(err, 0); - - char buf[PIPE_BUF]; - ssize_t n; - - // Check that stdout and stderr pipes work. - n = read(out, buf, PIPE_BUF); - ASSERT_GT(n, 0); - EXPECT_EQ(std::string(buf, n), "Hi\n"); - - n = read(err, buf, PIPE_BUF); - ASSERT_GT(n, 0); - EXPECT_EQ(std::string(buf, n), "There\n"); - - // Check that the write ends of stdout and stderr pipes got closed by the - // child process. If the child process kept other file descriptors connected - // to stdout and stderr, then the parent process wouldn't be able to detect - // that all write ends of these pipes are closed and it would block here. - EXPECT_EQ(read(out, buf, PIPE_BUF), 0); - EXPECT_EQ(read(err, buf, PIPE_BUF), 0); - - // Check that stdin pipe works. - const std::string s = "Greetings\n"; - EXPECT_EQ(write(in, s.data(), s.size()), s.size()); - - // Close write end of pipe connected to child's stdin. If there was another - // file descriptor connected to this write end, then the child wouldn't be - // able to detect that this write end is closed and it would block. - ASSERT_EQ(close(in), 0); - - // Check that child process continued and ended. - n = read(to_wait[0], buf, PIPE_BUF); - ASSERT_GT(n, 0); - EXPECT_EQ(std::string(buf, n), "Greetings and Goodbye\n"); - EXPECT_EQ(minijail_wait(j.get()), 42); -} - TEST(Test, minijail_run_pid_pipes) { - // TODO(crbug.com/895875): The preload library interferes with ASan since they - // both need to use LD_PRELOAD. - if (running_with_asan()) { - SUCCEED(); - return; - } constexpr char teststr[] = "test\n"; - ScopedMinijail j(minijail_new()); - minijail_set_preload_path(j.get(), kPreloadPath); + struct minijail* j = minijail_new(); + minijail_set_preload_path(j, kPreloadPath); char* argv[4]; argv[0] = const_cast<char*>(kCatPath); argv[1] = nullptr; pid_t pid; int child_stdin, child_stdout; - int mj_run_ret = minijail_run_pid_pipes(j.get(), argv[0], argv, &pid, - &child_stdin, &child_stdout, nullptr); + int mj_run_ret = minijail_run_pid_pipes(j, argv[0], argv, &pid, &child_stdin, + &child_stdout, nullptr); EXPECT_EQ(mj_run_ret, 0); const size_t teststr_len = strlen(teststr); @@ -423,9 +253,8 @@ TEST(Test, minijail_run_pid_pipes) { argv[2] = "echo test >&2"; argv[3] = nullptr; int child_stderr; - mj_run_ret = minijail_run_pid_pipes(j.get(), argv[0], argv, &pid, - &child_stdin, &child_stdout, - &child_stderr); + mj_run_ret = minijail_run_pid_pipes(j, argv[0], argv, &pid, &child_stdin, + &child_stdout, &child_stderr); EXPECT_EQ(mj_run_ret, 0); read_ret = read(child_stderr, buf, sizeof(buf)); @@ -434,6 +263,8 @@ TEST(Test, minijail_run_pid_pipes) { waitpid(pid, &status, 0); ASSERT_TRUE(WIFEXITED(status)); EXPECT_EQ(WEXITSTATUS(status), 0); + + minijail_destroy(j); } TEST(Test, minijail_run_pid_pipes_no_preload) { @@ -449,7 +280,7 @@ TEST(Test, minijail_run_pid_pipes_no_preload) { struct minijail *j = minijail_new(); - argv[0] = const_cast<char*>(kCatPath); + argv[0] = (char*)kCatPath; argv[1] = NULL; mj_run_ret = minijail_run_pid_pipes_no_preload(j, argv[0], argv, &pid, @@ -470,7 +301,7 @@ TEST(Test, minijail_run_pid_pipes_no_preload) { ASSERT_TRUE(WIFSIGNALED(status)); EXPECT_EQ(WTERMSIG(status), SIGTERM); - argv[0] = const_cast<char*>(kShellPath); + argv[0] = (char*)kShellPath; argv[1] = "-c"; argv[2] = "echo test >&2"; argv[3] = NULL; @@ -503,7 +334,7 @@ TEST(Test, minijail_run_env_pid_pipes_no_preload) { struct minijail *j = minijail_new(); - argv[0] = const_cast<char*>(kShellPath); + argv[0] = (char*)kShellPath; argv[1] = "-c"; argv[2] = "echo \"${TEST_PARENT+set}|${TEST_VAR}\""; argv[3] = NULL; @@ -519,9 +350,9 @@ TEST(Test, minijail_run_env_pid_pipes_no_preload) { EXPECT_EQ(mj_run_ret, 0); read_ret = read(child_stdout, buf, sizeof(buf)); - EXPECT_EQ(read_ret, (int)testvar_len + 2); + EXPECT_GE(read_ret, (int)testvar_len); - EXPECT_EQ("|test\n", std::string(buf, 0, testvar_len + 2)); + EXPECT_EQ("|test\n", std::string(buf)); EXPECT_EQ(waitpid(pid, &status, 0), pid); ASSERT_TRUE(WIFEXITED(status)); @@ -549,7 +380,7 @@ TEST(Test, test_minijail_no_fd_leaks) { struct minijail *j = minijail_new(); - argv[0] = const_cast<char*>(kShellPath); + argv[0] = (char*)kShellPath; argv[1] = "-c"; argv[2] = script; argv[3] = NULL; @@ -591,17 +422,18 @@ TEST(Test, test_minijail_fork) { int pipe_fds[2]; ssize_t pid_size = sizeof(mj_fork_ret); - ScopedMinijail j(minijail_new()); + struct minijail *j = minijail_new(); ASSERT_EQ(pipe(pipe_fds), 0); - mj_fork_ret = minijail_fork(j.get()); + mj_fork_ret = minijail_fork(j); ASSERT_GE(mj_fork_ret, 0); if (mj_fork_ret == 0) { pid_t pid_in_parent; // Wait for the parent to tell us the pid in the parent namespace. ASSERT_EQ(read(pipe_fds[0], &pid_in_parent, pid_size), pid_size); ASSERT_EQ(pid_in_parent, getpid()); + minijail_destroy(j); exit(0); } @@ -609,6 +441,8 @@ TEST(Test, test_minijail_fork) { waitpid(mj_fork_ret, &status, 0); ASSERT_TRUE(WIFEXITED(status)); EXPECT_EQ(WEXITSTATUS(status), 0); + + minijail_destroy(j); } static int early_exit(void* payload) { @@ -629,7 +463,7 @@ TEST(Test, test_minijail_callback) { MINIJAIL_HOOK_EVENT_PRE_DROP_CAPS); EXPECT_EQ(status, 0); - argv[0] = const_cast<char*>(kCatPath); + argv[0] = (char*)kCatPath; argv[1] = NULL; mj_run_ret = minijail_run_pid_pipes_no_preload(j, argv[0], argv, &pid, NULL, NULL, NULL); @@ -664,7 +498,7 @@ TEST(Test, test_minijail_preserve_fd) { ASSERT_EQ(status, 0); minijail_close_open_fds(j); - argv[0] = const_cast<char*>(kCatPath); + argv[0] = (char*)kCatPath; argv[1] = NULL; mj_run_ret = minijail_run_no_preload(j, argv[0], argv); EXPECT_EQ(mj_run_ret, 0); @@ -818,7 +652,7 @@ TEST_F(NamespaceTest, test_tmpfs_userns) { minijail_gidmap(j, gidmap); minijail_namespace_user_disable_setgroups(j); - argv[0] = const_cast<char*>(kShellPath); + argv[0] = (char*)kShellPath; argv[1] = "-c"; argv[2] = "exec touch /tmp/foo"; argv[3] = NULL; @@ -834,9 +668,7 @@ TEST_F(NamespaceTest, test_tmpfs_userns) { TEST_F(NamespaceTest, test_namespaces) { constexpr char teststr[] = "test\n"; - // TODO(crbug.com/895875): The preload library interferes with ASan since they - // both need to use LD_PRELOAD. - if (!userns_supported_ || running_with_asan()) { + if (!userns_supported_) { SUCCEED(); return; } @@ -881,11 +713,11 @@ TEST_F(NamespaceTest, test_namespaces) { minijail_close_open_fds(j.get()); test_function(j.get()); - char* const argv[] = {const_cast<char*>(kCatPath), nullptr}; + const char* argv[] = {kCatPath, nullptr}; pid_t container_pid; int child_stdin, child_stdout; int mj_run_ret = - run_function(j.get(), argv[0], argv, + run_function(j.get(), argv[0], const_cast<char* const*>(argv), &container_pid, &child_stdin, &child_stdout, nullptr); EXPECT_EQ(mj_run_ret, 0); @@ -1041,47 +873,3 @@ TEST(Test, parse_size) { ASSERT_EQ(-EINVAL, parse_size(&size, "-1G")); ASSERT_EQ(-EINVAL, parse_size(&size, "; /bin/rm -- ")); } - -void TestCreateSession(bool create_session) { - int status; - int pipe_fds[2]; - pid_t child_pid; - pid_t parent_sid = getsid(0); - ssize_t pid_size = sizeof(pid_t); - - ScopedMinijail j(minijail_new()); - // stdin/stdout/stderr might be attached to TTYs. Close them to avoid creating - // a new session because of that. - minijail_close_open_fds(j.get()); - - if (create_session) - minijail_create_session(j.get()); - - ASSERT_EQ(pipe(pipe_fds), 0); - minijail_preserve_fd(j.get(), pipe_fds[0], pipe_fds[0]); - - child_pid = minijail_fork(j.get()); - ASSERT_GE(child_pid, 0); - if (child_pid == 0) { - pid_t sid_in_parent; - ASSERT_EQ(read(pipe_fds[0], &sid_in_parent, pid_size), pid_size); - if (create_session) - ASSERT_NE(sid_in_parent, getsid(0)); - else - ASSERT_EQ(sid_in_parent, getsid(0)); - exit(0); - } - - EXPECT_EQ(write(pipe_fds[1], &parent_sid, pid_size), pid_size); - waitpid(child_pid, &status, 0); - ASSERT_TRUE(WIFEXITED(status)); - EXPECT_EQ(WEXITSTATUS(status), 0); -} - -TEST(Test, default_no_new_session) { - TestCreateSession(/*create_session=*/false); -} - -TEST(Test, create_new_session) { - TestCreateSession(/*create_session=*/true); -} diff --git a/minijail0.1 b/minijail0.1 index b8a7752b..0fbf38e0 100644 --- a/minijail0.1 +++ b/minijail0.1 @@ -18,8 +18,6 @@ The \fIsrc\fR path must be an absolute path. If \fIdest\fR is not specified, it will default to \fIsrc\fR. If the destination does not exist, it will be created as a file or directory based on the \fIsrc\fR type (including missing parent directories). -To create a writable bind-mount set \fIwritable\fR to \fB1\fR. If not specified -it will default to \fB0\fR (read-only). .TP \fB-B <mask>\fR Skip setting securebits in \fImask\fR when restricting capabilities (\fB-c\fR). @@ -157,13 +155,9 @@ Run inside a new IPC namespace. This option makes the program's System V IPC namespace independent. .TP \fB-L\fR -Report blocked syscalls when using a seccomp filter. On kernels with support for -SECCOMP_RET_LOG, every blocked syscall will be reported through the audit -subsystem (see \fBseccomp\fR(2) for more details on SECCOMP_RET_LOG -availability.) On all other kernels, the first failing syscall will be logged to -syslog. This latter case will also force certain syscalls to be allowed in order -to write to syslog. Note: this option is disabled and ignored for release -builds. +Report blocked syscalls to syslog when using seccomp filter. This option will +force certain syscalls to be allowed in order to achieve this, depending on the +system. .TP \fB-m[<uid> <loweruid> <count>[,<uid> <loweruid> <count>]]\fR Set the uid mapping of a user namespace (implies \fB-pU\fR). Same arguments as @@ -282,10 +276,7 @@ namespace to \fIhostname\fR. .TP \fB--logging=<system>\fR Use \fIsystem\fR as the logging system. \fIsystem\fR must be one of -\fBauto\fR (the default), \fBsyslog\fR, or \fBstderr\fR. - -\fBauto\fR will use \fBstderr\fR if connected to a tty (e.g. run directly by a -user), otherwise it will use \fBsyslog\fR. +\fBsyslog\fR (the default) or \fBstderr\fR. .TP \fB--profile <profile>\fR Choose from one of the available sandboxing profiles, which are simple way to @@ -352,4 +343,4 @@ The Chromium OS Authors <chromiumos-dev@chromium.org> Copyright \(co 2011 The Chromium OS Authors License BSD-like. .SH "SEE ALSO" -\fBlibminijail.h\fR \fBminijail0\fR(5) \fBseccomp\fR(2) +\fBlibminijail.h\fR \fBminijail0\fR(5) diff --git a/minijail0_cli.c b/minijail0_cli.c index 277c2224..807e567c 100644 --- a/minijail0_cli.c +++ b/minijail0_cli.c @@ -139,16 +139,9 @@ static void add_binding(struct minijail *j, char *arg) } if (dest == NULL || dest[0] == '\0') dest = src; - int writable; - if (flags == NULL || flags[0] == '\0' || !strcmp(flags, "0")) - writable = 0; - else if (!strcmp(flags, "1")) - writable = 1; - else { - fprintf(stderr, "Bad value for <writable>: %s\n", flags); - exit(1); - } - if (minijail_bind(j, src, dest, writable)) { + if (flags == NULL || flags[0] == '\0') + flags = "0"; + if (minijail_bind(j, src, dest, atoi(flags))) { fprintf(stderr, "minijail_bind failed.\n"); exit(1); } @@ -505,9 +498,8 @@ static void usage(const char *progn) " -K: Do not change share mode of any existing mounts.\n" " -K<mode>: Mark all existing mounts as <mode> instead of MS_PRIVATE.\n" " -l: Enter new IPC namespace.\n" - " -L: Report blocked syscalls when using seccomp filter.\n" - " If the kernel does not support SECCOMP_RET_LOG,\n" - " forces the following syscalls to be allowed:\n" + " -L: Report blocked syscalls to syslog when using seccomp filter.\n" + " Forces the following syscalls to be allowed:\n" " ", progn); /* clang-format on */ for (i = 0; i < log_syscalls_len; i++) @@ -547,7 +539,7 @@ static void usage(const char *progn) " --ambient: Raise ambient capabilities. Requires -c.\n" " --uts[=name]: Enter a new UTS namespace (and set hostname).\n" " --logging=<s>:Use <s> as the logging system.\n" - " <s> must be 'auto' (default), 'syslog', or 'stderr'.\n" + " <s> must be 'syslog' (default) or 'stderr'.\n" " --profile <p>:Configure minijail0 to run with the <p> sandboxing profile,\n" " which is a convenient way to express multiple flags\n" " that are typically used together.\n" @@ -582,7 +574,7 @@ int parse_args(struct minijail *j, int argc, char *const argv[], int forward = 1; int binding = 0; int chroot = 0, pivot_root = 0; - int mount_ns = 0, change_remount = 0; + int mount_ns = 0, skip_remount = 0; int inherit_suppl_gids = 0, keep_suppl_gids = 0; int caps = 0, ambient_caps = 0; int seccomp = -1; @@ -593,7 +585,7 @@ int parse_args(struct minijail *j, int argc, char *const argv[], int set_uidmap = 0, set_gidmap = 0; size_t tmp_size = 0; const char *filter_path = NULL; - int log_to_stderr = -1; + int log_to_stderr = 0; const char *optstring = "+u:g:sS:c:C:P:b:B:V:f:m::M::k:a:e::R:T:vrGhHinNplLt::IUK::wyYzd"; @@ -681,12 +673,11 @@ int parse_args(struct minijail *j, int argc, char *const argv[], add_mount(j, optarg); break; case 'K': - if (optarg) { + if (optarg) set_remount_mode(j, optarg); - } else { + else minijail_skip_remount_private(j); - } - change_remount = 1; + skip_remount = 1; break; case 'P': use_pivot_root(j, optarg, &pivot_root, chroot); @@ -830,11 +821,9 @@ int parse_args(struct minijail *j, int argc, char *const argv[], minijail_namespace_set_hostname(j, optarg); break; case 130: /* Logging. */ - if (!strcmp(optarg, "auto")) { - log_to_stderr = -1; - } else if (!strcmp(optarg, "syslog")) { + if (!strcmp(optarg, "syslog")) log_to_stderr = 0; - } else if (!strcmp(optarg, "stderr")) { + else if (!strcmp(optarg, "stderr")) { log_to_stderr = 1; } else { fprintf(stderr, "--logger must be 'syslog' or " @@ -866,10 +855,6 @@ int parse_args(struct minijail *j, int argc, char *const argv[], } } - if (log_to_stderr == -1) { - /* Autodetect default logging output. */ - log_to_stderr = isatty(STDIN_FILENO) ? 1 : 0; - } if (log_to_stderr) { init_logging(LOG_TO_FD, STDERR_FILENO, LOG_INFO); /* @@ -910,14 +895,12 @@ int parse_args(struct minijail *j, int argc, char *const argv[], } /* - * / is only remounted when entering a new mount namespace, so unless - * that's set there is no need for the -K/-K<mode> flags. + * Remounting / as MS_PRIVATE only happens when entering a new mount + * namespace, so skipping it only applies in that case. */ - if (change_remount && !mount_ns) { - fprintf(stderr, "No need to use -K (skip remounting '/') or " - "-K<mode> (remount '/' as <mode>)\n" - "without -v (new mount namespace).\n" - "Do you need to add '-v' explicitly?\n"); + if (skip_remount && !mount_ns) { + fprintf(stderr, "Can't skip marking mounts as MS_PRIVATE" + " without mount namespaces.\n"); exit(1); } diff --git a/minijail0_cli_unittest.cc b/minijail0_cli_unittest.cc index 0d6a07d4..a00541a8 100644 --- a/minijail0_cli_unittest.cc +++ b/minijail0_cli_unittest.cc @@ -405,10 +405,6 @@ TEST_F(CliTest, invalid_binding) { // Missing mount namespace/etc... argv = {"-b", "/", "/bin/sh"}; ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), ""); - - // Bad value for <writable>. - argv = {"-b", "/,,writable", "/bin/sh"}; - ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), ""); } // Valid calls to the mount option. diff --git a/parse_seccomp_policy.cc b/parse_seccomp_policy.cc index 38fcbee1..c563a4a1 100644 --- a/parse_seccomp_policy.cc +++ b/parse_seccomp_policy.cc @@ -78,14 +78,8 @@ int main(int argc, char** argv) { if (!f) pdie("fopen(%s) failed", argv[1]); - struct filter_options fopts { - .action = ACTION_RET_KILL, - .allow_logging = 0, - .allow_syscalls_for_logging = 0, - }; - struct sock_fprog fp; - int res = compile_filter(argv[1], f, &fp, &fopts); + int res = compile_filter(argv[1], f, &fp, 0, 0); fclose(f); if (res != 0) die("compile_filter failed"); diff --git a/syscall_filter.c b/syscall_filter.c index 3b78f97f..049797a6 100644 --- a/syscall_filter.c +++ b/syscall_filter.c @@ -137,13 +137,6 @@ void append_ret_errno(struct filter_block *head, int errno_val) append_filter_block(head, filter, ONE_INSTR); } -void append_ret_log(struct filter_block *head) -{ - struct sock_filter *filter = new_instr_buf(ONE_INSTR); - set_bpf_ret_log(filter); - append_filter_block(head, filter, ONE_INSTR); -} - void append_allow_syscall(struct filter_block *head, int nr) { struct sock_filter *filter = new_instr_buf(ALLOW_SYSCALL_LEN); @@ -276,7 +269,7 @@ int compile_atom(struct parser_state *state, struct filter_block *head, } int compile_errno(struct parser_state *state, struct filter_block *head, - char *ret_errno, enum block_action action) + char *ret_errno, int use_ret_trap) { char *errno_ptr = NULL; @@ -299,17 +292,10 @@ int compile_errno(struct parser_state *state, struct filter_block *head, append_ret_errno(head, errno_val); } else { - switch (action) { - case ACTION_RET_KILL: + if (!use_ret_trap) append_ret_kill(head); - break; - case ACTION_RET_TRAP: + else append_ret_trap(head); - break; - case ACTION_RET_LOG: - compiler_warn(state, "invalid action: ACTION_RET_LOG"); - return -1; - } } return 0; } @@ -318,7 +304,7 @@ struct filter_block *compile_policy_line(struct parser_state *state, int nr, const char *policy_line, unsigned int entry_lbl_id, struct bpf_labels *labels, - enum block_action action) + int use_ret_trap) { /* * |policy_line| should be an expression of the form: @@ -382,7 +368,7 @@ struct filter_block *compile_policy_line(struct parser_state *state, int nr, /* Checks whether we're unconditionally blocking this syscall. */ if (strncmp(line, "return", strlen("return")) == 0) { - if (compile_errno(state, head, line, action) < 0) { + if (compile_errno(state, head, line, use_ret_trap) < 0) { free_block_list(head); free(line); return NULL; @@ -437,23 +423,16 @@ struct filter_block *compile_policy_line(struct parser_state *state, int nr, * otherwise just kill the task. */ if (ret_errno) { - if (compile_errno(state, head, ret_errno, action) < 0) { + if (compile_errno(state, head, ret_errno, use_ret_trap) < 0) { free_block_list(head); free(line); return NULL; } } else { - switch (action) { - case ACTION_RET_KILL: + if (!use_ret_trap) append_ret_kill(head); - break; - case ACTION_RET_TRAP: + else append_ret_trap(head); - break; - case ACTION_RET_LOG: - append_ret_log(head); - break; - } } /* @@ -556,13 +535,12 @@ static ssize_t getmultiline(char **lineptr, size_t *n, FILE *stream) memcpy(&line[ret + 1], next_line, next_ret + 1); free(next_line); *lineptr = line; - return *n - 1; + return ret; } int compile_file(const char *filename, FILE *policy_file, struct filter_block *head, struct filter_block **arg_blocks, - struct bpf_labels *labels, - const struct filter_options *filteropts, + struct bpf_labels *labels, int use_ret_trap, int allow_logging, unsigned int include_level) { /* clang-format off */ @@ -616,7 +594,8 @@ int compile_file(const char *filename, FILE *policy_file, goto free_line; } if (compile_file(filename, included_file, head, - arg_blocks, labels, filteropts, + arg_blocks, labels, use_ret_trap, + allow_logging, include_level + 1) == -1) { compiler_warn(&state, "'@include %s' failed", filename); @@ -652,7 +631,7 @@ int compile_file(const char *filename, FILE *policy_file, if (nr < 0) { compiler_warn(&state, "nonexistent syscall '%s'", syscall_name); - if (filteropts->allow_logging) { + if (allow_logging) { /* * If we're logging failures, assume we're in a * debugging case and continue. @@ -690,16 +669,14 @@ int compile_file(const char *filename, FILE *policy_file, append_filter_block(head, nr_comp, ALLOW_SYSCALL_LEN); /* Build the arg filter block. */ - struct filter_block *block = - compile_policy_line(&state, nr, policy_line, id, - labels, filteropts->action); + struct filter_block *block = compile_policy_line( + &state, nr, policy_line, id, labels, use_ret_trap); if (!block) { if (*arg_blocks) { free_block_list(*arg_blocks); *arg_blocks = NULL; } - warn("could not allocate filter block"); ret = -1; goto free_line; } @@ -718,7 +695,6 @@ int compile_file(const char *filename, FILE *policy_file, free_block_list(*arg_blocks); *arg_blocks = NULL; } - warn("getmultiline() failed"); ret = -1; } @@ -728,8 +704,7 @@ free_line: } int compile_filter(const char *filename, FILE *initial_file, - struct sock_fprog *prog, - const struct filter_options *filteropts) + struct sock_fprog *prog, int use_ret_trap, int allow_logging) { int ret = 0; struct bpf_labels labels; @@ -753,48 +728,26 @@ int compile_filter(const char *filename, FILE *initial_file, len = bpf_load_syscall_nr(load_nr); append_filter_block(head, load_nr, len); - /* - * On kernels without SECCOMP_RET_LOG, Minijail can attempt to write the - * first failing syscall to syslog(3). In order for syslog(3) to work, - * some syscalls need to be unconditionally allowed. - */ - if (filteropts->allow_syscalls_for_logging) + /* If logging failures, allow the necessary syscalls first. */ + if (allow_logging) allow_logging_syscalls(head); if (compile_file(filename, initial_file, head, &arg_blocks, &labels, - filteropts, 0 /* include_level */) != 0) { + use_ret_trap, allow_logging, + 0 /* include_level */) != 0) { warn("compile_filter: compile_file() failed"); ret = -1; goto free_filter; } /* - * If none of the syscalls match, either fall through to LOG, TRAP, or - * KILL. + * If none of the syscalls match, either fall through to KILL, + * or return TRAP. */ - switch (filteropts->action) { - case ACTION_RET_KILL: + if (!use_ret_trap) append_ret_kill(head); - break; - case ACTION_RET_TRAP: + else append_ret_trap(head); - break; - case ACTION_RET_LOG: - if (filteropts->allow_logging) { - append_ret_log(head); - } else { - warn("compile_filter: cannot use RET_LOG without " - "allowing logging"); - ret = -1; - goto free_filter; - } - break; - default: - warn("compile_filter: invalid log action %d", - filteropts->action); - ret = -1; - goto free_filter; - } /* Allocate the final buffer, now that we know its size. */ size_t final_filter_len = diff --git a/syscall_filter.h b/syscall_filter.h index 019f3f09..737ef499 100644 --- a/syscall_filter.h +++ b/syscall_filter.h @@ -29,31 +29,20 @@ struct parser_state { size_t line_number; }; -enum block_action { ACTION_RET_KILL = 0, ACTION_RET_TRAP, ACTION_RET_LOG }; - -struct filter_options { - enum block_action action; - int allow_logging; - int allow_syscalls_for_logging; -}; - struct bpf_labels; struct filter_block *compile_policy_line(struct parser_state *state, int nr, const char *policy_line, unsigned int label_id, struct bpf_labels *labels, - enum block_action action); - + int do_ret_trap); int compile_file(const char *filename, FILE *policy_file, struct filter_block *head, struct filter_block **arg_blocks, - struct bpf_labels *labels, - const struct filter_options *filteropts, + struct bpf_labels *labels, int use_ret_trap, int allow_logging, unsigned int include_level); - int compile_filter(const char *filename, FILE *policy_file, - struct sock_fprog *prog, - const struct filter_options *filteropts); + struct sock_fprog *prog, int do_ret_trap, + int add_logging_syscalls); struct filter_block *new_filter_block(void); int flatten_block_list(struct filter_block *head, struct sock_filter *filter, diff --git a/syscall_filter_unittest.cc b/syscall_filter_unittest.cc index 95b38f49..aca5f54b 100644 --- a/syscall_filter_unittest.cc +++ b/syscall_filter_unittest.cc @@ -38,23 +38,18 @@ enum ret_trap { }; enum use_logging { - NO_LOGGING = 0, - USE_SIGSYS_LOGGING = 1, - USE_RET_LOG_LOGGING = 2, + NO_LOGGING = 0, + USE_LOGGING = 1, }; int test_compile_filter( std::string filename, FILE* policy_file, struct sock_fprog* prog, - enum block_action action = ACTION_RET_KILL, - enum use_logging allow_logging = NO_LOGGING) { - struct filter_options filteropts { - .action = action, - .allow_logging = allow_logging != NO_LOGGING, - .allow_syscalls_for_logging = allow_logging == USE_SIGSYS_LOGGING, - }; - return compile_filter(filename.c_str(), policy_file, prog, &filteropts); + enum ret_trap do_ret_trap = USE_RET_KILL, + enum use_logging add_logging_syscalls = NO_LOGGING) { + return compile_filter(filename.c_str(), policy_file, prog, do_ret_trap, + add_logging_syscalls); } int test_compile_file( @@ -63,16 +58,11 @@ int test_compile_file( struct filter_block* head, struct filter_block** arg_blocks, struct bpf_labels* labels, - enum block_action action = ACTION_RET_KILL, + enum ret_trap use_ret_trap = USE_RET_KILL, enum use_logging allow_logging = NO_LOGGING, unsigned int include_level = 0) { - struct filter_options filteropts { - .action = action, - .allow_logging = allow_logging != NO_LOGGING, - .allow_syscalls_for_logging = allow_logging == USE_SIGSYS_LOGGING, - }; return compile_file(filename.c_str(), policy_file, head, arg_blocks, labels, - &filteropts, include_level); + use_ret_trap, allow_logging, include_level); } struct filter_block* test_compile_policy_line( @@ -81,9 +71,9 @@ struct filter_block* test_compile_policy_line( std::string policy_line, unsigned int label_id, struct bpf_labels* labels, - enum block_action action = ACTION_RET_KILL) { - return compile_policy_line(state, nr, policy_line.c_str(), label_id, - labels, action); + enum ret_trap do_ret_trap = USE_RET_KILL) { + return compile_policy_line(state, nr, policy_line.c_str(), label_id, labels, + do_ret_trap); } } // namespace @@ -532,86 +522,6 @@ TEST_F(ArgFilterTest, arg0_equals) { free_block_list(block); } -TEST_F(ArgFilterTest, arg0_equals_trap) { - std::string fragment = "arg0 == 0"; - - struct filter_block* block = test_compile_policy_line( - &state_, nr_, fragment, id_, &labels_, ACTION_RET_TRAP); - - ASSERT_NE(block, nullptr); - size_t exp_total_len = 1 + (BPF_ARG_COMP_LEN + 1) + 2 + 1 + 2; - EXPECT_EQ(block->total_len, exp_total_len); - - /* First block is a label. */ - struct filter_block* curr_block = block; - ASSERT_NE(curr_block, nullptr); - EXPECT_EQ(curr_block->len, 1U); - EXPECT_LBL(curr_block->instrs); - - /* Second block is a comparison. */ - curr_block = curr_block->next; - EXPECT_COMP(curr_block); - - /* Third block is a jump and a label (end of AND group). */ - curr_block = curr_block->next; - ASSERT_NE(curr_block, nullptr); - EXPECT_GROUP_END(curr_block); - - /* Fourth block is SECCOMP_RET_TRAP. */ - curr_block = curr_block->next; - ASSERT_NE(curr_block, nullptr); - EXPECT_TRAP(curr_block); - - /* Fifth block is "SUCCESS" label and SECCOMP_RET_ALLOW. */ - curr_block = curr_block->next; - ASSERT_NE(curr_block, nullptr); - EXPECT_ALLOW(curr_block); - - EXPECT_EQ(curr_block->next, nullptr); - - free_block_list(block); -} - -TEST_F(ArgFilterTest, arg0_equals_log) { - std::string fragment = "arg0 == 0"; - - struct filter_block* block = test_compile_policy_line( - &state_, nr_, fragment, id_, &labels_, ACTION_RET_LOG); - - ASSERT_NE(block, nullptr); - size_t exp_total_len = 1 + (BPF_ARG_COMP_LEN + 1) + 2 + 1 + 2; - EXPECT_EQ(block->total_len, exp_total_len); - - /* First block is a label. */ - struct filter_block* curr_block = block; - ASSERT_NE(curr_block, nullptr); - EXPECT_EQ(curr_block->len, 1U); - EXPECT_LBL(curr_block->instrs); - - /* Second block is a comparison. */ - curr_block = curr_block->next; - EXPECT_COMP(curr_block); - - /* Third block is a jump and a label (end of AND group). */ - curr_block = curr_block->next; - ASSERT_NE(curr_block, nullptr); - EXPECT_GROUP_END(curr_block); - - /* Fourth block is SECCOMP_RET_LOG. */ - curr_block = curr_block->next; - ASSERT_NE(curr_block, nullptr); - EXPECT_LOG(curr_block); - - /* Fifth block is "SUCCESS" label and SECCOMP_RET_ALLOW. */ - curr_block = curr_block->next; - ASSERT_NE(curr_block, nullptr); - EXPECT_ALLOW(curr_block); - - EXPECT_EQ(curr_block->next, nullptr); - - free_block_list(block); -} - TEST_F(ArgFilterTest, arg0_short_gt_ge_comparisons) { for (std::string fragment : {"arg1 < 0xff", "arg1 <= 0xff", "arg1 > 0xff", "arg1 >= 0xff"}) { @@ -1018,7 +928,7 @@ TEST_F(ArgFilterTest, log_no_ret_error) { struct filter_block* block = test_compile_policy_line(&state_, nr_, fragment, id_, &labels_, - ACTION_RET_TRAP); + USE_RET_TRAP); ASSERT_NE(block, nullptr); size_t exp_total_len = 1 + (BPF_ARG_COMP_LEN + 1) + 2 + 1 + 2; @@ -1102,7 +1012,7 @@ TEST_F(ArgFilterTest, no_log_bad_ret_error) { struct filter_block* block = test_compile_policy_line(&state_, nr_, fragment, id_, &labels_, - ACTION_RET_TRAP); + USE_RET_TRAP); ASSERT_NE(block, nullptr); size_t exp_total_len = 1 + (BPF_ARG_COMP_LEN + 1) + 2 + 1 + 2; EXPECT_EQ(block->total_len, exp_total_len); @@ -1354,7 +1264,7 @@ TEST_F(FileTest, seccomp_read) { TEST_F(FileTest, multiline) { std::string policy = "read:\\\n1\n" - "openat:arg0 \\\nin\\\n \\\n5"; + "openat:arg0 in\\\n5"; const int LABEL_ID = 0; @@ -1434,8 +1344,7 @@ TEST(FilterTest, seccomp_mode1_trap) { FILE* policy_file = write_policy_to_pipe(policy); ASSERT_NE(policy_file, nullptr); - int res = - test_compile_filter("policy", policy_file, &actual, ACTION_RET_TRAP); + int res = test_compile_filter("policy", policy_file, &actual, USE_RET_TRAP); fclose(policy_file); /* @@ -1462,66 +1371,6 @@ TEST(FilterTest, seccomp_mode1_trap) { free(actual.filter); } -TEST(FilterTest, seccomp_mode1_log) { - struct sock_fprog actual; - std::string policy = - "read: 1\n" - "write: 1\n" - "rt_sigreturn: 1\n" - "exit: 1\n"; - - FILE* policy_file = write_policy_to_pipe(policy); - ASSERT_NE(policy_file, nullptr); - - int res = test_compile_filter("policy", policy_file, &actual, ACTION_RET_LOG, - USE_RET_LOG_LOGGING); - fclose(policy_file); - - /* - * Checks return value, filter length, and that the filter - * validates arch, loads syscall number, and - * only allows expected syscalls. - */ - ASSERT_EQ(res, 0); - EXPECT_EQ(actual.len, 13); - EXPECT_ARCH_VALIDATION(actual.filter); - EXPECT_EQ_STMT(actual.filter + ARCH_VALIDATION_LEN, - BPF_LD+BPF_W+BPF_ABS, syscall_nr); - EXPECT_ALLOW_SYSCALL(actual.filter + ARCH_VALIDATION_LEN + 1, - __NR_read); - EXPECT_ALLOW_SYSCALL(actual.filter + ARCH_VALIDATION_LEN + 3, - __NR_write); - EXPECT_ALLOW_SYSCALL(actual.filter + ARCH_VALIDATION_LEN + 5, - __NR_rt_sigreturn); - EXPECT_ALLOW_SYSCALL(actual.filter + ARCH_VALIDATION_LEN + 7, - __NR_exit); - EXPECT_EQ_STMT(actual.filter + ARCH_VALIDATION_LEN + 9, BPF_RET+BPF_K, - SECCOMP_RET_LOG); - - free(actual.filter); -} - -TEST(FilterTest, seccomp_mode1_log_fails) { - struct sock_fprog actual; - std::string policy = - "read: 1\n" - "write: 1\n" - "rt_sigreturn: 1\n" - "exit: 1\n"; - - FILE* policy_file = write_policy_to_pipe(policy); - ASSERT_NE(policy_file, nullptr); - - int res = test_compile_filter("policy", policy_file, &actual, ACTION_RET_LOG, - NO_LOGGING); - fclose(policy_file); - - /* - * ACTION_RET_LOG should never be used without allowing logging. - */ - ASSERT_EQ(res, -1); -} - TEST(FilterTest, seccomp_read_write) { struct sock_fprog actual; std::string policy = @@ -1662,8 +1511,8 @@ TEST(FilterTest, log) { FILE* policy_file = write_policy_to_pipe(policy); ASSERT_NE(policy_file, nullptr); - int res = test_compile_filter("policy", policy_file, &actual, ACTION_RET_TRAP, - USE_SIGSYS_LOGGING); + int res = test_compile_filter("policy", policy_file, &actual, USE_RET_TRAP, + USE_LOGGING); fclose(policy_file); size_t i; @@ -1709,8 +1558,8 @@ TEST(FilterTest, allow_log_but_kill) { FILE* policy_file = write_policy_to_pipe(policy); ASSERT_NE(policy_file, nullptr); - int res = test_compile_filter("policy", policy_file, &actual, ACTION_RET_KILL, - USE_SIGSYS_LOGGING); + int res = test_compile_filter("policy", policy_file, &actual, USE_RET_KILL, + USE_LOGGING); fclose(policy_file); size_t i; @@ -1718,7 +1567,7 @@ TEST(FilterTest, allow_log_but_kill) { /* * Checks return value, filter length, and that the filter * validates arch, loads syscall number, only allows expected syscalls, - * and kills on failure. + * and returns TRAP on failure. * NOTE(jorgelo): the filter is longer since we add the syscalls needed * for logging. */ @@ -1863,7 +1712,7 @@ TEST(FilterTest, include) { FILE* file_plain = write_policy_to_pipe(policy_plain); ASSERT_NE(file_plain, nullptr); int res_plain = test_compile_filter("policy", file_plain, &compiled_plain, - ACTION_RET_KILL); + USE_RET_KILL); fclose(file_plain); std::string policy_with_include = @@ -1871,8 +1720,9 @@ TEST(FilterTest, include) { FILE* file_with_include = write_policy_to_pipe(policy_with_include); ASSERT_NE(file_with_include, nullptr); - int res_with_include = test_compile_filter( - "policy", file_with_include, &compiled_with_include, ACTION_RET_KILL); + int res_with_include = + test_compile_filter("policy", file_with_include, &compiled_with_include, + USE_RET_KILL); fclose(file_with_include); /* diff --git a/syscall_filter_unittest_macros.h b/syscall_filter_unittest_macros.h index 3972cb41..4923fc4c 100644 --- a/syscall_filter_unittest_macros.h +++ b/syscall_filter_unittest_macros.h @@ -3,11 +3,6 @@ * found in the LICENSE file. */ -#ifndef SYSCALL_FILTER_UNITTEST_MACROS_H -#define SYSCALL_FILTER_UNITTEST_MACROS_H - -#include "bpf.h" - /* BPF testing macros. */ #define EXPECT_EQ_BLOCK(_block, _code, _k, _jt, _jf) \ do { \ @@ -81,13 +76,6 @@ do { \ BPF_RET+BPF_K, SECCOMP_RET_TRAP); \ } while (0) -#define EXPECT_LOG(_block) \ -do { \ - EXPECT_EQ((_block)->len, 1U); \ - EXPECT_EQ_STMT((_block)->instrs, \ - BPF_RET+BPF_K, SECCOMP_RET_LOG); \ -} while (0) - #define EXPECT_ALLOW(_block) \ do { \ EXPECT_EQ((_block)->len, 2U); \ @@ -119,5 +107,3 @@ do { \ EXPECT_EQ_BLOCK(&(_filter)[1], \ BPF_JMP+BPF_JA, (_id), (_jt), (_jf)); \ } while (0) - -#endif // SYSCALL_FILTER_UNITTEST_MACROS_H @@ -222,17 +222,14 @@ int setup_pipe_end(int fds[2], size_t index) return fds[index]; } -int dupe_and_close_fd(int fds[2], size_t index, int fd) +int setup_and_dupe_pipe_end(int fds[2], size_t index, int fd) { if (index > 1) return -1; + close(fds[1 - index]); /* dup2(2) the corresponding end of the pipe into |fd|. */ - fd = dup2(fds[index], fd); - - close(fds[0]); - close(fds[1]); - return fd; + return dup2(fds[index], fd); } int write_pid_to_path(pid_t pid, const char *path) @@ -467,61 +464,3 @@ int lookup_group(const char *group, gid_t *gid) *gid = pgr->gr_gid; return 0; } - -static int seccomp_action_is_available(const char *wanted) -{ - if (is_android()) { - /* - * Accessing |actions_avail| is generating SELinux denials, so - * skip for now. - * TODO(crbug.com/978022, jorgelo): Remove once the denial is - * fixed. - */ - return 0; - } - const char actions_avail_path[] = - "/proc/sys/kernel/seccomp/actions_avail"; - FILE *f = fopen(actions_avail_path, "re"); - - if (!f) { - pwarn("fopen(%s) failed", actions_avail_path); - return 0; - } - - char *actions_avail = NULL; - size_t buf_size = 0; - if (getline(&actions_avail, &buf_size, f) < 0) { - pwarn("getline() failed"); - free(actions_avail); - return 0; - } - - /* - * This is just substring search, which means that partial matches will - * match too (e.g. "action" would match "longaction"). There are no - * seccomp actions which include other actions though, so we're good for - * now. Eventually we might want to split the string by spaces. - */ - return strstr(actions_avail, wanted) != NULL; -} - -int seccomp_ret_log_available(void) -{ - static int ret_log_available = -1; - - if (ret_log_available == -1) - ret_log_available = seccomp_action_is_available("log"); - - return ret_log_available; -} - -int seccomp_ret_kill_process_available(void) -{ - static int ret_kill_process_available = -1; - - if (ret_kill_process_available == -1) - ret_kill_process_available = - seccomp_action_is_available("kill_process"); - - return ret_kill_process_available; -} @@ -47,7 +47,7 @@ int cap_ambient_supported(void); int config_net_loopback(void); int setup_pipe_end(int fds[2], size_t index); -int dupe_and_close_fd(int fds[2], size_t index, int fd); +int setup_and_dupe_pipe_end(int fds[2], size_t index, int fd); int write_pid_to_path(pid_t pid, const char *path); int write_proc_file(pid_t pid, const char *content, const char *basename); @@ -60,9 +60,6 @@ int setup_mount_destination(const char *source, const char *dest, uid_t uid, int lookup_user(const char *user, uid_t *uid, gid_t *gid); int lookup_group(const char *group, gid_t *gid); -int seccomp_ret_log_available(void); -int seccomp_ret_kill_process_available(void); - #ifdef __cplusplus }; /* extern "C" */ #endif diff --git a/system_unittest.cc b/system_unittest.cc index e13630ab..36d8dc3b 100644 --- a/system_unittest.cc +++ b/system_unittest.cc @@ -187,10 +187,10 @@ TEST(setup_pipe_end, index1) { } // Invalid indexes should return errors, not crash. -TEST(dupe_and_close_fd, bad_index) { - EXPECT_LT(dupe_and_close_fd(nullptr, 2, -1), 0); - EXPECT_LT(dupe_and_close_fd(nullptr, 3, -1), 0); - EXPECT_LT(dupe_and_close_fd(nullptr, 4, -1), 0); +TEST(setup_and_dupe_pipe_end, bad_index) { + EXPECT_LT(setup_and_dupe_pipe_end(nullptr, 2, -1), 0); + EXPECT_LT(setup_and_dupe_pipe_end(nullptr, 3, -1), 0); + EXPECT_LT(setup_and_dupe_pipe_end(nullptr, 4, -1), 0); } // An invalid path should return an error. @@ -260,7 +260,7 @@ TEST(setup_mount_destination, dest_exists) { EXPECT_EQ(0, setup_mount_destination(nullptr, "/dev", 0, 0, false, nullptr)); } -// Mount flags should be obtained for bind-mounts. +// Mount flags should be obtained for bind-mounts TEST(setup_mount_destination, mount_flags) { struct statvfs stvfs_buf; ASSERT_EQ(0, statvfs("/proc", &stvfs_buf)); @@ -362,8 +362,3 @@ TEST(setup_mount_destination, create_char_dev) { // We check it's a directory by deleting it as such. EXPECT_EQ(0, rmdir(child_dev.c_str())); } - -TEST(seccomp_actions_available, smoke) { - seccomp_ret_log_available(); - seccomp_ret_kill_process_available(); -} @@ -9,7 +9,6 @@ #ifndef _UTIL_H_ #define _UTIL_H_ -#include <stdbool.h> #include <stdio.h> #include <stdlib.h> #include <sys/types.h> @@ -106,40 +105,12 @@ static inline int is_android(void) #endif } -static inline bool compiled_with_asan(void) -{ -#if defined(__SANITIZE_ADDRESS__) - /* For gcc. */ - return true; -#elif defined(__has_feature) - /* For clang. */ - return __has_feature(address_sanitizer) || - __has_feature(hwaddress_sanitizer); -#else - return false; -#endif -} - void __asan_init(void) attribute_weak; void __hwasan_init(void) attribute_weak; -static inline bool running_with_asan(void) +static inline int running_with_asan(void) { - /* - * There are some configurations under which ASan needs a dynamic (as - * opposed to compile-time) test. Some Android processes that start - * before /data is mounted run with non-instrumented libminijail.so, so - * the symbol-sniffing code must be present to make the right decision. - */ - return compiled_with_asan() || &__asan_init != 0 || &__hwasan_init != 0; -} - -static inline bool debug_logging_allowed(void) { -#if defined(ALLOW_DEBUG_LOGGING) - return true; -#else - return false; -#endif + return &__asan_init != 0 || &__hwasan_init != 0; } int lookup_syscall(const char *name); |