• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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