• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 separate 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   }
162   if (exec_fd < 0) {
163     // Extract the fd when it's owned by EmbedFile
164     exec_fd = sapi::EmbedFile::instance()->GetDupFdForFileToc(
165         forkserver_bin_embed_create());
166   }
167   if (exec_fd < 0) {
168     return absl::InternalError("Getting FD for init binary failed");
169   }
170   file_util::fileops::FDCloser exec_fd_closer(exec_fd);
171 
172   int sv[2];
173   if (socketpair(AF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0, sv) == -1) {
174     return absl::ErrnoToStatus(errno, "Creating socket pair failed");
175   }
176 
177   // Fork the fork-server, and clean-up the resources (close remote sockets).
178   const size_t stack_size = PTHREAD_STACK_MIN;
179   int clone_flags = CLONE_VM | CLONE_VFORK | SIGCHLD;
180   // CLONE_VM does not play well with TSan.
181   if constexpr (sapi::sanitizers::IsTSan()) {
182     clone_flags &= ~CLONE_VM & ~CLONE_VFORK;
183   }
184   char* stack =
185       static_cast<char*>(mmap(NULL, stack_size, PROT_READ | PROT_WRITE,
186                               MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0));
187   if (stack == MAP_FAILED) {
188     return absl::ErrnoToStatus(errno, "Allocating stack failed");
189   }
190   absl::Cleanup stack_dealloc = [stack, stack_size] {
191     munmap(stack, stack_size);
192   };
193   ForkserverArgs args = {
194       .exec_fd = exec_fd,
195       .comms_fd = sv[0],
196   };
197   pid_t pid = clone(LaunchForkserver, &stack[stack_size], clone_flags, &args,
198                     nullptr, nullptr, nullptr);
199   if (pid == -1) {
200     return absl::ErrnoToStatus(errno, "Forking forkserver process failed");
201   }
202 
203   close(sv[0]);
204   return std::make_unique<GlobalForkClient>(sv[1], pid);
205 }
206 
WaitForForkserver(pid_t pid)207 void WaitForForkserver(pid_t pid) {
208   int status;
209   pid_t wpid = TEMP_FAILURE_RETRY(waitpid(pid, &status, 0));
210   if (wpid != pid) {
211     SAPI_RAW_PLOG(ERROR, "Waiting for %d failed", pid);
212   }
213   if (WIFEXITED(status)) {
214     int exit_code = WEXITSTATUS(status);
215     if (exit_code == 0) {
216       SAPI_RAW_LOG(INFO, "forkserver (pid=%d) terminated normally", pid);
217     } else {
218       SAPI_RAW_LOG(WARNING, "forkserver (pid=%d) terminated with exit code %d",
219                    pid, exit_code);
220     }
221   } else if (WIFSIGNALED(status)) {
222     SAPI_RAW_LOG(WARNING, "forkserver (pid=%d) terminated by signal %d", pid,
223                  WTERMSIG(status));
224   }
225 }
226 
227 }  // namespace
228 
229 absl::Mutex GlobalForkClient::instance_mutex_(absl::kConstInit);
230 GlobalForkClient* GlobalForkClient::instance_ = nullptr;
231 
EnsureStarted(GlobalForkserverStartMode mode)232 void GlobalForkClient::EnsureStarted(GlobalForkserverStartMode mode) {
233   absl::MutexLock lock(&instance_mutex_);
234   EnsureStartedLocked(mode);
235 }
236 
EnsureStartedLocked(GlobalForkserverStartMode mode)237 void GlobalForkClient::EnsureStartedLocked(GlobalForkserverStartMode mode) {
238   if (instance_) {
239     return;
240   }
241   if (getenv(kForkServerDisableEnv)) {
242     SAPI_RAW_LOG(ERROR,
243                  "Start of the Global Fork-Server prevented by the %s "
244                  "environment variable present",
245                  kForkServerDisableEnv);
246     return;
247   }
248   if (!GetForkserverStartMode().contains(mode)) {
249     SAPI_RAW_LOG(
250         ERROR, "Start of the Global Fork-Server prevented by commandline flag");
251     return;
252   }
253   absl::StatusOr<std::unique_ptr<GlobalForkClient>> forkserver =
254       StartGlobalForkServer();
255   if (!forkserver.ok()) {
256     SAPI_RAW_LOG(ERROR, "Starting forkserver failed: %s",
257                  forkserver.status().message().data());
258     return;
259   }
260   instance_ = forkserver->release();
261 }
262 
ForceStart()263 void GlobalForkClient::ForceStart() {
264   absl::MutexLock lock(&GlobalForkClient::instance_mutex_);
265   SAPI_RAW_CHECK(instance_ == nullptr,
266                  "A force start requested when the Global Fork-Server was "
267                  "already running");
268   absl::StatusOr<std::unique_ptr<GlobalForkClient>> forkserver =
269       StartGlobalForkServer();
270   SAPI_RAW_CHECK(forkserver.ok(), forkserver.status().ToString().c_str());
271   instance_ = forkserver->release();
272 }
273 
Shutdown()274 void GlobalForkClient::Shutdown() {
275   pid_t pid = -1;
276   {
277     absl::MutexLock lock(&GlobalForkClient::instance_mutex_);
278     if (instance_) {
279       pid = instance_->fork_client_.pid();
280     }
281     delete instance_;
282     instance_ = nullptr;
283   }
284   if (pid != -1) {
285     WaitForForkserver(pid);
286   }
287 }
288 
SendRequest(const ForkRequest & request,int exec_fd,int comms_fd)289 SandboxeeProcess GlobalForkClient::SendRequest(const ForkRequest& request,
290                                                int exec_fd, int comms_fd) {
291   absl::ReleasableMutexLock lock(&GlobalForkClient::instance_mutex_);
292   EnsureStartedLocked(GlobalForkserverStartMode::kOnDemand);
293   if (!instance_) {
294     return SandboxeeProcess();
295   }
296   SandboxeeProcess process =
297       instance_->fork_client_.SendRequest(request, exec_fd, comms_fd);
298   if (instance_->comms_.IsTerminated()) {
299     LOG(ERROR) << "Global forkserver connection terminated";
300     pid_t server_pid = instance_->fork_client_.pid();
301     delete instance_;
302     instance_ = nullptr;
303     // Don't wait for process exit while still holding the lock and potentially
304     // blocking other threads.
305     lock.Release();
306     WaitForForkserver(server_pid);
307   }
308   return process;
309 }
310 
GetPid()311 pid_t GlobalForkClient::GetPid() {
312   absl::MutexLock lock(&instance_mutex_);
313   EnsureStartedLocked(GlobalForkserverStartMode::kOnDemand);
314   if (!instance_) {
315     return -1;
316   }
317   return instance_->fork_client_.pid();
318 }
319 
IsStarted()320 bool GlobalForkClient::IsStarted() {
321   absl::ReaderMutexLock lock(&instance_mutex_);
322   return instance_ != nullptr;
323 }
324 }  // namespace sandbox2
325