aboutsummaryrefslogtreecommitdiff
path: root/libminijail_unittest.cc
diff options
context:
space:
mode:
Diffstat (limited to 'libminijail_unittest.cc')
-rw-r--r--libminijail_unittest.cc191
1 files changed, 190 insertions, 1 deletions
diff --git a/libminijail_unittest.cc b/libminijail_unittest.cc
index 78e3cfb..868b7d7 100644
--- a/libminijail_unittest.cc
+++ b/libminijail_unittest.cc
@@ -9,6 +9,7 @@
#include <dirent.h>
#include <fcntl.h>
+#include <sys/mman.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <sys/types.h>
@@ -473,6 +474,94 @@ TEST(Test, close_original_pipes_after_dup2) {
EXPECT_EQ(minijail_wait(j.get()), 42);
}
+TEST(Test, minijail_no_clobber_pipe_fd) {
+ const ScopedMinijail j(minijail_new());
+ char* const script = R"(
+ echo Hi >&1;
+ exec 1>&-;
+ exec 4>&-;
+ exec 7>&-;
+ read line1;
+ read line2;
+ echo "$line1$line2 and Goodbye" >&2;
+ exit 42;
+ )";
+ char* const argv[] = {"sh", "-c", script, nullptr};
+
+ const int npipes = 3;
+ int fds[npipes][2];
+
+ // Create pipes.
+ for (int i = 0; i < npipes; ++i) {
+ ASSERT_EQ(pipe(fds[i]), 0);
+ }
+
+ // All pipes are output pipes except for the first one which is used as
+ // input pipe.
+ std::swap(fds[0][0], fds[0][1]);
+
+ // Generate a lot of mappings to try to clobber any file descriptors generated
+ // by libminijail.
+ for (int offset = 0; offset < npipes * 3; offset += npipes) {
+ for (int i = 0 ; i < npipes; ++i) {
+ const int fd = fds[i][1];
+ minijail_preserve_fd(j.get(), fd, i + offset);
+ }
+ }
+
+ minijail_close_open_fds(j.get());
+
+ EXPECT_EQ(minijail_run_no_preload(j.get(), kShellPath, argv), 0);
+
+ // Close unused end of pipes.
+ for (int i = 0; i < npipes; ++i) {
+ const int fd = fds[i][1];
+ ASSERT_EQ(close(fd), 0);
+ }
+
+ const int in = fds[0][0];
+ const int out = fds[1][0];
+ const int err = fds[2][0];
+
+ char buf[PIPE_BUF];
+ ssize_t nbytes;
+
+ // Check that stdout pipe works.
+ nbytes = read(out, buf, PIPE_BUF);
+ ASSERT_GT(nbytes, 0);
+ EXPECT_EQ(std::string(buf, nbytes), "Hi\n");
+
+ // Check that the write end of stdout pipe got closed by the child process. If
+ // the child process kept other file descriptors connected to stdout, then the
+ // parent process wouldn't be able to detect that all write ends of this pipe
+ // are closed and it would block here.
+ EXPECT_EQ(read(out, buf, PIPE_BUF), 0);
+ ASSERT_EQ(close(out), 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 process
+ // 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.
+ nbytes = read(err, buf, PIPE_BUF);
+ ASSERT_GT(nbytes, 0);
+ EXPECT_EQ(std::string(buf, nbytes), "Greetings and Goodbye\n");
+
+ // Check that the write end of the stderr pipe is closed when the child
+ // process finishes.
+ EXPECT_EQ(read(err, buf, PIPE_BUF), 0);
+ ASSERT_EQ(close(err), 0);
+
+ // Check the child process termination status.
+ EXPECT_EQ(minijail_wait(j.get()), 42);
+}
+
TEST(Test, minijail_run_env_pid_pipes) {
// TODO(crbug.com/895875): The preload library interferes with ASan since they
// both need to use LD_PRELOAD.
@@ -536,6 +625,56 @@ TEST(Test, minijail_run_env_pid_pipes) {
EXPECT_EQ(WEXITSTATUS(status), 0);
}
+TEST(Test, minijail_run_fd_env_pid_pipes) {
+ // TODO(crbug.com/895875): The preload library interferes with ASan since they
+ // both need to use LD_PRELOAD.
+ if (running_with_asan())
+ GTEST_SKIP();
+
+ ScopedMinijail j(minijail_new());
+ minijail_set_preload_path(j.get(), kPreloadPath);
+
+ char *argv[4];
+ argv[0] = const_cast<char*>(kShellPath);
+ argv[1] = "-c";
+ argv[2] = "echo \"${TEST_PARENT+set}|${TEST_VAR}\" >&2\n";
+ argv[3] = nullptr;
+
+ char *envp[2];
+ envp[0] = "TEST_VAR=test";
+ envp[1] = nullptr;
+
+ // 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);
+
+ int elf_fd = open(const_cast<char*>(kShellPath), O_RDONLY | O_CLOEXEC);
+ ASSERT_NE(elf_fd, -1);
+
+ int dev_null = open("/dev/null", O_RDONLY);
+ ASSERT_NE(dev_null, -1);
+ // Create a mapping to dev_null that would clobber elf_fd if it is not
+ // relocated.
+ minijail_preserve_fd(j.get(), dev_null, elf_fd);
+
+ pid_t pid;
+ int child_stdin, child_stdout, child_stderr;
+ int mj_run_ret =
+ minijail_run_fd_env_pid_pipes(j.get(), elf_fd, argv, envp, &pid,
+ &child_stdin, &child_stdout, &child_stderr);
+ EXPECT_EQ(mj_run_ret, 0);
+ close(dev_null);
+
+ char buf[kBufferSize] = {};
+ ssize_t read_ret = read(child_stderr, buf, sizeof(buf) - 1);
+ EXPECT_GE(read_ret, 0);
+ EXPECT_STREQ(buf, "|test\n");
+
+ int status;
+ EXPECT_EQ(waitpid(pid, &status, 0), pid);
+ ASSERT_TRUE(WIFEXITED(status));
+ EXPECT_EQ(WEXITSTATUS(status), 0);
+}
+
TEST(Test, minijail_run_env_pid_pipes_with_local_preload) {
// TODO(crbug.com/895875): The preload library interferes with ASan since they
// both need to use LD_PRELOAD.
@@ -601,6 +740,56 @@ TEST(Test, minijail_run_env_pid_pipes_with_local_preload) {
EXPECT_EQ(WEXITSTATUS(status), 0);
}
+TEST(Test, test_minijail_no_clobber_fds) {
+ int dev_null = open("/dev/null", O_RDONLY);
+ ASSERT_NE(dev_null, -1);
+
+ ScopedMinijail j(minijail_new());
+
+ // Keep stderr.
+ minijail_preserve_fd(j.get(), 2, 2);
+ // Create a lot of mappings to dev_null to possibly clobber libminijail.c fds.
+ for (int i = 3; i < 15; ++i) {
+ minijail_preserve_fd(j.get(), dev_null, i);
+ }
+
+ char *argv[4];
+ argv[0] = const_cast<char*>(kShellPath);
+ argv[1] = "-c";
+ argv[2] = "echo Hello; read line1; echo \"${line1}\" >&2";
+ argv[3] = nullptr;
+
+ pid_t pid;
+ int child_stdin;
+ int child_stdout;
+ int child_stderr;
+ int mj_run_ret = minijail_run_pid_pipes_no_preload(
+ j.get(), argv[0], argv, &pid, &child_stdin, &child_stdout, &child_stderr);
+ EXPECT_EQ(mj_run_ret, 0);
+
+ char buf[kBufferSize];
+ ssize_t read_ret = read(child_stdout, buf, sizeof(buf));
+ EXPECT_GE(read_ret, 0);
+ buf[read_ret] = '\0';
+ EXPECT_STREQ(buf, "Hello\n");
+
+ constexpr char to_write[] = "test in and err\n";
+ ssize_t write_ret = write(child_stdin, to_write, sizeof(to_write));
+ EXPECT_EQ(write_ret, sizeof(to_write));
+
+ read_ret = read(child_stderr, buf, sizeof(buf));
+ EXPECT_GE(read_ret, 0);
+ buf[read_ret] = '\0';
+ EXPECT_STREQ(buf, to_write);
+
+ int status;
+ waitpid(pid, &status, 0);
+ ASSERT_TRUE(WIFEXITED(status));
+ EXPECT_EQ(WEXITSTATUS(status), 0);
+
+ close(dev_null);
+}
+
TEST(Test, test_minijail_no_fd_leaks) {
pid_t pid;
int child_stdout;
@@ -1092,7 +1281,7 @@ TEST_F(NamespaceTest, test_remount_all_private) {
argv[0] = const_cast<char*>(kShellPath);
argv[1] = "-c";
argv[2] = "grep -E 'shared:|master:|propagate_from:|unbindable:' "
- "/proc/self/mountinfo";
+ "/proc/self/mountinfo";
argv[3] = NULL;
mj_run_ret = minijail_run_pid_pipes_no_preload(
j, argv[0], argv, &pid, NULL, &child_stdout, NULL);