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:
ScopedCaptureStdout()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
~ScopedCaptureStdout()39 ~ScopedCaptureStdout() {
40 CHECK(dup2(original_stdout_fd_.get(), STDOUT_FILENO) != -1);
41 }
42
GetContents()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
SetUp()65 void SetUp() override {
66 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
67
68 base::FilePath rootfs;
69 ASSERT_TRUE(base::CreateTemporaryDirInDir(
70 temp_dir_.GetPath(), 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
TearDown()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
container()93 struct container* container() {
94 return container_;
95 }
config()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
TEST_F(LibcontainerTargetTest,AddHookRedirectTest)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
TEST_F(LibcontainerTargetTest,AddHookOrderTest)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
TEST_F(LibcontainerTargetTest,AddHookPidArgument)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
main(int argc,char ** argv)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