xref: /aosp_15_r20/external/sandboxed-api/sandboxed_api/sandbox.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 #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