blob: 6fb2dfaceaf73a5b00ba038f309513e679a07d98 [file] [log] [blame]
// Copyright 2016 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <fcntl.h>
#include <sys/mount.h>
#include <sys/types.h>
#include <unistd.h>
#include <base/at_exit.h>
#include <base/files/file_util.h>
#include <base/files/scoped_file.h>
#include <base/files/scoped_temp_dir.h>
#include <base/macros.h>
#include <base/strings/string_number_conversions.h>
#include <base/strings/stringprintf.h>
#include <gtest/gtest.h>
#include <libcontainer.h>
#include <libminijail.h>
namespace libcontainer {
namespace {
// A small RAII class that redirects stdout while it's alive. It also gets the
// first 4k of the output.
class ScopedCaptureStdout {
public:
ScopedCaptureStdout() {
original_stdout_fd_.reset(dup(STDOUT_FILENO));
CHECK(original_stdout_fd_.is_valid());
int pipe_fds[2];
CHECK(pipe2(pipe_fds, O_NONBLOCK) != -1);
read_fd_.reset(pipe_fds[0]);
CHECK(dup2(pipe_fds[1], STDOUT_FILENO) != -1);
CHECK(close(pipe_fds[1]) != -1);
}
~ScopedCaptureStdout() {
CHECK(dup2(original_stdout_fd_.get(), STDOUT_FILENO) != -1);
}
std::string GetContents() {
char buffer[4096];
ssize_t read_bytes = read(read_fd_.get(), buffer, sizeof(buffer) - 1);
CHECK(read_bytes >= 0);
buffer[read_bytes] = '\0';
return std::string(buffer, read_bytes);
}
private:
base::ScopedFD read_fd_;
base::ScopedFD original_stdout_fd_;
DISALLOW_COPY_AND_ASSIGN(ScopedCaptureStdout);
};
} // namespace
class LibcontainerTargetTest : public ::testing::Test {
public:
LibcontainerTargetTest() = default;
~LibcontainerTargetTest() override = default;
void SetUp() override {
ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
base::FilePath rootfs;
ASSERT_TRUE(base::CreateTemporaryDirInDir(
temp_dir_.GetPath(), FILE_PATH_LITERAL("rootfs"), &rootfs));
config_ = container_config_create();
ASSERT_NE(nullptr, config_);
ASSERT_EQ(0, container_config_uid_map(config_, "0 0 429496729"));
ASSERT_EQ(0, container_config_gid_map(config_, "0 0 429496729"));
ASSERT_EQ(0, container_config_rootfs(config_, "/"));
ASSERT_EQ(0, container_config_set_cgroup_parent(
config_, "chronos_containers", 1000, 1000));
container_ = container_new("containerUT", rootfs.value().c_str());
ASSERT_NE(nullptr, container_);
}
void TearDown() override {
container_destroy(container_);
container_ = nullptr;
container_config_destroy(config_);
config_ = nullptr;
ASSERT_TRUE(temp_dir_.Delete());
}
struct container* container() {
return container_;
}
struct container_config* config() {
return config_;
}
private:
base::ScopedTempDir temp_dir_;
struct container* container_ = nullptr;
struct container_config* config_ = nullptr;
DISALLOW_COPY_AND_ASSIGN(LibcontainerTargetTest);
};
TEST_F(LibcontainerTargetTest, AddHookRedirectTest) {
// Preserve stdout/stderr to get the output from the container.
int stdio_fds[] = {STDOUT_FILENO, STDERR_FILENO};
ASSERT_EQ(0, container_config_inherit_fds(config(), stdio_fds,
arraysize(stdio_fds)));
static const char* kPreChrootArgv[] = {
"/bin/cat",
};
int stdin_fd;
ASSERT_EQ(0, container_config_add_hook(
config(), MINIJAIL_HOOK_EVENT_PRE_CHROOT, kPreChrootArgv[0],
kPreChrootArgv, arraysize(kPreChrootArgv), &stdin_fd,
nullptr, nullptr));
EXPECT_EQ(1, write(stdin_fd, "1", 1));
close(stdin_fd);
static const char* kProgramArgv[] = {
"/bin/echo",
"-n",
"2",
};
ASSERT_EQ(0, container_config_program_argv(config(), kProgramArgv,
arraysize(kProgramArgv)));
std::string output;
{
ScopedCaptureStdout capture_stdout;
EXPECT_EQ(0, container_start(container(), config()));
EXPECT_EQ(0, container_wait(container()));
output = capture_stdout.GetContents();
}
EXPECT_EQ("12", output);
}
TEST_F(LibcontainerTargetTest, AddHookOrderTest) {
// Preserve stdout/stderr to get the output from the container.
int stdio_fds[] = {STDOUT_FILENO, STDERR_FILENO};
ASSERT_EQ(0, container_config_inherit_fds(config(), stdio_fds,
arraysize(stdio_fds)));
static const char* kProgramArgv[] = {
"/bin/echo",
"-n",
"3",
};
ASSERT_EQ(0, container_config_program_argv(config(), kProgramArgv,
arraysize(kProgramArgv)));
// Hooks are run in the following order: pre-chroot, pre-dropcaps, pre-execve
static const char* kPreExecveArgv[] = {
"/bin/echo",
"-n",
"2",
};
ASSERT_EQ(0, container_config_add_hook(
config(), MINIJAIL_HOOK_EVENT_PRE_EXECVE, kPreExecveArgv[0],
kPreExecveArgv, arraysize(kPreExecveArgv), nullptr, nullptr,
nullptr));
static const char* kPreChrootArgv[] = {
"/bin/echo",
"-n",
"1",
};
ASSERT_EQ(0, container_config_add_hook(
config(), MINIJAIL_HOOK_EVENT_PRE_CHROOT, kPreChrootArgv[0],
kPreChrootArgv, arraysize(kPreChrootArgv), nullptr, nullptr,
nullptr));
std::string output;
{
ScopedCaptureStdout capture_stdout;
EXPECT_EQ(0, container_start(container(), config()));
EXPECT_EQ(0, container_wait(container()));
output = capture_stdout.GetContents();
}
EXPECT_EQ("123", output);
}
TEST_F(LibcontainerTargetTest, AddHookPidArgument) {
// Preserve stdout/stderr to get the output from the container.
int stdio_fds[] = {STDOUT_FILENO, STDERR_FILENO};
ASSERT_EQ(0, container_config_inherit_fds(config(), stdio_fds,
arraysize(stdio_fds)));
static const char* kProgramArgv[] = {
"/bin/true",
};
ASSERT_EQ(0, container_config_program_argv(config(), kProgramArgv,
arraysize(kProgramArgv)));
static const char* kPreExecveArgv[] = {
"/bin/echo",
"-n",
"$PID",
};
ASSERT_EQ(0, container_config_add_hook(
config(), MINIJAIL_HOOK_EVENT_PRE_EXECVE, kPreExecveArgv[0],
kPreExecveArgv, arraysize(kPreExecveArgv), nullptr, nullptr,
nullptr));
std::string output;
int pid;
{
ScopedCaptureStdout capture_stdout;
EXPECT_EQ(0, container_start(container(), config()));
pid = container_pid(container());
EXPECT_EQ(0, container_wait(container()));
output = capture_stdout.GetContents();
}
EXPECT_EQ(base::IntToString(pid), output);
}
} // namespace libcontainer
// Avoid including syslog.h, since it collides with some of the logging
// constants in libchrome.
#define SYSLOG_LOG_INFO 6
int main(int argc, char** argv) {
base::AtExitManager exit_manager;
testing::InitGoogleTest(&argc, argv);
testing::GTEST_FLAG(throw_on_failure) = true;
minijail_log_to_fd(STDERR_FILENO, SYSLOG_LOG_INFO);
return RUN_ALL_TESTS();
}