Roll src/third_party/WebKit d9c6159:8139f33 (svn 201974:201975)
[chromium-blink-merge.git] / sandbox / linux / services / namespace_sandbox_unittest.cc
blob43e0ae5923542a57c0408752fe1234a41f084e0c
1 // Copyright 2015 The Chromium 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.
5 #include "sandbox/linux/services/namespace_sandbox.h"
7 #include <signal.h>
8 #include <sys/types.h>
9 #include <sys/wait.h>
10 #include <unistd.h>
12 #include <string>
13 #include <utility>
15 #include "base/command_line.h"
16 #include "base/files/file_enumerator.h"
17 #include "base/files/file_path.h"
18 #include "base/logging.h"
19 #include "base/memory/scoped_ptr.h"
20 #include "base/process/launch.h"
21 #include "base/process/process.h"
22 #include "base/test/multiprocess_test.h"
23 #include "sandbox/linux/services/credentials.h"
24 #include "sandbox/linux/services/namespace_utils.h"
25 #include "sandbox/linux/services/proc_util.h"
26 #include "sandbox/linux/tests/unit_tests.h"
27 #include "testing/gtest/include/gtest/gtest.h"
28 #include "testing/multiprocess_func_list.h"
30 namespace sandbox {
32 namespace {
34 bool RootDirectoryIsEmpty() {
35 base::FilePath root("/");
36 int file_type =
37 base::FileEnumerator::DIRECTORIES | base::FileEnumerator::FILES;
38 base::FileEnumerator enumerator_before(root, false, file_type);
39 return enumerator_before.Next().empty();
42 class NamespaceSandboxTest : public base::MultiProcessTest {
43 public:
44 void TestProc(const std::string& procname) {
45 TestProcWithOptions(procname, NamespaceSandbox::Options());
48 void TestProcWithOptions(
49 const std::string& procname,
50 const NamespaceSandbox::Options& ns_sandbox_options) {
51 if (!Credentials::CanCreateProcessInNewUserNS()) {
52 return;
55 base::FileHandleMappingVector fds_to_remap = {
56 std::make_pair(STDOUT_FILENO, STDOUT_FILENO),
57 std::make_pair(STDERR_FILENO, STDERR_FILENO),
59 base::LaunchOptions launch_options;
60 launch_options.fds_to_remap = &fds_to_remap;
62 base::Process process = NamespaceSandbox::LaunchProcessWithOptions(
63 MakeCmdLine(procname), launch_options, ns_sandbox_options);
64 ASSERT_TRUE(process.IsValid());
66 const int kDummyExitCode = 42;
67 int exit_code = kDummyExitCode;
68 EXPECT_TRUE(process.WaitForExit(&exit_code));
69 EXPECT_EQ(0, exit_code);
73 MULTIPROCESS_TEST_MAIN(SimpleChildProcess) {
74 const bool in_user_ns = NamespaceSandbox::InNewUserNamespace();
75 const bool in_pid_ns = NamespaceSandbox::InNewPidNamespace();
76 const bool in_net_ns = NamespaceSandbox::InNewNetNamespace();
77 CHECK(in_user_ns);
78 CHECK_EQ(in_pid_ns,
79 NamespaceUtils::KernelSupportsUnprivilegedNamespace(CLONE_NEWPID));
80 CHECK_EQ(in_net_ns,
81 NamespaceUtils::KernelSupportsUnprivilegedNamespace(CLONE_NEWNET));
82 if (in_pid_ns) {
83 CHECK_EQ(1, getpid());
85 return 0;
88 TEST_F(NamespaceSandboxTest, BasicUsage) {
89 TestProc("SimpleChildProcess");
92 MULTIPROCESS_TEST_MAIN(PidNsOnlyChildProcess) {
93 const bool in_user_ns = NamespaceSandbox::InNewUserNamespace();
94 const bool in_pid_ns = NamespaceSandbox::InNewPidNamespace();
95 const bool in_net_ns = NamespaceSandbox::InNewNetNamespace();
96 CHECK(in_user_ns);
97 CHECK_EQ(in_pid_ns,
98 NamespaceUtils::KernelSupportsUnprivilegedNamespace(CLONE_NEWPID));
99 CHECK(!in_net_ns);
100 if (in_pid_ns) {
101 CHECK_EQ(1, getpid());
103 return 0;
107 TEST_F(NamespaceSandboxTest, BasicUsageWithOptions) {
108 NamespaceSandbox::Options options;
109 options.ns_types = CLONE_NEWUSER | CLONE_NEWPID;
110 TestProcWithOptions("PidNsOnlyChildProcess", options);
113 MULTIPROCESS_TEST_MAIN(ChrootMe) {
114 CHECK(!RootDirectoryIsEmpty());
115 CHECK(sandbox::Credentials::MoveToNewUserNS());
116 CHECK(sandbox::Credentials::DropFileSystemAccess(ProcUtil::OpenProc().get()));
117 CHECK(RootDirectoryIsEmpty());
118 return 0;
121 // Temporarily disabled on ASAN due to crbug.com/451603.
122 TEST_F(NamespaceSandboxTest, DISABLE_ON_ASAN(ChrootAndDropCapabilities)) {
123 TestProc("ChrootMe");
126 MULTIPROCESS_TEST_MAIN(NestedNamespaceSandbox) {
127 base::FileHandleMappingVector fds_to_remap = {
128 std::make_pair(STDOUT_FILENO, STDOUT_FILENO),
129 std::make_pair(STDERR_FILENO, STDERR_FILENO),
131 base::LaunchOptions launch_options;
132 launch_options.fds_to_remap = &fds_to_remap;
133 base::Process process = NamespaceSandbox::LaunchProcess(
134 base::CommandLine(base::FilePath("/bin/true")), launch_options);
135 CHECK(process.IsValid());
137 const int kDummyExitCode = 42;
138 int exit_code = kDummyExitCode;
139 CHECK(process.WaitForExit(&exit_code));
140 CHECK_EQ(0, exit_code);
141 return 0;
144 TEST_F(NamespaceSandboxTest, NestedNamespaceSandbox) {
145 TestProc("NestedNamespaceSandbox");
148 const int kNormalExitCode = 0;
150 // Ensure that CHECK(false) is distinguishable from _exit(kNormalExitCode).
151 // Allowing noise since CHECK(false) will write a stack trace to stderr.
152 SANDBOX_TEST_ALLOW_NOISE(ForkInNewPidNamespace, CheckDoesNotReturnZero) {
153 if (!Credentials::CanCreateProcessInNewUserNS()) {
154 return;
157 CHECK(sandbox::Credentials::MoveToNewUserNS());
158 const pid_t pid = NamespaceSandbox::ForkInNewPidNamespace(
159 /*drop_capabilities_in_child=*/true);
160 CHECK_GE(pid, 0);
162 if (pid == 0) {
163 CHECK(false);
164 _exit(kNormalExitCode);
167 int status;
168 PCHECK(waitpid(pid, &status, 0) == pid);
169 if (WIFEXITED(status)) {
170 CHECK_NE(kNormalExitCode, WEXITSTATUS(status));
174 SANDBOX_TEST(ForkInNewPidNamespace, BasicUsage) {
175 if (!Credentials::CanCreateProcessInNewUserNS()) {
176 return;
179 CHECK(sandbox::Credentials::MoveToNewUserNS());
180 const pid_t pid = NamespaceSandbox::ForkInNewPidNamespace(
181 /*drop_capabilities_in_child=*/true);
182 CHECK_GE(pid, 0);
184 if (pid == 0) {
185 CHECK_EQ(1, getpid());
186 CHECK(!Credentials::HasAnyCapability());
187 _exit(kNormalExitCode);
190 int status;
191 PCHECK(waitpid(pid, &status, 0) == pid);
192 CHECK(WIFEXITED(status));
193 CHECK_EQ(kNormalExitCode, WEXITSTATUS(status));
196 SANDBOX_TEST(ForkInNewPidNamespace, ExitWithSignal) {
197 if (!Credentials::CanCreateProcessInNewUserNS()) {
198 return;
201 CHECK(sandbox::Credentials::MoveToNewUserNS());
202 const pid_t pid = NamespaceSandbox::ForkInNewPidNamespace(
203 /*drop_capabilities_in_child=*/true);
204 CHECK_GE(pid, 0);
206 if (pid == 0) {
207 CHECK_EQ(1, getpid());
208 CHECK(!Credentials::HasAnyCapability());
209 CHECK(NamespaceSandbox::InstallTerminationSignalHandler(
210 SIGTERM, NamespaceSandbox::SignalExitCode(SIGTERM)));
211 while (true) {
212 raise(SIGTERM);
216 int status;
217 PCHECK(waitpid(pid, &status, 0) == pid);
218 CHECK(WIFEXITED(status));
219 CHECK_EQ(NamespaceSandbox::SignalExitCode(SIGTERM), WEXITSTATUS(status));
222 volatile sig_atomic_t signal_handler_called;
223 void ExitSuccessfully(int sig) {
224 signal_handler_called = 1;
227 SANDBOX_TEST(InstallTerminationSignalHandler, DoesNotOverrideExistingHandlers) {
228 struct sigaction action = {};
229 action.sa_handler = &ExitSuccessfully;
230 PCHECK(sigaction(SIGUSR1, &action, nullptr) == 0);
232 NamespaceSandbox::InstallDefaultTerminationSignalHandlers();
233 CHECK(!NamespaceSandbox::InstallTerminationSignalHandler(
234 SIGUSR1, NamespaceSandbox::SignalExitCode(SIGUSR1)));
236 raise(SIGUSR1);
237 CHECK_EQ(1, signal_handler_called);
240 } // namespace
242 } // namespace sandbox