1 // Copyright 2019 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 // Implementation of the sandbox2::ForkServer class.
16
17 #include "sandboxed_api/sandbox2/global_forkclient.h"
18
19 #include <fcntl.h>
20 #include <sched.h>
21 #include <sys/mman.h>
22 #include <sys/socket.h>
23 #include <sys/wait.h>
24 #include <unistd.h>
25
26 #include <cerrno>
27 #include <cstdlib>
28 #include <memory>
29 #include <string>
30 #include <vector>
31
32 #include "absl/base/const_init.h"
33 #include "absl/cleanup/cleanup.h"
34 #include "absl/flags/flag.h"
35 #include "absl/log/log.h"
36 #include "absl/status/status.h"
37 #include "absl/status/statusor.h"
38 #include "absl/strings/ascii.h"
39 #include "absl/strings/str_cat.h"
40 #include "absl/strings/str_join.h"
41 #include "absl/strings/str_split.h"
42 #include "absl/strings/string_view.h"
43 #include "absl/synchronization/mutex.h"
44 #include "sandboxed_api/config.h"
45 #include "sandboxed_api/embed_file.h"
46 #include "sandboxed_api/sandbox2/comms.h"
47 #include "sandboxed_api/sandbox2/fork_client.h"
48 #include "sandboxed_api/sandbox2/forkserver_bin_embed.h"
49 #include "sandboxed_api/sandbox2/util.h"
50 #include "sandboxed_api/util/fileops.h"
51 #include "sandboxed_api/util/raw_logging.h"
52
53 namespace sandbox2 {
54
55 namespace file_util = ::sapi::file_util;
56
57 namespace {
58
ToString(GlobalForkserverStartMode mode)59 std::string ToString(GlobalForkserverStartMode mode) {
60 switch (mode) {
61 case GlobalForkserverStartMode::kOnDemand:
62 return "ondemand";
63 default:
64 return "unknown";
65 }
66 }
67
68 } // namespace
69
AbslParseFlag(absl::string_view text,GlobalForkserverStartModeSet * out,std::string * error)70 bool AbslParseFlag(absl::string_view text, GlobalForkserverStartModeSet* out,
71 std::string* error) {
72 *out = {};
73 if (text == "never") {
74 return true;
75 }
76 for (absl::string_view mode : absl::StrSplit(text, ',')) {
77 mode = absl::StripAsciiWhitespace(mode);
78 if (mode == "ondemand") {
79 *out |= GlobalForkserverStartMode::kOnDemand;
80 } else {
81 *error = absl::StrCat("Invalid forkserver start mode: ", mode);
82 return false;
83 }
84 }
85 return true;
86 }
87
AbslUnparseFlag(GlobalForkserverStartModeSet in)88 std::string AbslUnparseFlag(GlobalForkserverStartModeSet in) {
89 std::vector<std::string> str_modes;
90 for (size_t i = 0; i < GlobalForkserverStartModeSet::kSize; ++i) {
91 auto mode = static_cast<GlobalForkserverStartMode>(i);
92 if (in.contains(mode)) {
93 str_modes.push_back(ToString(mode));
94 }
95 }
96 if (str_modes.empty()) {
97 return "never";
98 }
99 return absl::StrJoin(str_modes, ",");
100 }
101
102 } // namespace sandbox2
103
104 ABSL_FLAG(std::string, sandbox2_forkserver_binary_path, "",
105 "Path to forkserver_bin binary");
106 ABSL_FLAG(sandbox2::GlobalForkserverStartModeSet,
107 sandbox2_forkserver_start_mode,
108 sandbox2::GlobalForkserverStartModeSet(
109 sandbox2::GlobalForkserverStartMode::kOnDemand)
110 ,
111 "When Sandbox2 Forkserver process should be started");
112
113 namespace sandbox2 {
114
115 namespace {
116
GetForkserverStartMode()117 GlobalForkserverStartModeSet GetForkserverStartMode() {
118 return absl::GetFlag(FLAGS_sandbox2_forkserver_start_mode);
119 }
120
121 struct ForkserverArgs {
122 int exec_fd;
123 int comms_fd;
124 };
125
LaunchForkserver(void * vargs)126 int LaunchForkserver(void* vargs) {
127 auto* args = static_cast<ForkserverArgs*>(vargs);
128 // Move the comms FD to the proper, expected FD number.
129 // The new FD will not be CLOEXEC, which is what we want.
130 // If exec_fd == Comms::kSandbox2ClientCommsFD then it would be replaced by
131 // the comms fd and result in EACCESS at execveat.
132 // So first move exec_fd to another fd number.
133 if (args->exec_fd == Comms::kSandbox2ClientCommsFD) {
134 args->exec_fd = dup(args->exec_fd);
135 SAPI_RAW_PCHECK(args->exec_fd != -1, "duping exec fd failed");
136 fcntl(args->exec_fd, F_SETFD, FD_CLOEXEC);
137 }
138 SAPI_RAW_PCHECK(dup2(args->comms_fd, Comms::kSandbox2ClientCommsFD) != -1,
139 "duping comms fd failed");
140
141 char proc_name[] = "S2-FORK-SERV";
142 char* const argv[] = {proc_name, nullptr};
143 util::Execveat(args->exec_fd, "", argv, environ, AT_EMPTY_PATH);
144 SAPI_RAW_PLOG(FATAL, "Could not launch forkserver binary");
145 }
146
StartGlobalForkServer()147 absl::StatusOr<std::unique_ptr<GlobalForkClient>> StartGlobalForkServer() {
148 SAPI_RAW_LOG(INFO, "Starting global forkserver");
149
150 // Allow passing of a spearate forkserver_bin via flag
151 int exec_fd = -1;
152 std::string bin_path = absl::GetFlag(FLAGS_sandbox2_forkserver_binary_path);
153 if (!bin_path.empty()) {
154 exec_fd = open(bin_path.c_str(), O_RDONLY);
155 if (exec_fd < 0) {
156 return absl::ErrnoToStatus(
157 errno, absl::StrCat("Opening forkserver binary passed via "
158 "--sandbox2_forkserver_binary_path (",
159 bin_path, ")"));
160 }
161 } else if constexpr (sapi::host_os::IsAndroid()) {
162 return absl::FailedPreconditionError(
163 "sandbox2_forkserver_binary_path flag has to be set to the location of "
164 "the forkserver binary on Android");
165 }
166 if (exec_fd < 0) {
167 // Extract the fd when it's owned by EmbedFile
168 exec_fd = sapi::EmbedFile::instance()->GetDupFdForFileToc(
169 forkserver_bin_embed_create());
170 }
171 if (exec_fd < 0) {
172 return absl::InternalError("Getting FD for init binary failed");
173 }
174 file_util::fileops::FDCloser exec_fd_closer(exec_fd);
175
176 int sv[2];
177 if (socketpair(AF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0, sv) == -1) {
178 return absl::ErrnoToStatus(errno, "Creating socket pair failed");
179 }
180
181 // Fork the fork-server, and clean-up the resources (close remote sockets).
182 const size_t stack_size = PTHREAD_STACK_MIN;
183 int clone_flags = CLONE_VM | CLONE_VFORK | SIGCHLD;
184 // CLONE_VM does not play well with TSan.
185 if constexpr (sapi::sanitizers::IsTSan()) {
186 clone_flags &= ~CLONE_VM & ~CLONE_VFORK;
187 }
188 char* stack =
189 static_cast<char*>(mmap(NULL, stack_size, PROT_READ | PROT_WRITE,
190 MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0));
191 if (stack == MAP_FAILED) {
192 return absl::ErrnoToStatus(errno, "Allocating stack failed");
193 }
194 absl::Cleanup stack_dealloc = [stack, stack_size] {
195 munmap(stack, stack_size);
196 };
197 ForkserverArgs args = {
198 .exec_fd = exec_fd,
199 .comms_fd = sv[0],
200 };
201 pid_t pid = clone(LaunchForkserver, &stack[stack_size], clone_flags, &args,
202 nullptr, nullptr, nullptr);
203 if (pid == -1) {
204 return absl::ErrnoToStatus(errno, "Forking forkserver process failed");
205 }
206
207 close(sv[0]);
208 return std::make_unique<GlobalForkClient>(sv[1], pid);
209 }
210
WaitForForkserver(pid_t pid)211 void WaitForForkserver(pid_t pid) {
212 int status;
213 pid_t wpid = TEMP_FAILURE_RETRY(waitpid(pid, &status, 0));
214 if (wpid != pid) {
215 SAPI_RAW_PLOG(ERROR, "Waiting for %d failed", pid);
216 }
217 if (WIFEXITED(status)) {
218 int exit_code = WEXITSTATUS(status);
219 if (exit_code == 0) {
220 SAPI_RAW_LOG(INFO, "forkserver (pid=%d) terminated normally", pid);
221 } else {
222 SAPI_RAW_LOG(WARNING, "forkserver (pid=%d) terminated with exit code %d",
223 pid, exit_code);
224 }
225 } else if (WIFSIGNALED(status)) {
226 SAPI_RAW_LOG(WARNING, "forkserver (pid=%d) terminated by signal %d", pid,
227 WTERMSIG(status));
228 }
229 }
230
231 } // namespace
232
233 absl::Mutex GlobalForkClient::instance_mutex_(absl::kConstInit);
234 GlobalForkClient* GlobalForkClient::instance_ = nullptr;
235
EnsureStarted(GlobalForkserverStartMode mode)236 void GlobalForkClient::EnsureStarted(GlobalForkserverStartMode mode) {
237 absl::MutexLock lock(&instance_mutex_);
238 EnsureStartedLocked(mode);
239 }
240
EnsureStartedLocked(GlobalForkserverStartMode mode)241 void GlobalForkClient::EnsureStartedLocked(GlobalForkserverStartMode mode) {
242 if (instance_) {
243 return;
244 }
245 if (getenv(kForkServerDisableEnv)) {
246 SAPI_RAW_LOG(ERROR,
247 "Start of the Global Fork-Server prevented by the %s "
248 "environment variable present",
249 kForkServerDisableEnv);
250 return;
251 }
252 if (!GetForkserverStartMode().contains(mode)) {
253 SAPI_RAW_LOG(
254 ERROR, "Start of the Global Fork-Server prevented by commandline flag");
255 return;
256 }
257 absl::StatusOr<std::unique_ptr<GlobalForkClient>> forkserver =
258 StartGlobalForkServer();
259 if (!forkserver.ok()) {
260 SAPI_RAW_LOG(ERROR, "Starting forkserver failed: %s",
261 forkserver.status().message().data());
262 return;
263 }
264 instance_ = forkserver->release();
265 }
266
ForceStart()267 void GlobalForkClient::ForceStart() {
268 absl::MutexLock lock(&GlobalForkClient::instance_mutex_);
269 SAPI_RAW_CHECK(instance_ == nullptr,
270 "A force start requested when the Global Fork-Server was "
271 "already running");
272 absl::StatusOr<std::unique_ptr<GlobalForkClient>> forkserver =
273 StartGlobalForkServer();
274 SAPI_RAW_CHECK(forkserver.ok(), forkserver.status().ToString().c_str());
275 instance_ = forkserver->release();
276 }
277
Shutdown()278 void GlobalForkClient::Shutdown() {
279 pid_t pid = -1;
280 {
281 absl::MutexLock lock(&GlobalForkClient::instance_mutex_);
282 if (instance_) {
283 pid = instance_->fork_client_.pid();
284 }
285 delete instance_;
286 instance_ = nullptr;
287 }
288 if (pid != -1) {
289 WaitForForkserver(pid);
290 }
291 }
292
SendRequest(const ForkRequest & request,int exec_fd,int comms_fd)293 SandboxeeProcess GlobalForkClient::SendRequest(const ForkRequest& request,
294 int exec_fd, int comms_fd) {
295 absl::ReleasableMutexLock lock(&GlobalForkClient::instance_mutex_);
296 EnsureStartedLocked(GlobalForkserverStartMode::kOnDemand);
297 if (!instance_) {
298 return SandboxeeProcess();
299 }
300 SandboxeeProcess process =
301 instance_->fork_client_.SendRequest(request, exec_fd, comms_fd);
302 if (instance_->comms_.IsTerminated()) {
303 LOG(ERROR) << "Global forkserver connection terminated";
304 pid_t server_pid = instance_->fork_client_.pid();
305 delete instance_;
306 instance_ = nullptr;
307 // Don't wait for process exit while still holding the lock and potentially
308 // blocking other threads.
309 lock.Release();
310 WaitForForkserver(server_pid);
311 }
312 return process;
313 }
314
GetPid()315 pid_t GlobalForkClient::GetPid() {
316 absl::MutexLock lock(&instance_mutex_);
317 EnsureStartedLocked(GlobalForkserverStartMode::kOnDemand);
318 if (!instance_) {
319 return -1;
320 }
321 return instance_->fork_client_.pid();
322 }
323
IsStarted()324 bool GlobalForkClient::IsStarted() {
325 absl::ReaderMutexLock lock(&instance_mutex_);
326 return instance_ != nullptr;
327 }
328 } // namespace sandbox2
329