diff options
Diffstat (limited to 'sandbox/linux/services/namespace_sandbox_unittest.cc')
-rw-r--r-- | sandbox/linux/services/namespace_sandbox_unittest.cc | 217 |
1 files changed, 217 insertions, 0 deletions
diff --git a/sandbox/linux/services/namespace_sandbox_unittest.cc b/sandbox/linux/services/namespace_sandbox_unittest.cc new file mode 100644 index 0000000000..547ef6728c --- /dev/null +++ b/sandbox/linux/services/namespace_sandbox_unittest.cc @@ -0,0 +1,217 @@ +// Copyright 2015 The Chromium 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 "sandbox/linux/services/namespace_sandbox.h" + +#include <signal.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <unistd.h> + +#include <string> +#include <utility> + +#include "base/command_line.h" +#include "base/files/file_enumerator.h" +#include "base/files/file_path.h" +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "base/process/launch.h" +#include "base/process/process.h" +#include "base/test/multiprocess_test.h" +#include "sandbox/linux/services/credentials.h" +#include "sandbox/linux/services/namespace_utils.h" +#include "sandbox/linux/services/proc_util.h" +#include "sandbox/linux/tests/unit_tests.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "testing/multiprocess_func_list.h" + +namespace sandbox { + +namespace { + +bool RootDirectoryIsEmpty() { + base::FilePath root("/"); + int file_type = + base::FileEnumerator::DIRECTORIES | base::FileEnumerator::FILES; + base::FileEnumerator enumerator_before(root, false, file_type); + return enumerator_before.Next().empty(); +} + +class NamespaceSandboxTest : public base::MultiProcessTest { + public: + void TestProc(const std::string& procname) { + if (!Credentials::CanCreateProcessInNewUserNS()) { + return; + } + + base::FileHandleMappingVector fds_to_remap = { + std::make_pair(STDOUT_FILENO, STDOUT_FILENO), + std::make_pair(STDERR_FILENO, STDERR_FILENO), + }; + base::LaunchOptions launch_options; + launch_options.fds_to_remap = &fds_to_remap; + + base::Process process = + NamespaceSandbox::LaunchProcess(MakeCmdLine(procname), launch_options); + ASSERT_TRUE(process.IsValid()); + + const int kDummyExitCode = 42; + int exit_code = kDummyExitCode; + EXPECT_TRUE(process.WaitForExit(&exit_code)); + EXPECT_EQ(0, exit_code); + } +}; + +MULTIPROCESS_TEST_MAIN(SimpleChildProcess) { + scoped_ptr<base::Environment> env(base::Environment::Create()); + bool in_user_ns = NamespaceSandbox::InNewUserNamespace(); + bool in_pid_ns = NamespaceSandbox::InNewPidNamespace(); + bool in_net_ns = NamespaceSandbox::InNewNetNamespace(); + CHECK(in_user_ns); + CHECK_EQ(in_pid_ns, + NamespaceUtils::KernelSupportsUnprivilegedNamespace(CLONE_NEWPID)); + CHECK_EQ(in_net_ns, + NamespaceUtils::KernelSupportsUnprivilegedNamespace(CLONE_NEWNET)); + if (in_pid_ns) { + CHECK_EQ(1, getpid()); + } + return 0; +} + +TEST_F(NamespaceSandboxTest, BasicUsage) { + TestProc("SimpleChildProcess"); +} + +MULTIPROCESS_TEST_MAIN(ChrootMe) { + CHECK(!RootDirectoryIsEmpty()); + CHECK(sandbox::Credentials::MoveToNewUserNS()); + CHECK(sandbox::Credentials::DropFileSystemAccess(ProcUtil::OpenProc().get())); + CHECK(RootDirectoryIsEmpty()); + return 0; +} + +// Temporarily disabled on ASAN due to crbug.com/451603. +TEST_F(NamespaceSandboxTest, DISABLE_ON_ASAN(ChrootAndDropCapabilities)) { + TestProc("ChrootMe"); +} + +MULTIPROCESS_TEST_MAIN(NestedNamespaceSandbox) { + base::FileHandleMappingVector fds_to_remap = { + std::make_pair(STDOUT_FILENO, STDOUT_FILENO), + std::make_pair(STDERR_FILENO, STDERR_FILENO), + }; + base::LaunchOptions launch_options; + launch_options.fds_to_remap = &fds_to_remap; + base::Process process = NamespaceSandbox::LaunchProcess( + base::CommandLine(base::FilePath("/bin/true")), launch_options); + CHECK(process.IsValid()); + + const int kDummyExitCode = 42; + int exit_code = kDummyExitCode; + CHECK(process.WaitForExit(&exit_code)); + CHECK_EQ(0, exit_code); + return 0; +} + +TEST_F(NamespaceSandboxTest, NestedNamespaceSandbox) { + TestProc("NestedNamespaceSandbox"); +} + +const int kNormalExitCode = 0; +const int kSignalTerminationExitCode = 255; + +// Ensure that CHECK(false) is distinguishable from _exit(kNormalExitCode). +// Allowing noise since CHECK(false) will write a stack trace to stderr. +SANDBOX_TEST_ALLOW_NOISE(ForkInNewPidNamespace, CheckDoesNotReturnZero) { + if (!Credentials::CanCreateProcessInNewUserNS()) { + return; + } + + CHECK(sandbox::Credentials::MoveToNewUserNS()); + const pid_t pid = NamespaceSandbox::ForkInNewPidNamespace( + /*drop_capabilities_in_child=*/true); + CHECK_GE(pid, 0); + + if (pid == 0) { + CHECK(false); + _exit(kNormalExitCode); + } + + int status; + PCHECK(waitpid(pid, &status, 0) == pid); + if (WIFEXITED(status)) { + CHECK_NE(kNormalExitCode, WEXITSTATUS(status)); + } +} + +SANDBOX_TEST(ForkInNewPidNamespace, BasicUsage) { + if (!Credentials::CanCreateProcessInNewUserNS()) { + return; + } + + CHECK(sandbox::Credentials::MoveToNewUserNS()); + const pid_t pid = NamespaceSandbox::ForkInNewPidNamespace( + /*drop_capabilities_in_child=*/true); + CHECK_GE(pid, 0); + + if (pid == 0) { + CHECK_EQ(1, getpid()); + CHECK(!Credentials::HasAnyCapability()); + _exit(kNormalExitCode); + } + + int status; + PCHECK(waitpid(pid, &status, 0) == pid); + CHECK(WIFEXITED(status)); + CHECK_EQ(kNormalExitCode, WEXITSTATUS(status)); +} + +SANDBOX_TEST(ForkInNewPidNamespace, ExitWithSignal) { + if (!Credentials::CanCreateProcessInNewUserNS()) { + return; + } + + CHECK(sandbox::Credentials::MoveToNewUserNS()); + const pid_t pid = NamespaceSandbox::ForkInNewPidNamespace( + /*drop_capabilities_in_child=*/true); + CHECK_GE(pid, 0); + + if (pid == 0) { + CHECK_EQ(1, getpid()); + CHECK(!Credentials::HasAnyCapability()); + CHECK(NamespaceSandbox::InstallTerminationSignalHandler( + SIGTERM, kSignalTerminationExitCode)); + while (true) { + raise(SIGTERM); + } + } + + int status; + PCHECK(waitpid(pid, &status, 0) == pid); + CHECK(WIFEXITED(status)); + CHECK_EQ(kSignalTerminationExitCode, WEXITSTATUS(status)); +} + +volatile sig_atomic_t signal_handler_called; +void ExitSuccessfully(int sig) { + signal_handler_called = 1; +} + +SANDBOX_TEST(InstallTerminationSignalHandler, DoesNotOverrideExistingHandlers) { + struct sigaction action = {}; + action.sa_handler = &ExitSuccessfully; + PCHECK(sigaction(SIGUSR1, &action, nullptr) == 0); + + NamespaceSandbox::InstallDefaultTerminationSignalHandlers(); + CHECK(!NamespaceSandbox::InstallTerminationSignalHandler( + SIGUSR1, kSignalTerminationExitCode)); + + raise(SIGUSR1); + CHECK_EQ(1, signal_handler_called); +} + +} // namespace + +} // namespace sandbox |