/* Copyright 2016 The Chromium OS Authors. All rights reserved. * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. * * Test platform independent logic of Minijail using gtest. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "libminijail-private.h" #include "libminijail.h" #include "scoped_minijail.h" #include "util.h" namespace { #if defined(__ANDROID__) # define ROOT_PREFIX "/system" #else # define ROOT_PREFIX "" #endif constexpr char kShellPath[] = ROOT_PREFIX "/bin/sh"; constexpr char kCatPath[] = ROOT_PREFIX "/bin/cat"; constexpr char kPreloadPath[] = "./libminijailpreload.so"; constexpr size_t kBufferSize = 128; std::set GetProcessSubtreePids(pid_t root_pid) { std::set pids{root_pid}; bool progress = false; do { progress = false; DIR* d = opendir("/proc"); if (!d) pdie("opendir(\"/proc\")"); struct dirent* dir_entry; while ((dir_entry = readdir(d)) != nullptr) { if (dir_entry->d_type != DT_DIR) continue; char* end; const int pid = strtol(dir_entry->d_name, &end, 10); if (*end != '\0') continue; std::string path = "/proc/" + std::to_string(pid) + "/stat"; FILE* f = fopen(path.c_str(), "re"); if (!f) pdie("fopen(%s)", path.c_str()); pid_t ppid; int ret = fscanf(f, "%*d (%*[^)]) %*c %d", &ppid); fclose(f); if (ret != 1) { continue; } if (pids.find(ppid) == pids.end()) continue; progress |= pids.insert(pid).second; } closedir(d); } while (progress); return pids; } std::map GetNamespaces( pid_t pid, const std::vector& namespace_names) { std::map namespaces; char buf[kBufferSize]; for (const auto& namespace_name : namespace_names) { std::string path = "/proc/" + std::to_string(pid) + "/ns/" + namespace_name; ssize_t len = readlink(path.c_str(), buf, sizeof(buf)); if (len == -1) pdie("readlink(\"%s\")", path.c_str()); namespaces.emplace(namespace_name, std::string(buf, len)); } return namespaces; } } // namespace /* Prototypes needed only by test. */ size_t minijail_get_tmpfs_size(const struct minijail *); /* Silence unused variable warnings. */ TEST(silence, silence_unused) { EXPECT_STREQ(kLdPreloadEnvVar, kLdPreloadEnvVar); EXPECT_STREQ(kFdEnvVar, kFdEnvVar); EXPECT_STRNE(kFdEnvVar, kLdPreloadEnvVar); } TEST(consumebytes, zero) { char buf[1024]; size_t len = sizeof(buf); char *pos = &buf[0]; EXPECT_NE(nullptr, consumebytes(0, &pos, &len)); EXPECT_EQ(&buf[0], pos); EXPECT_EQ(sizeof(buf), len); } TEST(consumebytes, exact) { char buf[1024]; size_t len = sizeof(buf); char *pos = &buf[0]; /* One past the end since it consumes the whole buffer. */ char *end = &buf[sizeof(buf)]; EXPECT_NE(nullptr, consumebytes(len, &pos, &len)); EXPECT_EQ((size_t)0, len); EXPECT_EQ(end, pos); } TEST(consumebytes, half) { char buf[1024]; size_t len = sizeof(buf); char *pos = &buf[0]; /* One past the end since it consumes the whole buffer. */ char *end = &buf[sizeof(buf) / 2]; EXPECT_NE(nullptr, consumebytes(len / 2, &pos, &len)); EXPECT_EQ(sizeof(buf) / 2, len); EXPECT_EQ(end, pos); } TEST(consumebytes, toolong) { char buf[1024]; size_t len = sizeof(buf); char *pos = &buf[0]; /* One past the end since it consumes the whole buffer. */ EXPECT_EQ(nullptr, consumebytes(len + 1, &pos, &len)); EXPECT_EQ(sizeof(buf), len); EXPECT_EQ(&buf[0], pos); } TEST(consumestr, zero) { char buf[1024]; size_t len = 0; char *pos = &buf[0]; memset(buf, 0xff, sizeof(buf)); EXPECT_EQ(nullptr, consumestr(&pos, &len)); EXPECT_EQ((size_t)0, len); EXPECT_EQ(&buf[0], pos); } TEST(consumestr, nonul) { char buf[1024]; size_t len = sizeof(buf); char *pos = &buf[0]; memset(buf, 0xff, sizeof(buf)); EXPECT_EQ(nullptr, consumestr(&pos, &len)); EXPECT_EQ(sizeof(buf), len); EXPECT_EQ(&buf[0], pos); } TEST(consumestr, full) { char buf[1024]; size_t len = sizeof(buf); char *pos = &buf[0]; memset(buf, 0xff, sizeof(buf)); buf[sizeof(buf)-1] = '\0'; EXPECT_EQ((void *)buf, consumestr(&pos, &len)); EXPECT_EQ((size_t)0, len); EXPECT_EQ(&buf[sizeof(buf)], pos); } TEST(consumestr, trailing_nul) { char buf[1024]; size_t len = sizeof(buf) - 1; char *pos = &buf[0]; memset(buf, 0xff, sizeof(buf)); buf[sizeof(buf)-1] = '\0'; EXPECT_EQ(nullptr, consumestr(&pos, &len)); EXPECT_EQ(sizeof(buf) - 1, len); EXPECT_EQ(&buf[0], pos); } class MarshalTest : public ::testing::Test { protected: virtual void SetUp() { m_ = minijail_new(); j_ = minijail_new(); size_ = minijail_size(m_); } virtual void TearDown() { minijail_destroy(m_); minijail_destroy(j_); } char buf_[4096]; struct minijail *m_; struct minijail *j_; size_t size_; }; TEST_F(MarshalTest, empty) { ASSERT_EQ(0, minijail_marshal(m_, buf_, sizeof(buf_))); EXPECT_EQ(0, minijail_unmarshal(j_, buf_, size_)); } TEST_F(MarshalTest, 0xff) { memset(buf_, 0xff, sizeof(buf_)); /* Should fail on the first consumestr since a NUL will never be found. */ EXPECT_EQ(-EINVAL, minijail_unmarshal(j_, buf_, sizeof(buf_))); } TEST(Test, minijail_run_pid_pipes) { constexpr char teststr[] = "test\n"; struct minijail* j = minijail_new(); minijail_set_preload_path(j, kPreloadPath); char* argv[4]; argv[0] = const_cast(kCatPath); argv[1] = nullptr; pid_t pid; int child_stdin, child_stdout; 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); ssize_t write_ret = write(child_stdin, teststr, teststr_len); EXPECT_EQ(write_ret, static_cast(teststr_len)); char buf[kBufferSize]; ssize_t read_ret = read(child_stdout, buf, 8); EXPECT_EQ(read_ret, static_cast(teststr_len)); buf[teststr_len] = 0; EXPECT_EQ(strcmp(buf, teststr), 0); int status; EXPECT_EQ(kill(pid, SIGTERM), 0); waitpid(pid, &status, 0); ASSERT_TRUE(WIFSIGNALED(status)); EXPECT_EQ(WTERMSIG(status), SIGTERM); argv[0] = const_cast(kShellPath); argv[1] = "-c"; argv[2] = "echo test >&2"; argv[3] = nullptr; int 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)); EXPECT_GE(read_ret, static_cast(teststr_len)); waitpid(pid, &status, 0); ASSERT_TRUE(WIFEXITED(status)); EXPECT_EQ(WEXITSTATUS(status), 0); minijail_destroy(j); } TEST(Test, minijail_run_pid_pipes_no_preload) { pid_t pid; int child_stdin, child_stdout, child_stderr; int mj_run_ret; ssize_t write_ret, read_ret; char buf[kBufferSize]; int status; char teststr[] = "test\n"; size_t teststr_len = strlen(teststr); char *argv[4]; struct minijail *j = minijail_new(); argv[0] = (char*)kCatPath; argv[1] = NULL; mj_run_ret = minijail_run_pid_pipes_no_preload(j, argv[0], argv, &pid, &child_stdin, &child_stdout, NULL); EXPECT_EQ(mj_run_ret, 0); write_ret = write(child_stdin, teststr, teststr_len); EXPECT_EQ(write_ret, (int)teststr_len); read_ret = read(child_stdout, buf, 8); EXPECT_EQ(read_ret, (int)teststr_len); buf[teststr_len] = 0; EXPECT_EQ(strcmp(buf, teststr), 0); EXPECT_EQ(kill(pid, SIGTERM), 0); waitpid(pid, &status, 0); ASSERT_TRUE(WIFSIGNALED(status)); EXPECT_EQ(WTERMSIG(status), SIGTERM); argv[0] = (char*)kShellPath; argv[1] = "-c"; argv[2] = "echo test >&2"; argv[3] = NULL; mj_run_ret = minijail_run_pid_pipes_no_preload(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)); EXPECT_GE(read_ret, (int)teststr_len); waitpid(pid, &status, 0); ASSERT_TRUE(WIFEXITED(status)); EXPECT_EQ(WEXITSTATUS(status), 0); minijail_destroy(j); } TEST(Test, minijail_run_env_pid_pipes_no_preload) { pid_t pid; int child_stdin, child_stdout, child_stderr; int mj_run_ret; ssize_t read_ret; char buf[kBufferSize]; int status; char test_envvar[] = "TEST_VAR=test"; size_t testvar_len = strlen("test"); char *argv[4]; char *envp[2]; struct minijail *j = minijail_new(); argv[0] = (char*)kShellPath; argv[1] = "-c"; argv[2] = "echo \"${TEST_PARENT+set}|${TEST_VAR}\""; argv[3] = NULL; envp[0] = test_envvar; envp[1] = NULL; // Set a canary env var in the parent that should not be present in the child. ASSERT_EQ(setenv("TEST_PARENT", "test", 1 /*overwrite*/), 0); mj_run_ret = minijail_run_env_pid_pipes_no_preload( j, argv[0], argv, envp, &pid, &child_stdin, &child_stdout, &child_stderr); EXPECT_EQ(mj_run_ret, 0); read_ret = read(child_stdout, buf, sizeof(buf)); EXPECT_GE(read_ret, (int)testvar_len); EXPECT_EQ("|test\n", std::string(buf)); EXPECT_EQ(waitpid(pid, &status, 0), pid); ASSERT_TRUE(WIFEXITED(status)); EXPECT_EQ(WEXITSTATUS(status), 0); minijail_destroy(j); } TEST(Test, test_minijail_no_fd_leaks) { pid_t pid; int child_stdout; int mj_run_ret; ssize_t read_ret; char buf[kBufferSize]; char script[kBufferSize]; int status; char *argv[4]; int dev_null = open("/dev/null", O_RDONLY); ASSERT_NE(dev_null, -1); snprintf(script, sizeof(script), "[ -e /proc/self/fd/%d ] && echo yes || echo no", dev_null); struct minijail *j = minijail_new(); argv[0] = (char*)kShellPath; argv[1] = "-c"; argv[2] = script; argv[3] = NULL; mj_run_ret = minijail_run_pid_pipes_no_preload( j, argv[0], argv, &pid, NULL, &child_stdout, NULL); EXPECT_EQ(mj_run_ret, 0); read_ret = read(child_stdout, buf, sizeof(buf)); EXPECT_GE(read_ret, 0); buf[read_ret] = '\0'; EXPECT_STREQ(buf, "yes\n"); waitpid(pid, &status, 0); ASSERT_TRUE(WIFEXITED(status)); EXPECT_EQ(WEXITSTATUS(status), 0); minijail_close_open_fds(j); mj_run_ret = minijail_run_pid_pipes_no_preload( j, argv[0], argv, &pid, NULL, &child_stdout, NULL); EXPECT_EQ(mj_run_ret, 0); read_ret = read(child_stdout, buf, sizeof(buf)); EXPECT_GE(read_ret, 0); buf[read_ret] = '\0'; EXPECT_STREQ(buf, "no\n"); waitpid(pid, &status, 0); ASSERT_TRUE(WIFEXITED(status)); EXPECT_EQ(WEXITSTATUS(status), 0); minijail_destroy(j); close(dev_null); } TEST(Test, test_minijail_fork) { pid_t mj_fork_ret; int status; int pipe_fds[2]; ssize_t pid_size = sizeof(mj_fork_ret); struct minijail *j = minijail_new(); ASSERT_EQ(pipe(pipe_fds), 0); 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); } EXPECT_EQ(write(pipe_fds[1], &mj_fork_ret, pid_size), pid_size); waitpid(mj_fork_ret, &status, 0); ASSERT_TRUE(WIFEXITED(status)); EXPECT_EQ(WEXITSTATUS(status), 0); minijail_destroy(j); } static int early_exit(void* payload) { exit(static_cast(reinterpret_cast(payload))); } TEST(Test, test_minijail_callback) { pid_t pid; int mj_run_ret; int status; char *argv[2]; int exit_code = 42; struct minijail *j = minijail_new(); status = minijail_add_hook(j, &early_exit, reinterpret_cast(exit_code), MINIJAIL_HOOK_EVENT_PRE_DROP_CAPS); EXPECT_EQ(status, 0); argv[0] = (char*)kCatPath; argv[1] = NULL; mj_run_ret = minijail_run_pid_pipes_no_preload(j, argv[0], argv, &pid, NULL, NULL, NULL); EXPECT_EQ(mj_run_ret, 0); status = minijail_wait(j); EXPECT_EQ(status, exit_code); minijail_destroy(j); } TEST(Test, test_minijail_preserve_fd) { int mj_run_ret; int status; char *argv[2]; char teststr[] = "test\n"; size_t teststr_len = strlen(teststr); int read_pipe[2]; int write_pipe[2]; char buf[1024]; struct minijail *j = minijail_new(); status = pipe(read_pipe); ASSERT_EQ(status, 0); status = pipe(write_pipe); ASSERT_EQ(status, 0); status = minijail_preserve_fd(j, write_pipe[0], STDIN_FILENO); ASSERT_EQ(status, 0); status = minijail_preserve_fd(j, read_pipe[1], STDOUT_FILENO); ASSERT_EQ(status, 0); minijail_close_open_fds(j); argv[0] = (char*)kCatPath; argv[1] = NULL; mj_run_ret = minijail_run_no_preload(j, argv[0], argv); EXPECT_EQ(mj_run_ret, 0); close(write_pipe[0]); status = write(write_pipe[1], teststr, teststr_len); EXPECT_EQ(status, (int)teststr_len); close(write_pipe[1]); close(read_pipe[1]); status = read(read_pipe[0], buf, 8); EXPECT_EQ(status, (int)teststr_len); buf[teststr_len] = 0; EXPECT_EQ(strcmp(buf, teststr), 0); status = minijail_wait(j); EXPECT_EQ(status, 0); minijail_destroy(j); } TEST(Test, test_minijail_reset_signal_mask) { struct minijail *j = minijail_new(); sigset_t original_signal_mask; { sigset_t signal_mask; ASSERT_EQ(0, sigemptyset(&signal_mask)); ASSERT_EQ(0, sigaddset(&signal_mask, SIGUSR1)); ASSERT_EQ(0, sigprocmask(SIG_SETMASK, &signal_mask, &original_signal_mask)); } minijail_reset_signal_mask(j); pid_t mj_fork_ret = minijail_fork(j); ASSERT_GE(mj_fork_ret, 0); if (mj_fork_ret == 0) { sigset_t signal_mask; ASSERT_EQ(0, sigprocmask(SIG_SETMASK, NULL, &signal_mask)); ASSERT_EQ(0, sigismember(&signal_mask, SIGUSR1)); minijail_destroy(j); exit(0); } ASSERT_EQ(0, sigprocmask(SIG_SETMASK, &original_signal_mask, NULL)); int status; waitpid(mj_fork_ret, &status, 0); ASSERT_TRUE(WIFEXITED(status)); EXPECT_EQ(WEXITSTATUS(status), 0); minijail_destroy(j); } TEST(Test, test_minijail_reset_signal_handlers) { struct minijail *j = minijail_new(); ASSERT_EQ(SIG_DFL, signal(SIGUSR1, SIG_DFL)); ASSERT_EQ(SIG_DFL, signal(SIGUSR1, SIG_IGN)); ASSERT_EQ(SIG_IGN, signal(SIGUSR1, SIG_IGN)); minijail_reset_signal_handlers(j); pid_t mj_fork_ret = minijail_fork(j); ASSERT_GE(mj_fork_ret, 0); if (mj_fork_ret == 0) { ASSERT_EQ(SIG_DFL, signal(SIGUSR1, SIG_DFL)); minijail_destroy(j); exit(0); } ASSERT_NE(SIG_ERR, signal(SIGUSR1, SIG_DFL)); int status; waitpid(mj_fork_ret, &status, 0); ASSERT_TRUE(WIFEXITED(status)); EXPECT_EQ(WEXITSTATUS(status), 0); minijail_destroy(j); } namespace { // Tests that require userns access. // Android unit tests don't currently support entering user namespaces as // unprivileged users due to having an older kernel. Chrome OS unit tests // don't support it either due to being in a chroot environment (see man 2 // clone for more information about failure modes with the CLONE_NEWUSER flag). class NamespaceTest : public ::testing::Test { protected: static void SetUpTestCase() { userns_supported_ = UsernsSupported(); } // Whether userns is supported. static bool userns_supported_; static bool UsernsSupported() { pid_t pid = fork(); if (pid == -1) pdie("could not fork"); if (pid == 0) _exit(unshare(CLONE_NEWUSER) == 0 ? 0 : 1); int status; if (waitpid(pid, &status, 0) < 0) pdie("could not wait"); if (!WIFEXITED(status)) die("child did not exit properly: %#x", status); bool ret = WEXITSTATUS(status) == 0; if (!ret) warn("Skipping userns related tests"); return ret; } }; bool NamespaceTest::userns_supported_; } // namespace TEST_F(NamespaceTest, test_tmpfs_userns) { int mj_run_ret; int status; char *argv[4]; char uidmap[kBufferSize], gidmap[kBufferSize]; constexpr uid_t kTargetUid = 1000; // Any non-zero value will do. constexpr gid_t kTargetGid = 1000; if (!userns_supported_) { SUCCEED(); return; } struct minijail *j = minijail_new(); minijail_namespace_pids(j); minijail_namespace_vfs(j); minijail_mount_tmp(j); minijail_run_as_init(j); // Perform userns mapping. minijail_namespace_user(j); snprintf(uidmap, sizeof(uidmap), "%d %d 1", kTargetUid, getuid()); snprintf(gidmap, sizeof(gidmap), "%d %d 1", kTargetGid, getgid()); minijail_change_uid(j, kTargetUid); minijail_change_gid(j, kTargetGid); minijail_uidmap(j, uidmap); minijail_gidmap(j, gidmap); minijail_namespace_user_disable_setgroups(j); argv[0] = (char*)kShellPath; argv[1] = "-c"; argv[2] = "exec touch /tmp/foo"; argv[3] = NULL; mj_run_ret = minijail_run_no_preload(j, argv[0], argv); EXPECT_EQ(mj_run_ret, 0); status = minijail_wait(j); EXPECT_EQ(status, 0); minijail_destroy(j); } TEST_F(NamespaceTest, test_namespaces) { constexpr char teststr[] = "test\n"; if (!userns_supported_) { SUCCEED(); return; } std::string uidmap = "0 " + std::to_string(getuid()) + " 1"; std::string gidmap = "0 " + std::to_string(getgid()) + " 1"; const std::vector namespace_names = {"pid", "mnt", "user", "net", "cgroup", "uts"}; // Grab the set of namespaces outside the container. std::map init_namespaces = GetNamespaces(getpid(), namespace_names); std::function test_functions[] = { [](struct minijail* j attribute_unused) {}, [](struct minijail* j) { minijail_mount(j, "/", "/", "", MS_BIND | MS_REC | MS_RDONLY); minijail_enter_pivot_root(j, "/tmp"); }, [](struct minijail* j) { minijail_enter_chroot(j, "/"); }, }; // This test is run with and without the preload library. for (const auto& run_function : {minijail_run_pid_pipes, minijail_run_pid_pipes_no_preload}) { for (const auto& test_function : test_functions) { ScopedMinijail j(minijail_new()); minijail_set_preload_path(j.get(), kPreloadPath); // Enter all the namespaces we can. minijail_namespace_cgroups(j.get()); minijail_namespace_net(j.get()); minijail_namespace_pids(j.get()); minijail_namespace_user(j.get()); minijail_namespace_vfs(j.get()); minijail_namespace_uts(j.get()); // Set up the user namespace. minijail_uidmap(j.get(), uidmap.c_str()); minijail_gidmap(j.get(), gidmap.c_str()); minijail_namespace_user_disable_setgroups(j.get()); minijail_close_open_fds(j.get()); test_function(j.get()); const char* argv[] = {kCatPath, nullptr}; pid_t container_pid; int child_stdin, child_stdout; int mj_run_ret = run_function(j.get(), argv[0], const_cast(argv), &container_pid, &child_stdin, &child_stdout, nullptr); EXPECT_EQ(mj_run_ret, 0); // Send some data to stdin and read it back to ensure that the child // process is running. const size_t teststr_len = strlen(teststr); ssize_t write_ret = write(child_stdin, teststr, teststr_len); EXPECT_EQ(write_ret, static_cast(teststr_len)); char buf[kBufferSize]; ssize_t read_ret = read(child_stdout, buf, 8); EXPECT_EQ(read_ret, static_cast(teststr_len)); buf[teststr_len] = 0; EXPECT_EQ(strcmp(buf, teststr), 0); // Grab the set of namespaces in every container process. They must not // match the ones in the init namespace, and they must all match each // other. std::map container_namespaces = GetNamespaces(container_pid, namespace_names); EXPECT_NE(container_namespaces, init_namespaces); for (pid_t pid : GetProcessSubtreePids(container_pid)) EXPECT_EQ(container_namespaces, GetNamespaces(pid, namespace_names)); EXPECT_EQ(0, close(child_stdin)); int status = minijail_wait(j.get()); EXPECT_EQ(status, 0); } } } TEST_F(NamespaceTest, test_enter_ns) { char uidmap[kBufferSize], gidmap[kBufferSize]; if (!userns_supported_) { SUCCEED(); return; } // We first create a child in a new userns so we have privs to run more tests. // We can't combine the steps as the kernel disallows many resource sharing // from outside the userns. struct minijail *j = minijail_new(); minijail_namespace_vfs(j); minijail_namespace_pids(j); minijail_run_as_init(j); // Perform userns mapping. minijail_namespace_user(j); snprintf(uidmap, sizeof(uidmap), "0 %d 1", getuid()); snprintf(gidmap, sizeof(gidmap), "0 %d 1", getgid()); minijail_uidmap(j, uidmap); minijail_gidmap(j, gidmap); minijail_namespace_user_disable_setgroups(j); pid_t pid = minijail_fork(j); if (pid == 0) { // Child. minijail_destroy(j); // Create new namespaces inside this userns which we may enter. j = minijail_new(); minijail_namespace_net(j); minijail_namespace_vfs(j); pid = minijail_fork(j); if (pid == 0) { // Child. minijail_destroy(j); // Finally enter those namespaces. j = minijail_new(); // We need to get the absolute path because entering a new mntns will // implicitly chdir(/) for us. char *path = realpath(kPreloadPath, nullptr); ASSERT_NE(nullptr, path); minijail_set_preload_path(j, path); minijail_namespace_net(j); minijail_namespace_vfs(j); minijail_namespace_enter_net(j, "/proc/self/ns/net"); minijail_namespace_enter_vfs(j, "/proc/self/ns/mnt"); char *argv[] = {"/bin/true", nullptr}; EXPECT_EQ(0, minijail_run(j, argv[0], argv)); EXPECT_EQ(0, minijail_wait(j)); minijail_destroy(j); exit(0); } else { ASSERT_GT(pid, 0); EXPECT_EQ(0, minijail_wait(j)); minijail_destroy(j); exit(0); } } else { ASSERT_GT(pid, 0); EXPECT_EQ(0, minijail_wait(j)); minijail_destroy(j); } } TEST(Test, parse_size) { size_t size; ASSERT_EQ(0, parse_size(&size, "42")); ASSERT_EQ(42U, size); ASSERT_EQ(0, parse_size(&size, "16K")); ASSERT_EQ(16384U, size); ASSERT_EQ(0, parse_size(&size, "1M")); ASSERT_EQ(1024U * 1024, size); uint64_t gigabyte = 1024ULL * 1024 * 1024; ASSERT_EQ(0, parse_size(&size, "3G")); ASSERT_EQ(3U, size / gigabyte); ASSERT_EQ(0U, size % gigabyte); ASSERT_EQ(0, parse_size(&size, "4294967294")); ASSERT_EQ(3U, size / gigabyte); ASSERT_EQ(gigabyte - 2, size % gigabyte); #if __WORDSIZE == 64 uint64_t exabyte = gigabyte * 1024 * 1024 * 1024; ASSERT_EQ(0, parse_size(&size, "9E")); ASSERT_EQ(9U, size / exabyte); ASSERT_EQ(0U, size % exabyte); ASSERT_EQ(0, parse_size(&size, "15E")); ASSERT_EQ(15U, size / exabyte); ASSERT_EQ(0U, size % exabyte); ASSERT_EQ(0, parse_size(&size, "18446744073709551614")); ASSERT_EQ(15U, size / exabyte); ASSERT_EQ(exabyte - 2, size % exabyte); ASSERT_EQ(-ERANGE, parse_size(&size, "16E")); ASSERT_EQ(-ERANGE, parse_size(&size, "19E")); ASSERT_EQ(-EINVAL, parse_size(&size, "7GTPE")); #elif __WORDSIZE == 32 ASSERT_EQ(-ERANGE, parse_size(&size, "5G")); ASSERT_EQ(-ERANGE, parse_size(&size, "9G")); ASSERT_EQ(-ERANGE, parse_size(&size, "9E")); ASSERT_EQ(-ERANGE, parse_size(&size, "7GTPE")); #endif ASSERT_EQ(-EINVAL, parse_size(&size, "")); ASSERT_EQ(-EINVAL, parse_size(&size, "14u")); ASSERT_EQ(-EINVAL, parse_size(&size, "14.2G")); ASSERT_EQ(-EINVAL, parse_size(&size, "-1G")); ASSERT_EQ(-EINVAL, parse_size(&size, "; /bin/rm -- ")); }