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