Home | History | Annotate | Download | only in src
      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