1 // Copyright 2016 The Chromium OS Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include <fcntl.h> 6 #include <sys/mount.h> 7 #include <sys/types.h> 8 #include <unistd.h> 9 10 #include <base/at_exit.h> 11 #include <base/files/file_util.h> 12 #include <base/files/scoped_file.h> 13 #include <base/files/scoped_temp_dir.h> 14 #include <base/macros.h> 15 #include <base/strings/string_number_conversions.h> 16 #include <base/strings/stringprintf.h> 17 #include <gtest/gtest.h> 18 #include <libcontainer.h> 19 #include <libminijail.h> 20 21 namespace libcontainer { 22 23 namespace { 24 25 // A small RAII class that redirects stdout while it's alive. It also gets the 26 // first 4k of the output. 27 class ScopedCaptureStdout { 28 public: 29 ScopedCaptureStdout() { 30 original_stdout_fd_.reset(dup(STDOUT_FILENO)); 31 CHECK(original_stdout_fd_.is_valid()); 32 int pipe_fds[2]; 33 CHECK(pipe2(pipe_fds, O_NONBLOCK) != -1); 34 read_fd_.reset(pipe_fds[0]); 35 CHECK(dup2(pipe_fds[1], STDOUT_FILENO) != -1); 36 CHECK(close(pipe_fds[1]) != -1); 37 } 38 39 ~ScopedCaptureStdout() { 40 CHECK(dup2(original_stdout_fd_.get(), STDOUT_FILENO) != -1); 41 } 42 43 std::string GetContents() { 44 char buffer[4096]; 45 ssize_t read_bytes = read(read_fd_.get(), buffer, sizeof(buffer) - 1); 46 CHECK(read_bytes >= 0); 47 buffer[read_bytes] = '\0'; 48 return std::string(buffer, read_bytes); 49 } 50 51 private: 52 base::ScopedFD read_fd_; 53 base::ScopedFD original_stdout_fd_; 54 55 DISALLOW_COPY_AND_ASSIGN(ScopedCaptureStdout); 56 }; 57 58 } // namespace 59 60 class LibcontainerTargetTest : public ::testing::Test { 61 public: 62 LibcontainerTargetTest() = default; 63 ~LibcontainerTargetTest() override = default; 64 65 void SetUp() override { 66 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); 67 68 base::FilePath rootfs; 69 ASSERT_TRUE(base::CreateTemporaryDirInDir( 70 temp_dir_.path(), FILE_PATH_LITERAL("rootfs"), &rootfs)); 71 72 config_ = container_config_create(); 73 ASSERT_NE(nullptr, config_); 74 75 ASSERT_EQ(0, container_config_uid_map(config_, "0 0 429496729")); 76 ASSERT_EQ(0, container_config_gid_map(config_, "0 0 429496729")); 77 ASSERT_EQ(0, container_config_rootfs(config_, "/")); 78 ASSERT_EQ(0, container_config_set_cgroup_parent( 79 config_, "chronos_containers", 1000, 1000)); 80 81 container_ = container_new("containerUT", rootfs.value().c_str()); 82 ASSERT_NE(nullptr, container_); 83 } 84 85 void TearDown() override { 86 container_destroy(container_); 87 container_ = nullptr; 88 container_config_destroy(config_); 89 config_ = nullptr; 90 ASSERT_TRUE(temp_dir_.Delete()); 91 } 92 93 struct container* container() { 94 return container_; 95 } 96 struct container_config* config() { 97 return config_; 98 } 99 100 private: 101 base::ScopedTempDir temp_dir_; 102 struct container* container_ = nullptr; 103 struct container_config* config_ = nullptr; 104 105 DISALLOW_COPY_AND_ASSIGN(LibcontainerTargetTest); 106 }; 107 108 TEST_F(LibcontainerTargetTest, AddHookRedirectTest) { 109 // Preserve stdout/stderr to get the output from the container. 110 int stdio_fds[] = {STDOUT_FILENO, STDERR_FILENO}; 111 ASSERT_EQ(0, container_config_inherit_fds(config(), stdio_fds, 112 arraysize(stdio_fds))); 113 114 static const char* kPreChrootArgv[] = { 115 "/bin/cat", 116 }; 117 int stdin_fd; 118 ASSERT_EQ(0, container_config_add_hook( 119 config(), MINIJAIL_HOOK_EVENT_PRE_CHROOT, kPreChrootArgv[0], 120 kPreChrootArgv, arraysize(kPreChrootArgv), &stdin_fd, 121 nullptr, nullptr)); 122 EXPECT_EQ(1, write(stdin_fd, "1", 1)); 123 close(stdin_fd); 124 125 static const char* kProgramArgv[] = { 126 "/bin/echo", 127 "-n", 128 "2", 129 }; 130 ASSERT_EQ(0, container_config_program_argv(config(), kProgramArgv, 131 arraysize(kProgramArgv))); 132 133 std::string output; 134 { 135 ScopedCaptureStdout capture_stdout; 136 EXPECT_EQ(0, container_start(container(), config())); 137 EXPECT_EQ(0, container_wait(container())); 138 output = capture_stdout.GetContents(); 139 } 140 EXPECT_EQ("12", output); 141 } 142 143 TEST_F(LibcontainerTargetTest, AddHookOrderTest) { 144 // Preserve stdout/stderr to get the output from the container. 145 int stdio_fds[] = {STDOUT_FILENO, STDERR_FILENO}; 146 ASSERT_EQ(0, container_config_inherit_fds(config(), stdio_fds, 147 arraysize(stdio_fds))); 148 149 static const char* kProgramArgv[] = { 150 "/bin/echo", 151 "-n", 152 "3", 153 }; 154 ASSERT_EQ(0, container_config_program_argv(config(), kProgramArgv, 155 arraysize(kProgramArgv))); 156 157 // Hooks are run in the following order: pre-chroot, pre-dropcaps, pre-execve 158 static const char* kPreExecveArgv[] = { 159 "/bin/echo", 160 "-n", 161 "2", 162 }; 163 ASSERT_EQ(0, container_config_add_hook( 164 config(), MINIJAIL_HOOK_EVENT_PRE_EXECVE, kPreExecveArgv[0], 165 kPreExecveArgv, arraysize(kPreExecveArgv), nullptr, nullptr, 166 nullptr)); 167 168 static const char* kPreChrootArgv[] = { 169 "/bin/echo", 170 "-n", 171 "1", 172 }; 173 ASSERT_EQ(0, container_config_add_hook( 174 config(), MINIJAIL_HOOK_EVENT_PRE_CHROOT, kPreChrootArgv[0], 175 kPreChrootArgv, arraysize(kPreChrootArgv), nullptr, nullptr, 176 nullptr)); 177 178 std::string output; 179 { 180 ScopedCaptureStdout capture_stdout; 181 EXPECT_EQ(0, container_start(container(), config())); 182 EXPECT_EQ(0, container_wait(container())); 183 output = capture_stdout.GetContents(); 184 } 185 EXPECT_EQ("123", output); 186 } 187 188 TEST_F(LibcontainerTargetTest, AddHookPidArgument) { 189 // Preserve stdout/stderr to get the output from the container. 190 int stdio_fds[] = {STDOUT_FILENO, STDERR_FILENO}; 191 ASSERT_EQ(0, container_config_inherit_fds(config(), stdio_fds, 192 arraysize(stdio_fds))); 193 194 static const char* kProgramArgv[] = { 195 "/bin/true", 196 }; 197 ASSERT_EQ(0, container_config_program_argv(config(), kProgramArgv, 198 arraysize(kProgramArgv))); 199 200 static const char* kPreExecveArgv[] = { 201 "/bin/echo", 202 "-n", 203 "$PID", 204 }; 205 ASSERT_EQ(0, container_config_add_hook( 206 config(), MINIJAIL_HOOK_EVENT_PRE_EXECVE, kPreExecveArgv[0], 207 kPreExecveArgv, arraysize(kPreExecveArgv), nullptr, nullptr, 208 nullptr)); 209 210 std::string output; 211 int pid; 212 { 213 ScopedCaptureStdout capture_stdout; 214 EXPECT_EQ(0, container_start(container(), config())); 215 pid = container_pid(container()); 216 EXPECT_EQ(0, container_wait(container())); 217 output = capture_stdout.GetContents(); 218 } 219 EXPECT_EQ(base::IntToString(pid), output); 220 } 221 222 } // namespace libcontainer 223 224 // Avoid including syslog.h, since it collides with some of the logging 225 // constants in libchrome. 226 #define SYSLOG_LOG_INFO 6 227 228 int main(int argc, char** argv) { 229 base::AtExitManager exit_manager; 230 testing::InitGoogleTest(&argc, argv); 231 testing::GTEST_FLAG(throw_on_failure) = true; 232 minijail_log_to_fd(STDERR_FILENO, SYSLOG_LOG_INFO); 233 return RUN_ALL_TESTS(); 234 } 235