blob: 0d32c1a469cd263bcb977297845add7f45df1255 [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 <utility>
8#include <stdlib.h>
François Degros1ef69942019-10-01 15:31:17 +10009#include <unistd.h>
10
Sergei Datsenko1c8f2152019-06-19 15:21:21 +100011#include <sys/prctl.h>
Sergei Datsenko495f5da2019-06-06 17:44:23 +100012#include <sys/wait.h>
Sergei Datsenko495f5da2019-06-06 17:44:23 +100013
Qijiang Fan713061e2021-03-08 15:45:12 +090014#include <base/check.h>
15#include <base/check_op.h>
Sergei Datsenko495f5da2019-06-06 17:44:23 +100016#include <base/files/file_util.h>
17#include <base/logging.h>
Qijiang Fan886c4692021-02-19 11:54:10 +090018#include <base/notreached.h>
Sergei Datsenko495f5da2019-06-06 17:44:23 +100019#include <brillo/syslog_logging.h>
20#include <chromeos/libminijail.h>
21
22namespace cros_disks {
Sergei Datsenko495f5da2019-06-06 17:44:23 +100023namespace {
Sergei Datsenko495f5da2019-06-06 17:44:23 +100024
François Degros21bc9bb2020-04-18 00:14:43 +100025// Signal handler that forwards the received signal to all children processes.
Sergei Datsenko495f5da2019-06-06 17:44:23 +100026void SigTerm(int sig) {
François Degros21bc9bb2020-04-18 00:14:43 +100027 if (kill(-1, sig) < 0) {
28 const int err = errno;
29 RAW_LOG(ERROR, "Cannot broadcast signal");
30 _exit(err + 64);
Sergei Datsenko1c8f2152019-06-19 15:21:21 +100031 }
Sergei Datsenko495f5da2019-06-06 17:44:23 +100032}
33
34} // namespace
35
François Degrosc2bfe0e2019-10-14 13:08:49 +110036SubprocessPipe::SubprocessPipe(const Direction direction) {
François Degros4a76d702019-09-26 14:54:35 +100037 int fds[2];
François Degrosc2bfe0e2019-10-14 13:08:49 +110038 PCHECK(pipe(fds) >= 0);
39 child_fd.reset(fds[1 - direction]);
40 parent_fd.reset(fds[direction]);
41 PCHECK(base::SetCloseOnExec(parent_fd.get()));
Sergei Datsenko495f5da2019-06-06 17:44:23 +100042}
43
François Degros1ef69942019-10-01 15:31:17 +100044base::ScopedFD SubprocessPipe::Open(const Direction direction,
45 base::ScopedFD* const parent_fd) {
46 DCHECK(parent_fd);
47
48 SubprocessPipe p(direction);
49 *parent_fd = std::move(p.parent_fd);
50 return std::move(p.child_fd);
51}
52
53SandboxedInit::SandboxedInit(base::ScopedFD in_fd,
54 base::ScopedFD out_fd,
55 base::ScopedFD err_fd,
56 base::ScopedFD ctrl_fd)
57 : in_fd_(std::move(in_fd)),
58 out_fd_(std::move(out_fd)),
59 err_fd_(std::move(err_fd)),
60 ctrl_fd_(std::move(ctrl_fd)) {}
François Degros4a76d702019-09-26 14:54:35 +100061
Sergei Datsenko495f5da2019-06-06 17:44:23 +100062SandboxedInit::~SandboxedInit() = default;
63
François Degros73168562019-10-03 13:34:26 +100064[[noreturn]] void SandboxedInit::RunInsideSandboxNoReturn(
Anand K Mistry08a71ca2019-09-13 10:38:20 +100065 base::OnceCallback<int()> launcher) {
Sergei Datsenko495f5da2019-06-06 17:44:23 +100066 // To run our custom init that handles daemonized processes inside the
67 // sandbox we have to set up fork/exec ourselves. We do error-handling
68 // the "minijail-style" - abort if something not right.
69
70 // This performs as the init process in the jail PID namespace (PID 1).
71 // Redirect in/out so logging can communicate assertions and children
72 // to inherit right FDs.
73 brillo::InitLog(brillo::kLogToSyslog | brillo::kLogToStderr);
François Degros1ef69942019-10-01 15:31:17 +100074
75 if (dup2(in_fd_.get(), STDIN_FILENO) < 0) {
76 PLOG(FATAL) << "Cannot dup2 stdin";
Sergei Datsenko495f5da2019-06-06 17:44:23 +100077 }
François Degros1ef69942019-10-01 15:31:17 +100078
79 if (dup2(out_fd_.get(), STDOUT_FILENO) < 0) {
80 PLOG(FATAL) << "Cannot dup2 stdout";
Sergei Datsenko495f5da2019-06-06 17:44:23 +100081 }
François Degros1ef69942019-10-01 15:31:17 +100082
83 if (dup2(err_fd_.get(), STDERR_FILENO) < 0) {
84 PLOG(FATAL) << "Cannot dup2 stderr";
Sergei Datsenko495f5da2019-06-06 17:44:23 +100085 }
86
Anand K Mistry3e2b58b2019-10-02 15:59:05 +100087 // Set an identifiable process name.
François Degros4a76d702019-09-26 14:54:35 +100088 if (prctl(PR_SET_NAME, "cros-disks-INIT") < 0) {
Sergei Datsenko1c8f2152019-06-19 15:21:21 +100089 PLOG(WARNING) << "Can't set init's process name";
90 }
91
François Degros1ef69942019-10-01 15:31:17 +100092 // Close unused file descriptors.
93 in_fd_.reset();
94 out_fd_.reset();
95 err_fd_.reset();
François Degrosc2bfe0e2019-10-14 13:08:49 +110096
97 // Avoid leaking file descriptor into launcher process.
François Degros1ef69942019-10-01 15:31:17 +100098 PCHECK(base::SetCloseOnExec(ctrl_fd_.get()));
Sergei Datsenko495f5da2019-06-06 17:44:23 +100099
François Degros21bc9bb2020-04-18 00:14:43 +1000100 // Setup the SIGTERM signal handler.
101 if (signal(SIGTERM, SigTerm) == SIG_ERR) {
102 PLOG(FATAL) << "Cannot install signal handler";
103 }
104
Sergei Datsenko495f5da2019-06-06 17:44:23 +1000105 // PID of the launcher process inside the jail PID namespace (e.g. PID 2).
106 pid_t root_pid = StartLauncher(std::move(launcher));
107 CHECK_LT(0, root_pid);
108
François Degros1ef69942019-10-01 15:31:17 +1000109 _exit(RunInitLoop(root_pid, std::move(ctrl_fd_)));
Sergei Datsenko495f5da2019-06-06 17:44:23 +1000110 NOTREACHED();
111}
112
113int SandboxedInit::RunInitLoop(pid_t root_pid, base::ScopedFD ctrl_fd) {
114 CHECK(base::SetNonBlocking(ctrl_fd.get()));
115
116 // Most of this is mirroring minijail's embedded "init" (exit status handling)
117 // with addition of piping the "root" status code to the calling process.
118
François Degros02c5c712019-10-03 13:00:11 +1000119 // By now it's unlikely something to go wrong here, so disconnect
120 // from in/out.
121 HANDLE_EINTR(close(STDIN_FILENO));
122 HANDLE_EINTR(close(STDOUT_FILENO));
123 HANDLE_EINTR(close(STDERR_FILENO));
124
Sergei Datsenko495f5da2019-06-06 17:44:23 +1000125 // This loop will only end when either there are no processes left inside
126 // our PID namespace or we get a signal.
Sergei Datsenko1c8f2152019-06-19 15:21:21 +1000127 int last_failure_code = 0;
Sergei Datsenko495f5da2019-06-06 17:44:23 +1000128
François Degros43208ad2019-09-27 14:01:00 +1000129 while (true) {
130 // Wait for any child to terminate.
131 int wstatus;
132 const pid_t pid = HANDLE_EINTR(wait(&wstatus));
133
134 if (pid < 0) {
135 if (errno == ECHILD) {
136 // No more child
François Degros02c5c712019-10-03 13:00:11 +1000137 CHECK(!ctrl_fd.is_valid());
138 return last_failure_code;
François Degros43208ad2019-09-27 14:01:00 +1000139 }
140
141 PLOG(FATAL) << "Cannot wait for child processes";
142 }
143
François Degros02c5c712019-10-03 13:00:11 +1000144 // Convert wait status to exit code.
145 const int exit_code = WStatusToStatus(wstatus);
146 if (exit_code >= 0) {
147 // A child process finished.
Sergei Datsenko1c8f2152019-06-19 15:21:21 +1000148 if (exit_code) {
149 last_failure_code = exit_code;
150 }
151
François Degros02c5c712019-10-03 13:00:11 +1000152 // Was it the launcher?
Sergei Datsenko495f5da2019-06-06 17:44:23 +1000153 if (pid == root_pid) {
François Degros02c5c712019-10-03 13:00:11 +1000154 // Write the launcher's exit code to the control pipe.
155 const ssize_t written =
156 HANDLE_EINTR(write(ctrl_fd.get(), &exit_code, sizeof(exit_code)));
157 if (written != sizeof(exit_code)) {
158 PLOG(ERROR) << "Cannot write exit code";
Sergei Datsenko495f5da2019-06-06 17:44:23 +1000159 return MINIJAIL_ERR_INIT;
160 }
Sergei Datsenko1c8f2152019-06-19 15:21:21 +1000161
François Degros02c5c712019-10-03 13:00:11 +1000162 ctrl_fd.reset();
Sergei Datsenko495f5da2019-06-06 17:44:23 +1000163 }
164 }
165 }
Sergei Datsenko495f5da2019-06-06 17:44:23 +1000166}
167
Anand K Mistry08a71ca2019-09-13 10:38:20 +1000168pid_t SandboxedInit::StartLauncher(base::OnceCallback<int()> launcher) {
Sergei Datsenko495f5da2019-06-06 17:44:23 +1000169 pid_t exec_child = fork();
Sergei Datsenko1c8f2152019-06-19 15:21:21 +1000170
François Degros4a76d702019-09-26 14:54:35 +1000171 if (exec_child < 0) {
Sergei Datsenko495f5da2019-06-06 17:44:23 +1000172 PLOG(FATAL) << "Can't fork";
173 }
174
175 if (exec_child == 0) {
François Degros4a76d702019-09-26 14:54:35 +1000176 // In child process
Sergei Datsenko495f5da2019-06-06 17:44:23 +1000177 // Launch the invoked program.
François Degros73168562019-10-03 13:34:26 +1000178 _exit(std::move(launcher).Run());
Sergei Datsenko495f5da2019-06-06 17:44:23 +1000179 NOTREACHED();
180 }
181
François Degros4a76d702019-09-26 14:54:35 +1000182 // In parent process
Sergei Datsenko495f5da2019-06-06 17:44:23 +1000183 return exec_child;
184}
185
François Degros02c5c712019-10-03 13:00:11 +1000186bool SandboxedInit::PollLauncherStatus(base::ScopedFD* ctrl_fd,
187 int* exit_code) {
Sergei Datsenko495f5da2019-06-06 17:44:23 +1000188 CHECK(ctrl_fd->is_valid());
189 ssize_t read_bytes =
François Degros02c5c712019-10-03 13:00:11 +1000190 HANDLE_EINTR(read(ctrl_fd->get(), exit_code, sizeof(*exit_code)));
191 if (read_bytes != sizeof(*exit_code)) {
François Degros4a76d702019-09-26 14:54:35 +1000192 return false;
Sergei Datsenko495f5da2019-06-06 17:44:23 +1000193 }
François Degros4a76d702019-09-26 14:54:35 +1000194
195 ctrl_fd->reset();
196 return true;
Sergei Datsenko495f5da2019-06-06 17:44:23 +1000197}
198
Sergei Datsenko1c8f2152019-06-19 15:21:21 +1000199int SandboxedInit::WStatusToStatus(int wstatus) {
200 if (WIFEXITED(wstatus)) {
201 return WEXITSTATUS(wstatus);
Sergei Datsenko1c8f2152019-06-19 15:21:21 +1000202 }
François Degrosd5c741f2019-09-27 12:20:31 +1000203
204 if (WIFSIGNALED(wstatus)) {
205 // Mirrors behavior of minijail_wait().
206 const int signum = WTERMSIG(wstatus);
François Degrosa97ad492019-10-04 12:12:12 +1000207 return signum == SIGSYS ? MINIJAIL_ERR_JAIL
208 : MINIJAIL_ERR_SIG_BASE + signum;
François Degrosd5c741f2019-09-27 12:20:31 +1000209 }
210
Sergei Datsenko1c8f2152019-06-19 15:21:21 +1000211 return -1;
212}
213
Sergei Datsenko495f5da2019-06-06 17:44:23 +1000214} // namespace cros_disks