xref: /aosp_15_r20/external/sandboxed-api/sandboxed_api/sandbox2/global_forkclient.cc (revision ec63e07ab9515d95e79c211197c445ef84cefa6a)
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