blob: 88130a8c8adc8b228a465f3335538cd9843ffdd1 [file] [log] [blame]
Sergei Datsenko495f5da2019-06-06 17:44:23 +10001// Copyright 2019 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 "cros-disks/sandboxed_init.h"
6
7#include <functional>
8
9#include <stdlib.h>
10#include <sys/prctl.h>
11#include <unistd.h>
12
13#include <base/bind.h>
Qijiang Fan713061e2021-03-08 15:45:12 +090014#include <base/check.h>
15#include <base/check_op.h>
Sergei Datsenko6f740dd2019-07-17 17:30:32 +100016#include <base/files/file_util.h>
Qijiang Fan886c4692021-02-19 11:54:10 +090017#include <base/notreached.h>
Sergei Datsenko6f740dd2019-07-17 17:30:32 +100018#include <base/time/time.h>
Sergei Datsenko495f5da2019-06-06 17:44:23 +100019#include <gmock/gmock.h>
20#include <gtest/gtest.h>
21
22namespace cros_disks {
23
24namespace {
25
Sergei Datsenko6f740dd2019-07-17 17:30:32 +100026constexpr auto kTimeout = base::TimeDelta::FromSeconds(30);
Sergei Datsenko495f5da2019-06-06 17:44:23 +100027
28int CallFunc(std::function<int()> func) {
29 return func();
30}
31
32pid_t RunInFork(std::function<int()> func) {
33 pid_t pid = fork();
34 CHECK_NE(-1, pid);
35 if (pid == 0) {
36 CHECK_NE(-1, prctl(PR_SET_CHILD_SUBREAPER, 1));
37 exit(func());
38 }
39 return pid;
40}
41
42class SandboxedInitTest : public testing::Test {
43 public:
Ben Chanfc77d712019-06-20 12:45:56 -070044 SandboxedInitTest() = default;
Sergei Datsenko495f5da2019-06-06 17:44:23 +100045
Ben Chanfc77d712019-06-20 12:45:56 -070046 ~SandboxedInitTest() override = default;
Sergei Datsenko495f5da2019-06-06 17:44:23 +100047
48 protected:
49 void TearDown() override {
50 if (pid_ > 0) {
51 kill(pid_, SIGKILL);
52 }
53 }
54
55 void RunUnderInit(std::function<int()> func) {
François Degros1ef69942019-10-01 15:31:17 +100056 SandboxedInit init(
57 SubprocessPipe::Open(SubprocessPipe::kParentToChild, &in_),
58 SubprocessPipe::Open(SubprocessPipe::kChildToParent, &out_),
59 SubprocessPipe::Open(SubprocessPipe::kChildToParent, &err_),
60 SubprocessPipe::Open(SubprocessPipe::kChildToParent, &ctrl_));
François Degros73168562019-10-03 13:34:26 +100061 pid_ = RunInFork([&init, func]() -> int {
François Degros43208ad2019-09-27 14:01:00 +100062 CHECK_EQ(signal(SIGUSR1, [](int sig) { CHECK_EQ(sig, SIGUSR1); }),
63 SIG_DFL);
Anand K Mistry08a71ca2019-09-13 10:38:20 +100064 init.RunInsideSandboxNoReturn(base::BindOnce(CallFunc, func));
Sergei Datsenko495f5da2019-06-06 17:44:23 +100065 NOTREACHED();
Sergei Datsenko495f5da2019-06-06 17:44:23 +100066 });
Sergei Datsenko6f740dd2019-07-17 17:30:32 +100067 CHECK(base::SetNonBlocking(ctrl_.get()));
Sergei Datsenko495f5da2019-06-06 17:44:23 +100068 }
69
70 bool Wait(int* status, bool no_hang) {
71 CHECK_LT(0, pid_);
72 int ret = waitpid(pid_, status, no_hang ? WNOHANG : 0);
73 if (ret < 0) {
74 PLOG(FATAL) << "waitpid failed.";
75 return true;
76 }
77 if (ret == 0) {
78 return false;
79 }
80 if (WIFEXITED(*status) || WIFSIGNALED(*status)) {
81 pid_ = -1;
82 return true;
83 }
84 return false;
85 }
86
Sergei Datsenko6f740dd2019-07-17 17:30:32 +100087 bool Poll(const base::TimeDelta& timeout, std::function<bool()> func) {
88 constexpr int64_t kUSleepDelay = 100000;
89 auto counter = timeout.InMicroseconds() / kUSleepDelay;
90 while (!func()) {
91 if (counter-- <= 0) {
92 return false;
93 }
94 usleep(kUSleepDelay);
95 }
96 return true;
97 }
98
99 bool PollForExitStatus(const base::TimeDelta& timeout, int* status) {
100 return Poll(timeout, [status, this]() {
101 return SandboxedInit::PollLauncherStatus(&ctrl_, status);
102 });
103 }
104
105 bool PollWait(const base::TimeDelta& timeout, int* status) {
106 return Poll(timeout, [status, this]() { return Wait(status, true); });
107 }
108
Sergei Datsenko495f5da2019-06-06 17:44:23 +1000109 pid_t pid_ = -1;
110 base::ScopedFD in_, out_, err_, ctrl_;
111};
112
113} // namespace
114
115TEST_F(SandboxedInitTest, BasicReturnCode) {
116 pid_ = RunInFork([]() { return 42; });
117
118 int status;
119 ASSERT_TRUE(Wait(&status, false));
120 ASSERT_EQ(42, WEXITSTATUS(status));
121}
122
123TEST_F(SandboxedInitTest, RunInitNoDaemon_WaitForTermination) {
124 RunUnderInit([]() { return 12; });
125
126 int status;
127 ASSERT_TRUE(Wait(&status, false));
128 ASSERT_EQ(12, WEXITSTATUS(status));
129}
130
131TEST_F(SandboxedInitTest, RunInitNoDaemon_Crash) {
Tom Hughesd89eea12020-08-24 18:13:31 -0700132 RunUnderInit([]() -> int { _exit(1); });
Sergei Datsenko495f5da2019-06-06 17:44:23 +1000133
134 int status;
135 ASSERT_TRUE(Wait(&status, false));
136 ASSERT_EQ(1, WEXITSTATUS(status));
137}
138
139TEST_F(SandboxedInitTest, RunInitNoDaemon_IO) {
140 RunUnderInit([]() {
François Degros43208ad2019-09-27 14:01:00 +1000141 EXPECT_EQ(4, write(STDOUT_FILENO, "abcd", 4));
Sergei Datsenko495f5da2019-06-06 17:44:23 +1000142 return 12;
143 });
Sergei Datsenko495f5da2019-06-06 17:44:23 +1000144
145 char buffer[5] = {0};
146 ssize_t rd = read(out_.get(), buffer, 4);
147 EXPECT_EQ(4, rd);
148 EXPECT_STREQ("abcd", buffer);
149
150 int status;
151 ASSERT_TRUE(Wait(&status, false));
152 ASSERT_EQ(12, WEXITSTATUS(status));
153}
154
François Degros43208ad2019-09-27 14:01:00 +1000155TEST_F(SandboxedInitTest, RunInitNoDaemon_UndisturbedBySignal) {
156 RunUnderInit([]() {
157 // Signal that process started
158 HANDLE_EINTR(write(STDOUT_FILENO, "Begin", 5));
159
160 // Wait to be unblocked
161 char buffer[PIPE_BUF];
162 HANDLE_EINTR(read(STDIN_FILENO, buffer, PIPE_BUF));
163
164 // Signal that process was unblocked
165 HANDLE_EINTR(write(STDOUT_FILENO, "End", 3));
166 return 12;
167 });
168
169 // Wait for process to start.
170 char buffer[PIPE_BUF];
171 ssize_t rd = read(out_.get(), buffer, PIPE_BUF);
172 ASSERT_GT(rd, 0);
173 EXPECT_EQ(base::StringPiece(buffer, rd), "Begin");
174
175 // Send SIGUSR1 to init process.
176 for (int i = 0; i < 5; ++i) {
177 EXPECT_EQ(kill(pid_, SIGUSR1), 0);
178 usleep(100'000);
179 }
180
181 // Unblock the process.
182 ASSERT_GT(write(in_.get(), "Continue", 8), 0);
183
184 // Wait for process to continue.
185 rd = read(out_.get(), buffer, PIPE_BUF);
186 ASSERT_GT(rd, 0);
187 EXPECT_EQ(base::StringPiece(buffer, rd), "End");
188
189 // Wait for init process to finish.
190 int status;
191 ASSERT_TRUE(Wait(&status, false));
192 ASSERT_EQ(WEXITSTATUS(status), 12);
193}
194
Sergei Datsenko495f5da2019-06-06 17:44:23 +1000195TEST_F(SandboxedInitTest, RunInitNoDaemon_ReadLauncherCode) {
196 RunUnderInit([]() { return 12; });
Sergei Datsenko495f5da2019-06-06 17:44:23 +1000197
Sergei Datsenko495f5da2019-06-06 17:44:23 +1000198 ASSERT_TRUE(ctrl_.is_valid());
Sergei Datsenko6f740dd2019-07-17 17:30:32 +1000199 int status;
200 ASSERT_TRUE(PollForExitStatus(kTimeout, &status));
Sergei Datsenko495f5da2019-06-06 17:44:23 +1000201 ASSERT_FALSE(ctrl_.is_valid());
François Degros02c5c712019-10-03 13:00:11 +1000202 EXPECT_EQ(12, status);
Sergei Datsenko495f5da2019-06-06 17:44:23 +1000203
204 ASSERT_TRUE(Wait(&status, false));
205 ASSERT_EQ(12, WEXITSTATUS(status));
206}
207
208TEST_F(SandboxedInitTest, RunInitWithDaemon) {
209 int comm[2];
210 CHECK_NE(-1, pipe(comm));
211 RunUnderInit([comm]() {
212 if (daemon(0, 0) == -1) {
213 PLOG(FATAL) << "Can't daemon";
214 }
215 char buffer[4] = {0};
216 EXPECT_EQ(4, read(comm[0], buffer, 4));
Sergei Datsenko1c8f2152019-06-19 15:21:21 +1000217 return 42;
Sergei Datsenko495f5da2019-06-06 17:44:23 +1000218 });
Sergei Datsenko495f5da2019-06-06 17:44:23 +1000219
220 int status;
Sergei Datsenko6f740dd2019-07-17 17:30:32 +1000221 ASSERT_TRUE(PollForExitStatus(kTimeout, &status));
François Degros02c5c712019-10-03 13:00:11 +1000222 EXPECT_EQ(0, status);
Sergei Datsenko495f5da2019-06-06 17:44:23 +1000223
224 EXPECT_FALSE(Wait(&status, true));
225
226 // Tell the daemon to stop.
227 EXPECT_EQ(4, write(comm[1], "die", 4));
Sergei Datsenko6f740dd2019-07-17 17:30:32 +1000228 EXPECT_TRUE(Wait(&status, false));
229 close(comm[0]);
230 close(comm[1]);
231 EXPECT_EQ(42, WEXITSTATUS(status));
232}
233
234TEST_F(SandboxedInitTest, RunInitNoDaemon_NonBlockingWait) {
235 int comm[2];
236 CHECK_NE(-1, pipe(comm));
237 RunUnderInit([comm]() {
238 char buffer[4] = {0};
239 EXPECT_EQ(4, read(comm[0], buffer, 4));
240 return 6;
241 });
242
243 int status;
244 EXPECT_FALSE(Wait(&status, true));
245
246 EXPECT_EQ(4, write(comm[1], "die", 4));
247 EXPECT_TRUE(PollWait(kTimeout, &status));
248 close(comm[0]);
249 close(comm[1]);
250 EXPECT_EQ(6, WEXITSTATUS(status));
251}
252
253TEST_F(SandboxedInitTest, RunInitWithDaemon_NonBlockingWait) {
254 int comm[2];
255 CHECK_NE(-1, pipe(comm));
256 RunUnderInit([comm]() {
257 if (daemon(0, 0) == -1) {
258 PLOG(FATAL) << "Can't daemon";
259 }
260 sigset_t s = {};
261 PCHECK(0 == sigemptyset(&s));
262 PCHECK(0 == sigaddset(&s, SIGPIPE));
263 PCHECK(0 == sigprocmask(SIG_BLOCK, &s, nullptr));
264 char buffer[4] = {0};
265 EXPECT_EQ(4, read(comm[0], buffer, 4));
266 return 42;
267 });
268
269 int status;
270 ASSERT_TRUE(PollForExitStatus(kTimeout, &status));
François Degros02c5c712019-10-03 13:00:11 +1000271 EXPECT_EQ(0, status);
Sergei Datsenko6f740dd2019-07-17 17:30:32 +1000272
273 EXPECT_FALSE(Wait(&status, true));
274
275 // Tell the daemon to stop.
276 EXPECT_EQ(4, write(comm[1], "die", 4));
Sergei Datsenko495f5da2019-06-06 17:44:23 +1000277 close(comm[0]);
278 close(comm[1]);
279
Sergei Datsenko6f740dd2019-07-17 17:30:32 +1000280 EXPECT_TRUE(PollWait(kTimeout, &status));
Sergei Datsenko1c8f2152019-06-19 15:21:21 +1000281 EXPECT_EQ(42, WEXITSTATUS(status));
Sergei Datsenko495f5da2019-06-06 17:44:23 +1000282}
283
284} // namespace cros_disks