xref: /aosp_15_r20/external/sandboxed-api/sandboxed_api/client.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 <dlfcn.h>
16 #include <syscall.h>
17 #include <unistd.h>
18 
19 #include <algorithm>
20 #include <cstdint>
21 #include <cstdlib>
22 #include <cstring>
23 #include <iterator>
24 #include <list>
25 #include <string>
26 #include <type_traits>
27 #include <utility>
28 #include <vector>
29 
30 #include "absl/base/attributes.h"
31 #include "absl/base/dynamic_annotations.h"
32 #include "absl/flags/parse.h"
33 #include "absl/log/check.h"
34 #include "absl/log/initialize.h"
35 #include "absl/log/log.h"
36 #include "absl/status/statusor.h"
37 #include "absl/strings/str_cat.h"
38 #include "google/protobuf/descriptor.h"
39 #include "google/protobuf/message.h"
40 #include "sandboxed_api/call.h"
41 #include "sandboxed_api/lenval_core.h"
42 #include "sandboxed_api/proto_arg.pb.h"
43 #include "sandboxed_api/proto_helper.h"
44 #include "sandboxed_api/sandbox2/comms.h"
45 #include "sandboxed_api/sandbox2/forkingclient.h"
46 #include "sandboxed_api/sandbox2/logsink.h"
47 #include "sandboxed_api/util/raw_logging.h"
48 #include "sandboxed_api/var_type.h"
49 
50 #include <ffi.h>
51 
52 namespace sapi {
53 namespace {
54 
55 // Guess the FFI type on the basis of data size and float/non-float/bool.
GetFFIType(size_t size,v::Type type)56 ffi_type* GetFFIType(size_t size, v::Type type) {
57   switch (type) {
58     case v::Type::kVoid:
59       return &ffi_type_void;
60     case v::Type::kPointer:
61       return &ffi_type_pointer;
62     case v::Type::kFd:
63       return &ffi_type_sint;
64     case v::Type::kFloat:
65       if (size == sizeof(float)) {
66         return &ffi_type_float;
67       }
68       if (size == sizeof(double)) {
69         return &ffi_type_double;
70       }
71       if (size == sizeof(long double)) {
72         return &ffi_type_longdouble;
73       }
74       LOG(FATAL) << "Unsupported floating-point size: " << size;
75     case v::Type::kInt:
76       switch (size) {
77         case 1:
78           return &ffi_type_uint8;
79         case 2:
80           return &ffi_type_uint16;
81         case 4:
82           return &ffi_type_uint32;
83         case 8:
84           return &ffi_type_uint64;
85         default:
86           LOG(FATAL) << "Unsupported integral size: " << size;
87       }
88     case v::Type::kStruct:
89       LOG(FATAL) << "Structs are not supported as function arguments";
90     case v::Type::kProto:
91       LOG(FATAL) << "Protos are not supported as function arguments";
92     default:
93       LOG(FATAL) << "Unknown type: " << type << " of size: " << size;
94   }
95 }
96 
97 // Provides an interface to prepare the arguments for a function call.
98 // In case of protobuf arguments, the class allocates and manages
99 // memory for the deserialized protobuf.
100 class FunctionCallPreparer {
101  public:
FunctionCallPreparer(const FuncCall & call)102   explicit FunctionCallPreparer(const FuncCall& call) {
103     CHECK(call.argc <= FuncCall::kArgsMax)
104         << "Number of arguments of a sandbox call exceeds limits.";
105     for (int i = 0; i < call.argc; ++i) {
106       arg_types_[i] = GetFFIType(call.arg_size[i], call.arg_type[i]);
107     }
108     ret_type_ = GetFFIType(call.ret_size, call.ret_type);
109     for (int i = 0; i < call.argc; ++i) {
110       if (call.arg_type[i] == v::Type::kPointer &&
111           call.aux_type[i] == v::Type::kProto) {
112         // Deserialize protobuf stored in the LenValueStruct and keep a
113         // reference to both. This way we are able to update the content of the
114         // LenValueStruct (when the sandboxee modifies the protobuf).
115         // This will also make sure that the protobuf is freed afterwards.
116         arg_values_[i] = GetDeserializedProto(
117             reinterpret_cast<LenValStruct*>(call.args[i].arg_int));
118       } else if (call.arg_type[i] == v::Type::kFloat) {
119         arg_values_[i] = reinterpret_cast<const void*>(&call.args[i].arg_float);
120       } else {
121         arg_values_[i] = reinterpret_cast<const void*>(&call.args[i].arg_int);
122       }
123     }
124   }
125 
~FunctionCallPreparer()126   ~FunctionCallPreparer() {
127     for (const auto& idx_proto : protos_to_be_destroyed_) {
128       const auto proto = idx_proto.second;
129       LenValStruct* lvs = idx_proto.first;
130       // There is no way to figure out whether the protobuf structure has
131       // changed or not, so we always serialize the protobuf again and replace
132       // the LenValStruct content.
133       std::vector<uint8_t> serialized = SerializeProto(*proto).value();
134       // Reallocate the LV memory to match its length.
135       if (lvs->size != serialized.size()) {
136         void* newdata = realloc(lvs->data, serialized.size());
137         if (!newdata) {
138           LOG(FATAL) << "Failed to reallocate protobuf buffer (size="
139                      << serialized.size() << ")";
140         }
141         lvs->size = serialized.size();
142         lvs->data = newdata;
143       }
144       memcpy(lvs->data, serialized.data(), serialized.size());
145 
146       delete proto;
147     }
148   }
149 
ret_type() const150   ffi_type* ret_type() const { return ret_type_; }
arg_types() const151   ffi_type** arg_types() const { return const_cast<ffi_type**>(arg_types_); }
arg_values() const152   void** arg_values() const { return const_cast<void**>(arg_values_); }
153 
154  private:
155   // Deserializes the protobuf argument.
GetDeserializedProto(LenValStruct * src)156   google::protobuf::MessageLite** GetDeserializedProto(LenValStruct* src) {
157     ProtoArg proto_arg;
158     if (!proto_arg.ParseFromArray(src->data, src->size)) {
159       LOG(FATAL) << "Unable to parse ProtoArg.";
160     }
161     const google::protobuf::Descriptor* desc =
162         google::protobuf::DescriptorPool::generated_pool()->FindMessageTypeByName(
163             proto_arg.full_name());
164     LOG_IF(FATAL, desc == nullptr) << "Unable to find the descriptor for '"
165                                    << proto_arg.full_name() << "'" << desc;
166     google::protobuf::MessageLite* deserialized_proto =
167         google::protobuf::MessageFactory::generated_factory()->GetPrototype(desc)->New();
168     LOG_IF(FATAL, deserialized_proto == nullptr)
169         << "Unable to create deserialized proto for " << proto_arg.full_name();
170     if (!deserialized_proto->ParseFromString(proto_arg.protobuf_data())) {
171       LOG(FATAL) << "Unable to deserialized proto for "
172                  << proto_arg.full_name();
173     }
174     protos_to_be_destroyed_.push_back({src, deserialized_proto});
175     return &protos_to_be_destroyed_.back().second;
176   }
177 
178   // Use list instead of vector to preserve references even with modifications.
179   // Contains pairs of lenval message pointer -> deserialized message
180   // so that we can serialize the argument again after the function call.
181   std::list<std::pair<LenValStruct*, google::protobuf::MessageLite*>>
182       protos_to_be_destroyed_;
183   ffi_type* ret_type_;
184   ffi_type* arg_types_[FuncCall::kArgsMax];
185   const void* arg_values_[FuncCall::kArgsMax];
186 };
187 
188 }  // namespace
189 
190 namespace client {
191 
192 // Error codes in the client code:
193 enum class Error : uintptr_t {
194   kUnset = 0,
195   kDlOpen,
196   kDlSym,
197   kCall,
198 };
199 
200 // Handles requests to make function calls.
HandleCallMsg(const FuncCall & call,FuncRet * ret)201 void HandleCallMsg(const FuncCall& call, FuncRet* ret) {
202   VLOG(1) << "HandleMsgCall, func: '" << call.func
203           << "', # of args: " << call.argc;
204 
205   ret->ret_type = call.ret_type;
206 
207   void* handle = dlopen(nullptr, RTLD_NOW);
208   if (handle == nullptr) {
209     LOG(ERROR) << "dlopen(nullptr, RTLD_NOW)";
210     ret->success = false;
211     ret->int_val = static_cast<uintptr_t>(Error::kDlOpen);
212     return;
213   }
214 
215   auto f = dlsym(handle, call.func);
216   if (f == nullptr) {
217     LOG(ERROR) << "Function '" << call.func << "' not found";
218     ret->success = false;
219     ret->int_val = static_cast<uintptr_t>(Error::kDlSym);
220     return;
221   }
222   FunctionCallPreparer arg_prep(call);
223   ffi_cif cif;
224   if (ffi_prep_cif(&cif, FFI_DEFAULT_ABI, call.argc, arg_prep.ret_type(),
225                    arg_prep.arg_types()) != FFI_OK) {
226     ret->success = false;
227     ret->int_val = static_cast<uintptr_t>(Error::kCall);
228     return;
229   }
230 
231   if (ret->ret_type == v::Type::kFloat) {
232     ffi_call(&cif, FFI_FN(f), &ret->float_val, arg_prep.arg_values());
233   } else {
234     ffi_call(&cif, FFI_FN(f), &ret->int_val, arg_prep.arg_values());
235   }
236 
237   ret->success = true;
238 }
239 
240 // Handles requests to allocate memory inside the sandboxee.
HandleAllocMsg(const size_t size,FuncRet * ret)241 void HandleAllocMsg(const size_t size, FuncRet* ret) {
242   VLOG(1) << "HandleAllocMsg: size=" << size;
243 
244   const void* allocated = malloc(size);
245   // Memory is copied to the pointer using an API that the memory sanitizer
246   // is blind to (process_vm_writev). Mark the memory as initialized here, so
247   // that the sandboxed code can still be tested using MSAN.
248   ABSL_ANNOTATE_MEMORY_IS_INITIALIZED(allocated, size);
249 
250   ret->ret_type = v::Type::kPointer;
251   ret->int_val = reinterpret_cast<uintptr_t>(allocated);
252   ret->success = true;
253 }
254 
255 // Like HandleAllocMsg(), but handles requests to reallocate memory.
HandleReallocMsg(uintptr_t ptr,size_t size,FuncRet * ret)256 void HandleReallocMsg(uintptr_t ptr, size_t size, FuncRet* ret) {
257   VLOG(1) << "HandleReallocMsg(" << absl::StrCat(absl::Hex(ptr)) << ", " << size
258           << ")";
259 
260   const void* reallocated = realloc(reinterpret_cast<void*>(ptr), size);
261   // Memory is copied to the pointer using an API that the memory sanitizer
262   // is blind to (process_vm_writev). Mark the memory as initialized here, so
263   // that the sandboxed code can still be tested using MSAN.
264   ABSL_ANNOTATE_MEMORY_IS_INITIALIZED(reallocated, size);
265 
266   ret->ret_type = v::Type::kPointer;
267   ret->int_val = reinterpret_cast<uintptr_t>(reallocated);
268   ret->success = true;
269 }
270 
271 // Handles requests to free memory previously allocated by HandleAllocMsg() and
272 // HandleReallocMsg().
HandleFreeMsg(uintptr_t ptr,FuncRet * ret)273 void HandleFreeMsg(uintptr_t ptr, FuncRet* ret) {
274   VLOG(1) << "HandleFreeMsg: free(0x" << absl::StrCat(absl::Hex(ptr)) << ")";
275 
276   free(reinterpret_cast<void*>(ptr));
277   ret->ret_type = v::Type::kVoid;
278   ret->success = true;
279   ret->int_val = 0ULL;
280 }
281 
282 // Handles requests to find a symbol value.
HandleSymbolMsg(const char * symname,FuncRet * ret)283 void HandleSymbolMsg(const char* symname, FuncRet* ret) {
284   ret->ret_type = v::Type::kPointer;
285 
286   void* handle = dlopen(nullptr, RTLD_NOW);
287   if (handle == nullptr) {
288     ret->success = false;
289     ret->int_val = static_cast<uintptr_t>(Error::kDlOpen);
290     return;
291   }
292 
293   ret->int_val = reinterpret_cast<uintptr_t>(dlsym(handle, symname));
294   ret->success = true;
295 }
296 
297 // Handles requests to receive a file descriptor from sandboxer.
HandleSendFd(sandbox2::Comms * comms,FuncRet * ret)298 void HandleSendFd(sandbox2::Comms* comms, FuncRet* ret) {
299   ret->ret_type = v::Type::kInt;
300   int fd = -1;
301 
302   if (comms->RecvFD(&fd) == false) {
303     ret->success = false;
304     return;
305   }
306 
307   ret->int_val = fd;
308   ret->success = true;
309 }
310 
311 // Handles requests to send a file descriptor back to sandboxer.
HandleRecvFd(sandbox2::Comms * comms,int fd_to_transfer,FuncRet * ret)312 void HandleRecvFd(sandbox2::Comms* comms, int fd_to_transfer, FuncRet* ret) {
313   ret->ret_type = v::Type::kVoid;
314 
315   if (comms->SendFD(fd_to_transfer) == false) {
316     ret->success = false;
317     return;
318   }
319 
320   ret->success = true;
321 }
322 
323 // Handles requests to close a file descriptor in the sandboxee.
HandleCloseFd(sandbox2::Comms * comms,int fd_to_close,FuncRet * ret)324 void HandleCloseFd(sandbox2::Comms* comms, int fd_to_close, FuncRet* ret) {
325   VLOG(1) << "HandleCloseFd: close(" << fd_to_close << ")";
326   close(fd_to_close);
327 
328   ret->ret_type = v::Type::kVoid;
329   ret->success = true;
330 }
331 
HandleStrlen(sandbox2::Comms * comms,const char * ptr,FuncRet * ret)332 void HandleStrlen(sandbox2::Comms* comms, const char* ptr, FuncRet* ret) {
333   ret->ret_type = v::Type::kInt;
334   ret->int_val = strlen(ptr);
335   ret->success = true;
336 }
337 
338 template <typename T>
BytesAs(const std::vector<uint8_t> & bytes)339 static T BytesAs(const std::vector<uint8_t>& bytes) {
340   static_assert(std::is_trivial<T>(),
341                 "only trivial types can be used with BytesAs");
342   CHECK_EQ(bytes.size(), sizeof(T));
343   T rv;
344   memcpy(&rv, bytes.data(), sizeof(T));
345   return rv;
346 }
347 
ServeRequest(sandbox2::Comms * comms)348 void ServeRequest(sandbox2::Comms* comms) {
349   uint32_t tag;
350   std::vector<uint8_t> bytes;
351 
352   CHECK(comms->RecvTLV(&tag, &bytes));
353 
354   FuncRet ret{};  // Brace-init zeroes struct padding
355 
356   switch (tag) {
357     case comms::kMsgCall:
358       VLOG(1) << "Client::kMsgCall";
359       HandleCallMsg(BytesAs<FuncCall>(bytes), &ret);
360       break;
361     case comms::kMsgAllocate:
362       VLOG(1) << "Client::kMsgAllocate";
363       HandleAllocMsg(BytesAs<size_t>(bytes), &ret);
364       break;
365     case comms::kMsgReallocate:
366       VLOG(1) << "Client::kMsgReallocate";
367       {
368         auto req = BytesAs<comms::ReallocRequest>(bytes);
369         HandleReallocMsg(req.old_addr, req.size, &ret);
370       }
371       break;
372     case comms::kMsgFree:
373       VLOG(1) << "Client::kMsgFree";
374       HandleFreeMsg(BytesAs<uintptr_t>(bytes), &ret);
375       break;
376     case comms::kMsgSymbol:
377       CHECK_EQ(bytes.size(),
378                1 + std::distance(bytes.begin(),
379                                  std::find(bytes.begin(), bytes.end(), '\0')));
380       VLOG(1) << "Received Client::kMsgSymbol message";
381       HandleSymbolMsg(reinterpret_cast<const char*>(bytes.data()), &ret);
382       break;
383     case comms::kMsgExit:
384       VLOG(1) << "Received Client::kMsgExit message";
385       syscall(__NR_exit_group, 0UL);
386       break;
387     case comms::kMsgSendFd:
388       VLOG(1) << "Received Client::kMsgSendFd message";
389       HandleSendFd(comms, &ret);
390       break;
391     case comms::kMsgRecvFd:
392       VLOG(1) << "Received Client::kMsgRecvFd message";
393       HandleRecvFd(comms, BytesAs<int>(bytes), &ret);
394       break;
395     case comms::kMsgClose:
396       VLOG(1) << "Received Client::kMsgClose message";
397       HandleCloseFd(comms, BytesAs<int>(bytes), &ret);
398       break;
399     case comms::kMsgStrlen:
400       VLOG(1) << "Received Client::kMsgStrlen message";
401       HandleStrlen(comms, BytesAs<const char*>(bytes), &ret);
402       break;
403       break;
404     default:
405       LOG(FATAL) << "Received unknown tag: " << tag;
406       break;  // Not reached
407   }
408 
409   if (ret.ret_type == v::Type::kFloat) {
410     // Make MSAN happy with long double.
411     ABSL_ANNOTATE_MEMORY_IS_INITIALIZED(&ret.float_val, sizeof(ret.float_val));
412     VLOG(1) << "Returned value: " << ret.float_val
413             << ", Success: " << (ret.success ? "Yes" : "No");
414   } else {
415     VLOG(1) << "Returned value: " << ret.int_val << " (0x"
416             << absl::StrCat(absl::Hex(ret.int_val))
417             << "), Success: " << (ret.success ? "Yes" : "No");
418   }
419 
420   CHECK(comms->SendTLV(comms::kMsgReturn, sizeof(ret),
421                        reinterpret_cast<uint8_t*>(&ret)));
422 }
423 
424 }  // namespace client
425 }  // namespace sapi
426 
main(int argc,char * argv[])427 ABSL_ATTRIBUTE_WEAK int main(int argc, char* argv[]) {
428   absl::ParseCommandLine(argc, argv);
429   absl::InitializeLog();
430 
431   // Note regarding the FD usage here: Parent and child seem to make use of the
432   // same FD, although this is not true. During process setup `dup2()` will be
433   // called to replace the FD `kSandbox2ClientCommsFD`.
434   // We do not use a new comms object here as the destructor would close our FD.
435   sandbox2::Comms comms(sandbox2::Comms::kDefaultConnection);
436   sandbox2::ForkingClient s2client(&comms);
437 
438   // Forkserver loop.
439   while (true) {
440     pid_t pid = s2client.WaitAndFork();
441     if (pid == -1) {
442       LOG(FATAL) << "Could not spawn a new sandboxee";
443     }
444     if (pid == 0) {
445       break;
446     }
447   }
448 
449   // Child thread.
450   s2client.SandboxMeHere();
451 
452   // Enable log forwarding if enabled by the sandboxer.
453   if (s2client.HasMappedFD(sandbox2::LogSink::kLogFDName)) {
454     s2client.SendLogsToSupervisor();
455   }
456 
457   // Run SAPI stub.
458   while (true) {
459     sapi::client::ServeRequest(&comms);
460   }
461   LOG(FATAL) << "Unreachable";
462 }
463