blob: e35261d1592bdbbc2b0c6170dce2387aa32f52d0 [file] [log] [blame]
// Copyright 2022 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "init/process_killer/process_manager.h"
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include <base/files/file_util.h>
#include <base/files/scoped_temp_dir.h>
#include <base/strings/string_number_conversions.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <re2/re2.h>
#include <init/process_killer/process.h>
namespace init {
namespace {
std::optional<ActiveProcess> GetProcessFromPid(
const std::vector<ActiveProcess>& list, pid_t pid) {
for (const ActiveProcess& p : list) {
if (p.GetPid() == pid)
return p;
}
return std::nullopt;
}
} // namespace
class ProcessManagerTest : public ::testing::Test {
public:
void SetUpProcess(pid_t pid,
const std::string& mnt_ns,
const std::string& comm,
const std::string& mountinfo_contents,
const std::vector<OpenFileDescriptor>& fds,
const std::vector<OpenFileDescriptor>& maps) {
base::FilePath pid_dir =
tmp_dir_.GetPath().Append(base::NumberToString(pid));
ASSERT_TRUE(base::CreateDirectory(pid_dir));
// Set up mountinfo.
base::FilePath mountinfo = pid_dir.Append("mountinfo");
ASSERT_TRUE(base::WriteFile(mountinfo, mountinfo_contents));
// Set up comm for the process.
base::FilePath comm_path = pid_dir.Append("comm");
ASSERT_TRUE(base::WriteFile(comm_path, comm));
// Set up file descriptors.
base::FilePath fd_dir = pid_dir.Append("fd");
ASSERT_TRUE(base::CreateDirectory(fd_dir));
// Set up file descriptors. Add a few fake descriptors at the beginning to
// test handling of stdin/stdout/pipes.
ASSERT_TRUE(base::WriteFile(fd_dir.Append("0"), "foo"));
ASSERT_TRUE(base::WriteFile(fd_dir.Append("1"), "bar"));
ASSERT_TRUE(base::WriteFile(fd_dir.Append("2"), "baz"));
// Set up map_files.
base::FilePath map_files = pid_dir.Append("map_files");
ASSERT_TRUE(base::WriteFile(map_files, "foo"));
int current_fd = 3;
// Target dir contains the targets for the symbolic links.
base::FilePath target_dir = tmp_dir_.GetPath().Append("targets");
ASSERT_TRUE(base::CreateDirectory(target_dir));
for (auto& fd : fds) {
base::FilePath symlink =
fd_dir.Append(base::NumberToString(current_fd++));
base::FilePath target = target_dir.Append(fd.path.value());
ASSERT_TRUE(base::WriteFile(target, "foo"));
ASSERT_TRUE(base::CreateSymbolicLink(target, symlink));
}
for (auto& map : maps) {
base::FilePath symlink = fd_dir.Append(map.path.value());
base::FilePath target = target_dir.Append(map.path.value());
ASSERT_TRUE(base::WriteFile(target, "foo"));
ASSERT_TRUE(base::CreateSymbolicLink(target, symlink));
}
// Set up mount namespace symlink. Re-use the fd directory for storing
// the target.
base::FilePath ns_dir = pid_dir.Append("ns");
ASSERT_TRUE(base::CreateDirectory(ns_dir));
base::FilePath mnt_ns_path = target_dir.Append(mnt_ns);
ASSERT_TRUE(base::WriteFile(mnt_ns_path, "foo"));
ASSERT_TRUE(base::CreateSymbolicLink(mnt_ns_path, ns_dir.Append("mnt")));
}
void SetUp() override {
ASSERT_TRUE(tmp_dir_.CreateUniqueTempDir());
pm_ = std::make_unique<ProcessManager>(tmp_dir_.GetPath());
// Set up init process.
std::string mountinfo =
"21 12 8:1 /var /var rw,noexec - ext3 /dev/sda1 rw\n";
OpenFileDescriptor fd = {base::FilePath("foo")};
OpenFileDescriptor maps = {base::FilePath("abcd")};
SetUpProcess(1, "init_mnt_ns", "/sbin/init", mountinfo, {fd}, {maps});
}
protected:
base::ScopedTempDir tmp_dir_;
std::unique_ptr<ProcessManager> pm_;
};
TEST_F(ProcessManagerTest, InvalidProcessTest) {
ASSERT_TRUE(base::WriteFile(tmp_dir_.GetPath().Append("proc").Append("123"),
"foo", 3));
EXPECT_EQ(pm_->GetProcessList(true, true).size(), 1);
}
TEST_F(ProcessManagerTest, ValidProcessTest) {
std::string mountinfo = "21 12 8:1 /var /var rw,noexec - ext3 /dev/sda1 rw\n";
OpenFileDescriptor fd = {base::FilePath("foo")};
OpenFileDescriptor maps = {base::FilePath("abcd")};
SetUpProcess(2, "init_mnt_ns", "foo", mountinfo, {fd}, {maps});
std::vector<ActiveProcess> list = pm_->GetProcessList(true, true);
EXPECT_EQ(list.size(), 2);
auto p = GetProcessFromPid(list, 2);
EXPECT_TRUE(p);
EXPECT_TRUE(p->HasFileOpenOnMount(re2::RE2("foo")));
EXPECT_TRUE(p->HasFileOpenOnMount(re2::RE2("abcd")));
EXPECT_TRUE(p->HasMountOpenFromDevice(re2::RE2("/dev/sda1")));
EXPECT_TRUE(p->InInitMountNamespace());
}
TEST_F(ProcessManagerTest, ValidNamespacedProcessTest) {
std::string mountinfo = "21 12 8:1 /var /var rw,noexec - ext3 /dev/sda1 rw\n";
OpenFileDescriptor fd = {base::FilePath("foo")};
OpenFileDescriptor maps = {base::FilePath("abcd")};
SetUpProcess(2, "separate_mnt_ns", "foo", mountinfo, {fd}, {maps});
std::vector<ActiveProcess> list = pm_->GetProcessList(true, true);
EXPECT_EQ(list.size(), 2);
auto p = GetProcessFromPid(list, 2);
EXPECT_TRUE(p);
EXPECT_TRUE(p->HasFileOpenOnMount(re2::RE2("foo")));
EXPECT_TRUE(p->HasFileOpenOnMount(re2::RE2("abcd")));
EXPECT_TRUE(p->HasMountOpenFromDevice(re2::RE2("/dev/sda1")));
EXPECT_FALSE(p->InInitMountNamespace());
}
} // namespace init