blob: e4239cd563d5b97483cd7da1d761561033137094 [file] [log] [blame]
Ben Chan6f391cb2012-03-21 17:38:21 -07001// Copyright (c) 2012 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/process.h"
6
François Degrosc0b33b12019-09-12 14:50:43 +10007#include <csignal>
8#include <memory>
9#include <ostream>
10#include <utility>
11
12#include <fcntl.h>
13#include <sys/time.h>
14#include <unistd.h>
15
Sergei Datsenkocd676b72019-05-10 11:42:05 +100016#include <base/files/file_path.h>
17#include <base/files/file_util.h>
François Degrosc0b33b12019-09-12 14:50:43 +100018#include <base/files/scoped_file.h>
19#include <base/logging.h>
20#include <base/strings/string_piece.h>
21#include <base/strings/stringprintf.h>
22#include <chromeos/libminijail.h>
Sergei Datsenkocd676b72019-05-10 11:42:05 +100023
Ben Chan6f391cb2012-03-21 17:38:21 -070024#include <gmock/gmock.h>
25#include <gtest/gtest.h>
26
Sergei Datsenkocd676b72019-05-10 11:42:05 +100027#include "cros-disks/sandboxed_process.h"
28
Ben Chan6f391cb2012-03-21 17:38:21 -070029namespace cros_disks {
François Degrosc0b33b12019-09-12 14:50:43 +100030namespace {
Ben Chan6f391cb2012-03-21 17:38:21 -070031
Sergei Datsenkocd676b72019-05-10 11:42:05 +100032using testing::_;
33using testing::Contains;
Sergei Datsenko9246e9c2019-03-22 10:26:47 +110034using testing::ElementsAre;
François Degrosc0b33b12019-09-12 14:50:43 +100035using testing::PrintToStringParamName;
Sergei Datsenko9246e9c2019-03-22 10:26:47 +110036using testing::Return;
François Degrosc0b33b12019-09-12 14:50:43 +100037using testing::SizeIs;
38using testing::StartsWith;
39using testing::UnorderedElementsAre;
40using testing::Values;
Sergei Datsenko9246e9c2019-03-22 10:26:47 +110041
François Degrosc0b33b12019-09-12 14:50:43 +100042// Sets a signal handler for SIGALRM and an interval timer signaling SIGALRM at
43// regular intervals.
44class AlarmGuard {
45 public:
46 explicit AlarmGuard(const int timer_interval_ms) {
47 CHECK(!old_handler_);
48 count_ = 0;
49 old_handler_ = signal(SIGALRM, &Handler);
50 CHECK_NE(old_handler_, SIG_ERR);
51 SetIntervalTimer(timer_interval_ms * 1000 /* microseconds */);
52 }
53
54 ~AlarmGuard() {
55 SetIntervalTimer(0);
56 CHECK_EQ(signal(SIGALRM, old_handler_), &Handler);
57 old_handler_ = nullptr;
58 }
59
60 // Number of times SIGALRM has been received.
61 static int count() { return count_; }
62
63 private:
64 static void Handler(int sig) {
65 CHECK_EQ(sig, SIGALRM);
66 ++count_;
67 }
68
69 static void SetIntervalTimer(const int usec) {
70 const itimerval tv = {{0, usec}, {0, usec}};
71 if (setitimer(ITIMER_REAL, &tv, nullptr) < 0) {
72 PLOG(FATAL) << "Cannot set timer";
73 }
74 }
75
76 // Number of times SIGALRM has been received.
77 static int count_;
78
79 using SigHandler = void (*)(int);
80 static SigHandler old_handler_;
81
82 DISALLOW_COPY_AND_ASSIGN(AlarmGuard);
83};
84
85int AlarmGuard::count_ = 0;
86AlarmGuard::SigHandler AlarmGuard::old_handler_ = nullptr;
87
88// Anonymous pipe.
89struct Pipe {
90 base::ScopedFD read_fd, write_fd;
91
92 // Creates an open pipe.
93 Pipe() {
94 int fds[2];
95 if (pipe(fds) < 0) {
96 PLOG(FATAL) << "Cannot create pipe ";
97 }
98
99 read_fd.reset(fds[0]);
100 write_fd.reset(fds[1]);
101 }
102};
103
104std::string Read(const base::ScopedFD fd) {
105 char buffer[PIPE_BUF];
106
107 LOG(INFO) << "Reading up to " << PIPE_BUF << " bytes from fd " << fd.get()
108 << "...";
109 const ssize_t bytes_read = HANDLE_EINTR(read(fd.get(), buffer, PIPE_BUF));
110 PLOG_IF(FATAL, bytes_read < 0)
111 << "Cannot read from file descriptor " << fd.get();
112
113 LOG(INFO) << "Read " << bytes_read << " bytes from fd " << fd.get();
114 return std::string(buffer, bytes_read);
115}
116
117void WriteAndClose(const base::ScopedFD fd, base::StringPiece s) {
118 while (!s.empty()) {
119 const ssize_t bytes_written =
120 HANDLE_EINTR(write(fd.get(), s.data(), s.size()));
121 PLOG_IF(FATAL, bytes_written < 0)
122 << "Cannot write to file descriptor " << fd.get();
123
124 s.remove_prefix(bytes_written);
125 }
126}
127
128// A mock Process class for testing the Process base class.
Ben Chan6f391cb2012-03-21 17:38:21 -0700129class ProcessUnderTest : public Process {
130 public:
Ben Chan21150af2019-09-11 17:04:07 -0700131 MOCK_METHOD(pid_t,
132 StartImpl,
133 (base::ScopedFD*, base::ScopedFD*, base::ScopedFD*),
134 (override));
135 MOCK_METHOD(int, WaitImpl, (), (override));
136 MOCK_METHOD(bool, WaitNonBlockingImpl, (int*), (override));
Ben Chan6f391cb2012-03-21 17:38:21 -0700137};
138
François Degrosc0b33b12019-09-12 14:50:43 +1000139struct ProcessFactory {
140 base::StringPiece name;
141 std::unique_ptr<Process> (*make_process)();
142};
143
144std::ostream& operator<<(std::ostream& out, const ProcessFactory& x) {
145 return out << x.name;
146}
147
148} // namespace
149
Ben Chan6f391cb2012-03-21 17:38:21 -0700150class ProcessTest : public ::testing::Test {
151 protected:
152 ProcessUnderTest process_;
153};
154
155TEST_F(ProcessTest, GetArguments) {
François Degros5593b8c2019-07-25 12:27:42 +1000156 const char* const kTestArguments[] = {"/bin/ls", "-l", "", "."};
Ben Chan6057fe62016-12-02 10:08:59 -0800157 for (const char* test_argument : kTestArguments) {
158 process_.AddArgument(test_argument);
Ben Chan6f391cb2012-03-21 17:38:21 -0700159 }
160
Sergei Datsenkocd676b72019-05-10 11:42:05 +1000161 EXPECT_THAT(process_.arguments(), ElementsAre("/bin/ls", "-l", "", "."));
Sergei Datsenko9246e9c2019-03-22 10:26:47 +1100162
François Degros5593b8c2019-07-25 12:27:42 +1000163 char* const* arguments = process_.GetArguments();
Ben Chan44e7ea62014-08-29 18:13:37 -0700164 EXPECT_NE(nullptr, arguments);
Ben Chan6057fe62016-12-02 10:08:59 -0800165 for (const char* test_argument : kTestArguments) {
166 EXPECT_STREQ(test_argument, *arguments);
167 ++arguments;
Ben Chan6f391cb2012-03-21 17:38:21 -0700168 }
Ben Chan6057fe62016-12-02 10:08:59 -0800169 EXPECT_EQ(nullptr, *arguments);
Ben Chan6f391cb2012-03-21 17:38:21 -0700170}
171
172TEST_F(ProcessTest, GetArgumentsWithNoArgumentsAdded) {
Ben Chan44e7ea62014-08-29 18:13:37 -0700173 EXPECT_EQ(nullptr, process_.GetArguments());
Ben Chan6f391cb2012-03-21 17:38:21 -0700174}
175
Sergei Datsenko9246e9c2019-03-22 10:26:47 +1100176TEST_F(ProcessTest, Run_Success) {
Sergei Datsenkocd676b72019-05-10 11:42:05 +1000177 process_.AddArgument("foo");
François Degros5593b8c2019-07-25 12:27:42 +1000178 EXPECT_CALL(process_, StartImpl(_, _, _)).WillOnce(Return(123));
Sergei Datsenkocd676b72019-05-10 11:42:05 +1000179 EXPECT_CALL(process_, WaitImpl()).WillOnce(Return(42));
180 EXPECT_CALL(process_, WaitNonBlockingImpl(_)).Times(0);
Sergei Datsenko9246e9c2019-03-22 10:26:47 +1100181 EXPECT_EQ(42, process_.Run());
182}
183
184TEST_F(ProcessTest, Run_Fail) {
Sergei Datsenkocd676b72019-05-10 11:42:05 +1000185 process_.AddArgument("foo");
François Degros5593b8c2019-07-25 12:27:42 +1000186 EXPECT_CALL(process_, StartImpl(_, _, _)).WillOnce(Return(-1));
Sergei Datsenkocd676b72019-05-10 11:42:05 +1000187 EXPECT_CALL(process_, WaitImpl()).Times(0);
188 EXPECT_CALL(process_, WaitNonBlockingImpl(_)).Times(0);
Sergei Datsenko9246e9c2019-03-22 10:26:47 +1100189 EXPECT_EQ(-1, process_.Run());
190}
191
François Degrosc0b33b12019-09-12 14:50:43 +1000192class ProcessRunTest : public ::testing::TestWithParam<ProcessFactory> {
193 public:
194 ProcessRunTest() {
195 // Ensure that we get an error message if Minijail crashes.
196 // TODO(crbug.com/1007098) Remove the following line or this comment
197 // depending on how this bug is resolved.
198 minijail_log_to_fd(STDERR_FILENO, 0);
199 }
Sergei Datsenkocd676b72019-05-10 11:42:05 +1000200
François Degrosc0b33b12019-09-12 14:50:43 +1000201 const std::unique_ptr<Process> process_ = GetParam().make_process();
202};
Sergei Datsenkocd676b72019-05-10 11:42:05 +1000203
François Degrosc0b33b12019-09-12 14:50:43 +1000204TEST_P(ProcessRunTest, ReturnsZero) {
205 Process& process = *process_;
206 process.AddArgument("/bin/true");
207 EXPECT_EQ(process.Run(), 0);
Sergei Datsenkocd676b72019-05-10 11:42:05 +1000208}
209
François Degrosc0b33b12019-09-12 14:50:43 +1000210TEST_P(ProcessRunTest, ReturnsNonZero) {
211 Process& process = *process_;
212 process.AddArgument("/bin/false");
213 EXPECT_EQ(process.Run(), 1);
214}
215
216TEST_P(ProcessRunTest, KilledBySigKill) {
217 Process& process = *process_;
218 process.AddArgument("/bin/sh");
219 process.AddArgument("-c");
220 process.AddArgument("kill -KILL $$; sleep 1000");
221 EXPECT_EQ(process.Run(), 128 + SIGKILL);
222}
223
224TEST_P(ProcessRunTest, KilledBySigSys) {
225 Process& process = *process_;
226 process.AddArgument("/bin/sh");
227 process.AddArgument("-c");
228 process.AddArgument("kill -SYS $$; sleep 1000");
229 EXPECT_EQ(process.Run(), MINIJAIL_ERR_JAIL);
230}
231
232TEST_P(ProcessRunTest, CannotExec) {
233 Process& process = *process_;
234 process.AddArgument("non_existing_exe_foo_bar");
235 // SandboxedProcess returns 255, but it isn't explicitly specified.
236 EXPECT_GT(process.Run(), 0);
237}
238
239TEST_P(ProcessRunTest, CapturesInterleavedOutputs) {
240 Process& process = *process_;
241 process.AddArgument("/bin/sh");
242 process.AddArgument("-c");
243 process.AddArgument(R"(
244 printf 'Line 1\nLine ' >&1;
245 printf 'Line 2\nLine' >&2;
246 printf '3\nLine 4\n' >&1;
247 printf ' 5\nLine 6' >&2;
248 )");
249
250 std::vector<std::string> output;
251 EXPECT_EQ(process.Run(&output), 0);
252 EXPECT_THAT(output, UnorderedElementsAre("OUT: Line 1", "OUT: Line 3",
253 "OUT: Line 4", "ERR: Line 2",
254 "ERR: Line 5", "ERR: Line 6"));
255}
256
257TEST_P(ProcessRunTest, CapturesLotsOfOutputData) {
258 Process& process = *process_;
259 process.AddArgument("/bin/sh");
260 process.AddArgument("-c");
261 process.AddArgument(R"(
262 for i in $(seq 1 1000); do
263 printf 'Message %i\n' $i >&1;
264 printf 'Error %i\n' $i >&2;
265 done;
266 )");
267
268 std::vector<std::string> output;
269 EXPECT_EQ(process.Run(&output), 0);
270 EXPECT_THAT(output, SizeIs(2000));
271}
272
273TEST_P(ProcessRunTest, DoesNotBlockWhenReadingFromStdIn) {
274 Process& process = *process_;
275 process.AddArgument("/bin/cat");
276
277 // By default, /bin/cat reads from stdin. If the pipe connected to stdin was
278 // left open, the process would block indefinitely while reading from it.
279 EXPECT_EQ(process.Run(), 0);
280}
281
282TEST_P(ProcessRunTest, DoesNotWaitForBackgroundProcessToFinish) {
283 Process& process = *process_;
284 process.AddArgument("/bin/sh");
285 process.AddArgument("-c");
286
287 // Pipe to unblock the background process and allow it to finish.
288 Pipe p1;
289 ASSERT_EQ(fcntl(p1.write_fd.get(), F_SETFD, FD_CLOEXEC), 0);
290 // Pipe to monitor the background process and wait for it to finish.
291 Pipe p2;
292 ASSERT_EQ(fcntl(p2.read_fd.get(), F_SETFD, FD_CLOEXEC), 0);
293
294 process.AddArgument(base::StringPrintf(R"(
295 printf 'Begin\n';
296 (
297 exec 0<&-;
298 exec 1>&-;
299 exec 2>&-;
300 read line <&%d;
301 printf '%%s and End' "$line" >&%d;
302 exit 42;
303 )&
304 printf 'Started background process %%i\n' $!
305 exit 5;
306 )",
307 p1.read_fd.get(), p2.write_fd.get()));
308
309 std::vector<std::string> output;
310 EXPECT_EQ(process.Run(&output), 5);
311 EXPECT_THAT(
312 output,
313 ElementsAre("OUT: Begin", StartsWith("OUT: Started background process")));
314
315 // Unblock the orphaned background process.
316 LOG(INFO) << "Unblocking background process";
317 WriteAndClose(std::move(p1.write_fd), "Continue\n");
318
319 // Wait for the orphaned background processes to finish. If the main test
320 // program finishes before these background processes, the test framework
321 // complains about leaked processes.
322 p2.write_fd.reset();
323 LOG(INFO) << "Waiting for background process to finish";
324 EXPECT_EQ(Read(std::move(p2.read_fd)), "Continue and End");
325}
326
327TEST_P(ProcessRunTest, UndisturbedBySignalsWhenWaiting) {
328 Process& process = *process_;
329 process.AddArgument("/bin/sh");
330 process.AddArgument("-c");
331 process.AddArgument(R"(
332 sleep 1;
333 exit 42;
334 )");
335
336 // Activate an interval timer.
337 const AlarmGuard guard(13 /* milliseconds */);
338 EXPECT_TRUE(process.Start());
339 EXPECT_EQ(process.Wait(), 42);
340 EXPECT_GT(AlarmGuard::count(), 0);
341}
342
343INSTANTIATE_TEST_SUITE_P(ProcessRun,
344 ProcessRunTest,
345 Values(ProcessFactory{
346 "SandboxedProcess",
347 []() -> std::unique_ptr<Process> {
348 return std::make_unique<SandboxedProcess>();
349 }}),
350 PrintToStringParamName());
351
Ben Chan6f391cb2012-03-21 17:38:21 -0700352} // namespace cros_disks