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