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