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