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 #include "sandboxed_api/sandbox.h"
16
17 #include <sys/resource.h>
18 #include <sys/types.h>
19 #include <sys/uio.h>
20 #include <syscall.h>
21
22 #include <cstdio>
23 #include <initializer_list>
24 #include <memory>
25 #include <string>
26 #include <utility>
27 #include <vector>
28
29 #include "absl/base/dynamic_annotations.h"
30 #include "absl/base/macros.h"
31 #include "absl/log/log.h"
32 #include "absl/status/status.h"
33 #include "absl/status/statusor.h"
34 #include "absl/strings/str_cat.h"
35 #include "absl/strings/str_format.h"
36 #include "absl/time/time.h"
37 #include "sandboxed_api/config.h"
38 #include "sandboxed_api/embed_file.h"
39 #include "sandboxed_api/rpcchannel.h"
40 #include "sandboxed_api/sandbox2/executor.h"
41 #include "sandboxed_api/sandbox2/policy.h"
42 #include "sandboxed_api/sandbox2/policybuilder.h"
43 #include "sandboxed_api/sandbox2/result.h"
44 #include "sandboxed_api/sandbox2/sandbox2.h"
45 #include "sandboxed_api/sandbox2/util/bpf_helper.h"
46 #include "sandboxed_api/util/fileops.h"
47 #include "sandboxed_api/util/path.h"
48 #include "sandboxed_api/util/raw_logging.h"
49 #include "sandboxed_api/util/runfiles.h"
50 #include "sandboxed_api/util/status_macros.h"
51
52 namespace sapi {
53
~Sandbox()54 Sandbox::~Sandbox() {
55 Terminate();
56 // The forkserver will die automatically when the executor goes out of scope
57 // and closes the comms object.
58 }
59
60 // A generic policy which should work with majority of typical libraries, which
61 // are single-threaded and require ~30 basic syscalls.
InitDefaultPolicyBuilder(sandbox2::PolicyBuilder * builder)62 void InitDefaultPolicyBuilder(sandbox2::PolicyBuilder* builder) {
63 (*builder)
64 .AllowRead()
65 .AllowWrite()
66 .AllowExit()
67 .AllowGetRlimit()
68 .AllowGetIDs()
69 .AllowTCGETS()
70 .AllowTime()
71 .AllowOpen()
72 .AllowStat()
73 .AllowHandleSignals()
74 .AllowSystemMalloc()
75 .AllowSafeFcntl()
76 .AllowGetPIDs()
77 .AllowSleep()
78 .AllowReadlink()
79 .AllowAccess()
80 .AllowSyscalls({
81 __NR_recvmsg,
82 __NR_sendmsg,
83 __NR_futex,
84 __NR_close,
85 __NR_lseek,
86 __NR_uname,
87 __NR_kill,
88 __NR_tgkill,
89 __NR_tkill,
90 });
91
92 #ifdef __NR_arch_prctl // x86-64 only
93 builder->AllowSyscall(__NR_arch_prctl);
94 #endif
95
96 if constexpr (sanitizers::IsAny()) {
97 LOG(WARNING) << "Allowing additional calls to support the LLVM "
98 << "(ASAN/MSAN/TSAN) sanitizer";
99 builder->AllowLlvmSanitizers();
100 }
101 builder->AddFile("/etc/localtime")
102 .AddTmpfs("/tmp", 1ULL << 30 /* 1GiB tmpfs (max size */);
103 }
104
Terminate(bool attempt_graceful_exit)105 void Sandbox::Terminate(bool attempt_graceful_exit) {
106 if (!is_active()) {
107 return;
108 }
109
110 absl::StatusOr<sandbox2::Result> result;
111 if (attempt_graceful_exit) {
112 if (absl::Status requested_exit = rpc_channel_->Exit();
113 !requested_exit.ok()) {
114 LOG(WARNING)
115 << "rpc_channel->Exit() failed, calling AwaitResultWithTimeout(1) "
116 << requested_exit;
117 }
118 result = s2_->AwaitResultWithTimeout(absl::Seconds(1));
119 if (!result.ok()) {
120 LOG(WARNING) << "s2_->AwaitResultWithTimeout failed, status: "
121 << result.status() << " Killing PID: " << pid();
122 }
123 }
124
125 if (!attempt_graceful_exit || !result.ok()) {
126 s2_->Kill();
127 result = s2_->AwaitResult();
128 }
129
130 if (result->final_status() == sandbox2::Result::OK &&
131 result->reason_code() == 0) {
132 VLOG(2) << "Sandbox2 finished with: " << result->ToString();
133 } else {
134 LOG(WARNING) << "Sandbox2 finished with: " << result->ToString();
135 }
136 }
137
PathToSAPILib(const std::string & lib_path)138 static std::string PathToSAPILib(const std::string& lib_path) {
139 return file::IsAbsolutePath(lib_path) ? lib_path
140 : GetDataDependencyFilePath(lib_path);
141 }
142
Init(bool use_unotify_monitor)143 absl::Status Sandbox::Init(bool use_unotify_monitor) {
144 // It's already initialized
145 if (is_active()) {
146 return absl::OkStatus();
147 }
148
149 // Initialize the forkserver if it is not already running.
150 if (!fork_client_) {
151 // If FileToc was specified, it will be used over any paths to the SAPI
152 // library.
153 std::string lib_path;
154 int embed_lib_fd = -1;
155 if (embed_lib_toc_ && !sapi::host_os::IsAndroid()) {
156 embed_lib_fd = EmbedFile::instance()->GetDupFdForFileToc(embed_lib_toc_);
157 if (embed_lib_fd == -1) {
158 PLOG(ERROR) << "Cannot create executable FD for TOC:'"
159 << embed_lib_toc_->name << "'";
160 return absl::UnavailableError("Could not create executable FD");
161 }
162 lib_path = embed_lib_toc_->name;
163 } else {
164 lib_path = PathToSAPILib(GetLibPath());
165 if (lib_path.empty()) {
166 LOG(ERROR) << "SAPI library path is empty";
167 return absl::FailedPreconditionError("No SAPI library path given");
168 }
169 }
170 std::vector<std::string> args = {lib_path};
171 // Additional arguments, if needed.
172 GetArgs(&args);
173 std::vector<std::string> envs{};
174 // Additional envvars, if needed.
175 GetEnvs(&envs);
176
177 forkserver_executor_ =
178 (embed_lib_fd >= 0)
179 ? std::make_unique<sandbox2::Executor>(embed_lib_fd, args, envs)
180 : std::make_unique<sandbox2::Executor>(lib_path, args, envs);
181
182 fork_client_ = forkserver_executor_->StartForkServer();
183
184 if (!fork_client_) {
185 LOG(ERROR) << "Could not start forkserver";
186 return absl::UnavailableError("Could not start the forkserver");
187 }
188 }
189
190 sandbox2::PolicyBuilder policy_builder;
191 InitDefaultPolicyBuilder(&policy_builder);
192 if (use_unotify_monitor) {
193 policy_builder.CollectStacktracesOnSignal(false);
194 }
195 auto s2p = ModifyPolicy(&policy_builder);
196
197 // Spawn new process from the forkserver.
198 auto executor = std::make_unique<sandbox2::Executor>(fork_client_.get());
199
200 executor
201 // The client.cc code is capable of enabling sandboxing on its own.
202 ->set_enable_sandbox_before_exec(false)
203 // By default, set cwd to "/", can be changed in ModifyExecutor().
204 .set_cwd("/")
205 .limits()
206 // Disable time limits.
207 ->set_walltime_limit(absl::ZeroDuration())
208 .set_rlimit_cpu(RLIM64_INFINITY);
209
210 // Modify the executor, e.g. by setting custom limits and IPC.
211 ModifyExecutor(executor.get());
212
213 s2_ = std::make_unique<sandbox2::Sandbox2>(std::move(executor),
214 std::move(s2p), CreateNotifier());
215 if (use_unotify_monitor) {
216 SAPI_RETURN_IF_ERROR(s2_->EnableUnotifyMonitor());
217 }
218 s2_awaited_ = false;
219 auto res = s2_->RunAsync();
220
221 comms_ = s2_->comms();
222 pid_ = s2_->pid();
223
224 rpc_channel_ = std::make_unique<RPCChannel>(comms_);
225
226 if (!res) {
227 Terminate();
228 return absl::UnavailableError("Could not start the sandbox");
229 }
230 return absl::OkStatus();
231 }
232
is_active() const233 bool Sandbox::is_active() const { return s2_ && !s2_->IsTerminated(); }
234
Allocate(v::Var * var,bool automatic_free)235 absl::Status Sandbox::Allocate(v::Var* var, bool automatic_free) {
236 if (!is_active()) {
237 return absl::UnavailableError("Sandbox not active");
238 }
239 return var->Allocate(rpc_channel(), automatic_free);
240 }
241
Free(v::Var * var)242 absl::Status Sandbox::Free(v::Var* var) {
243 if (!is_active()) {
244 return absl::UnavailableError("Sandbox not active");
245 }
246 return var->Free(rpc_channel());
247 }
248
SynchronizePtrBefore(v::Callable * ptr)249 absl::Status Sandbox::SynchronizePtrBefore(v::Callable* ptr) {
250 if (!is_active()) {
251 return absl::UnavailableError("Sandbox not active");
252 }
253 if (ptr->GetType() != v::Type::kPointer) {
254 return absl::OkStatus();
255 }
256 // Cast is safe, since type is v::Type::kPointer
257 auto* p = static_cast<v::Ptr*>(ptr);
258 // NOLINTNEXTLINE(clang-diagnostic-deprecated-declarations)
259 if (p->GetSyncType() == v::Pointable::kSyncNone) {
260 return absl::OkStatus();
261 }
262
263 if (p->GetPointedVar()->GetRemote() == nullptr) {
264 // Allocate the memory, and make it automatically free-able, upon this
265 // object's (p->GetPointedVar()) end of life-time.
266 SAPI_RETURN_IF_ERROR(Allocate(p->GetPointedVar(), /*automatic_free=*/true));
267 }
268
269 // Allocation occurs during both before/after synchronization modes. But the
270 // memory is transferred to the sandboxee only if v::Pointable::kSyncBefore
271 // was requested.
272 // NOLINTNEXTLINE(clang-diagnostic-deprecated-declarations)
273 if ((p->GetSyncType() & v::Pointable::kSyncBefore) == 0) {
274 return absl::OkStatus();
275 }
276
277 VLOG(3) << "Synchronization (TO), ptr " << p << ", Type: " << p->GetSyncType()
278 << " for var: " << p->GetPointedVar()->ToString();
279
280 return p->GetPointedVar()->TransferToSandboxee(rpc_channel(), pid());
281 }
282
SynchronizePtrAfter(v::Callable * ptr) const283 absl::Status Sandbox::SynchronizePtrAfter(v::Callable* ptr) const {
284 if (!is_active()) {
285 return absl::UnavailableError("Sandbox not active");
286 }
287 if (ptr->GetType() != v::Type::kPointer) {
288 return absl::OkStatus();
289 }
290 v::Ptr* p = reinterpret_cast<v::Ptr*>(ptr);
291 // NOLINTNEXTLINE(clang-diagnostic-deprecated-declarations)
292 if ((p->GetSyncType() & v::Pointable::kSyncAfter) == 0) {
293 return absl::OkStatus();
294 }
295
296 VLOG(3) << "Synchronization (FROM), ptr " << p
297 << ", Type: " << p->GetSyncType()
298 << " for var: " << p->GetPointedVar()->ToString();
299
300 if (p->GetPointedVar()->GetRemote() == nullptr) {
301 LOG(ERROR) << "Trying to synchronize a variable which is not allocated in "
302 << "the sandboxee p=" << p->ToString();
303 return absl::FailedPreconditionError(absl::StrCat(
304 "Trying to synchronize a variable which is not allocated in the "
305 "sandboxee p=",
306 p->ToString()));
307 }
308
309 return p->GetPointedVar()->TransferFromSandboxee(rpc_channel(), pid());
310 }
311
Call(const std::string & func,v::Callable * ret,std::initializer_list<v::Callable * > args)312 absl::Status Sandbox::Call(const std::string& func, v::Callable* ret,
313 std::initializer_list<v::Callable*> args) {
314 if (!is_active()) {
315 return absl::UnavailableError("Sandbox not active");
316 }
317 // Send data.
318 FuncCall rfcall{};
319 rfcall.argc = args.size();
320 absl::SNPrintF(rfcall.func, ABSL_ARRAYSIZE(rfcall.func), "%s", func);
321
322 VLOG(1) << "CALL ENTRY: '" << func << "' with " << args.size()
323 << " argument(s)";
324
325 // Copy all arguments into rfcall.
326 int i = 0;
327 for (auto* arg : args) {
328 if (arg == nullptr) {
329 rfcall.arg_type[i] = v::Type::kPointer;
330 rfcall.arg_size[i] = sizeof(void*);
331 rfcall.args[i].arg_int = 0;
332 VLOG(1) << "CALL ARG: (" << i << "): nullptr";
333 ++i;
334 continue;
335 }
336 rfcall.arg_size[i] = arg->GetSize();
337 rfcall.arg_type[i] = arg->GetType();
338
339 // For pointers, set the auxiliary type and size.
340 if (rfcall.arg_type[i] == v::Type::kPointer) {
341 // Cast is safe, since type is v::Type::kPointer
342 auto* p = static_cast<v::Ptr*>(arg);
343 rfcall.aux_type[i] = p->GetPointedVar()->GetType();
344 rfcall.aux_size[i] = p->GetPointedVar()->GetSize();
345 }
346
347 // Synchronize all pointers before the call if it's needed.
348 SAPI_RETURN_IF_ERROR(SynchronizePtrBefore(arg));
349
350 if (arg->GetType() == v::Type::kFloat) {
351 arg->GetDataFromPtr(&rfcall.args[i].arg_float,
352 sizeof(rfcall.args[0].arg_float));
353 // Make MSAN happy with long double.
354 ABSL_ANNOTATE_MEMORY_IS_INITIALIZED(&rfcall.args[i].arg_float,
355 sizeof(rfcall.args[0].arg_float));
356 } else {
357 arg->GetDataFromPtr(&rfcall.args[i].arg_int,
358 sizeof(rfcall.args[0].arg_int));
359 }
360
361 if (rfcall.arg_type[i] == v::Type::kFd) {
362 // Cast is safe, since type is v::Type::kFd
363 auto* fd = static_cast<v::Fd*>(arg);
364 if (fd->GetRemoteFd() < 0) {
365 SAPI_RETURN_IF_ERROR(TransferToSandboxee(fd));
366 }
367 rfcall.args[i].arg_int = fd->GetRemoteFd();
368 }
369 VLOG(1) << "CALL ARG: (" << i << "), Type: " << arg->GetTypeString()
370 << ", Size: " << arg->GetSize() << ", Val: " << arg->ToString();
371 ++i;
372 }
373 rfcall.ret_type = ret->GetType();
374 rfcall.ret_size = ret->GetSize();
375
376 // Call & receive data.
377 FuncRet fret;
378 SAPI_RETURN_IF_ERROR(
379 rpc_channel()->Call(rfcall, comms::kMsgCall, &fret, rfcall.ret_type));
380
381 if (fret.ret_type == v::Type::kFloat) {
382 ret->SetDataFromPtr(&fret.float_val, sizeof(fret.float_val));
383 } else {
384 ret->SetDataFromPtr(&fret.int_val, sizeof(fret.int_val));
385 }
386
387 if (fret.ret_type == v::Type::kFd) {
388 SAPI_RETURN_IF_ERROR(TransferFromSandboxee(reinterpret_cast<v::Fd*>(ret)));
389 }
390
391 // Synchronize all pointers after the call if it's needed.
392 for (auto* arg : args) {
393 if (arg != nullptr) {
394 SAPI_RETURN_IF_ERROR(SynchronizePtrAfter(arg));
395 }
396 }
397
398 VLOG(1) << "CALL EXIT: Type: " << ret->GetTypeString()
399 << ", Size: " << ret->GetSize() << ", Val: " << ret->ToString();
400
401 return absl::OkStatus();
402 }
403
Symbol(const char * symname,void ** addr)404 absl::Status Sandbox::Symbol(const char* symname, void** addr) {
405 if (!is_active()) {
406 return absl::UnavailableError("Sandbox not active");
407 }
408 return rpc_channel_->Symbol(symname, addr);
409 }
410
TransferToSandboxee(v::Var * var)411 absl::Status Sandbox::TransferToSandboxee(v::Var* var) {
412 if (!is_active()) {
413 return absl::UnavailableError("Sandbox not active");
414 }
415 return var->TransferToSandboxee(rpc_channel(), pid());
416 }
417
TransferFromSandboxee(v::Var * var)418 absl::Status Sandbox::TransferFromSandboxee(v::Var* var) {
419 if (!is_active()) {
420 return absl::UnavailableError("Sandbox not active");
421 }
422 return var->TransferFromSandboxee(rpc_channel(), pid());
423 }
424
GetCString(const v::RemotePtr & str,size_t max_length)425 absl::StatusOr<std::string> Sandbox::GetCString(const v::RemotePtr& str,
426 size_t max_length) {
427 if (!is_active()) {
428 return absl::UnavailableError("Sandbox not active");
429 }
430
431 SAPI_ASSIGN_OR_RETURN(auto len, rpc_channel()->Strlen(str.GetValue()));
432 if (len > max_length) {
433 return absl::InvalidArgumentError(
434 absl::StrCat("Target string too large: ", len, " > ", max_length));
435 }
436 std::string buffer(len, '\0');
437 struct iovec local = {
438 .iov_base = &buffer[0],
439 .iov_len = len,
440 };
441 struct iovec remote = {
442 .iov_base = str.GetValue(),
443 .iov_len = len,
444 };
445
446 ssize_t ret = process_vm_readv(pid_, &local, 1, &remote, 1, 0);
447 if (ret == -1) {
448 PLOG(WARNING) << "reading c-string failed: process_vm_readv(pid: " << pid_
449 << " raddr: " << str.GetValue() << " size: " << len << ")";
450 return absl::UnavailableError("process_vm_readv failed");
451 }
452 if (ret != len) {
453 LOG(WARNING) << "partial read when reading c-string: process_vm_readv(pid: "
454 << pid_ << " raddr: " << str.GetValue() << " size: " << len
455 << ") transferred " << ret << " bytes";
456 return absl::UnavailableError("process_vm_readv succeeded partially");
457 }
458
459 return buffer;
460 }
461
AwaitResult()462 const sandbox2::Result& Sandbox::AwaitResult() {
463 if (s2_ && !s2_awaited_) {
464 result_ = s2_->AwaitResult();
465 s2_awaited_ = true;
466 }
467 return result_;
468 }
469
SetWallTimeLimit(absl::Duration limit) const470 absl::Status Sandbox::SetWallTimeLimit(absl::Duration limit) const {
471 if (!is_active()) {
472 return absl::UnavailableError("Sandbox not active");
473 }
474 s2_->set_walltime_limit(limit);
475 return absl::OkStatus();
476 }
477
Exit() const478 void Sandbox::Exit() const {
479 if (!is_active()) {
480 return;
481 }
482 s2_->set_walltime_limit(absl::Seconds(1));
483 if (!rpc_channel_->Exit().ok()) {
484 LOG(WARNING) << "rpc_channel->Exit() failed, killing PID: " << pid();
485 s2_->Kill();
486 }
487 }
488
ModifyPolicy(sandbox2::PolicyBuilder * builder)489 std::unique_ptr<sandbox2::Policy> Sandbox::ModifyPolicy(
490 sandbox2::PolicyBuilder* builder) {
491 return builder->BuildOrDie();
492 }
493
494 } // namespace sapi
495