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 // Implementation of sandbox2::Comms class.
16 //
17 // Warning: This class is not multi-thread safe (for callers). It uses a single
18 // communications channel (an AF_UNIX socket), so it requires exactly one sender
19 // and one receiver. If you plan to use it from many threads, provide external
20 // exclusive locking.
21
22 #include "sandboxed_api/sandbox2/comms.h"
23
24 #include <sys/socket.h>
25 #include <sys/uio.h>
26 #include <sys/un.h>
27 #include <syscall.h>
28 #include <unistd.h>
29
30 #include <atomic>
31 #include <cerrno>
32 #include <cstdint>
33 #include <cstdlib>
34 #include <cstring>
35 #include <functional>
36 #include <memory>
37 #include <string>
38 #include <utility>
39 #include <vector>
40
41 #include "absl/base/dynamic_annotations.h"
42 #include "absl/status/status.h"
43 #include "absl/status/statusor.h"
44 #include "absl/strings/numbers.h"
45 #include "absl/strings/str_format.h"
46 #include "absl/strings/string_view.h"
47 #include "google/protobuf/message_lite.h"
48 #include "sandboxed_api/sandbox2/util.h"
49 #include "sandboxed_api/util/fileops.h"
50 #include "sandboxed_api/util/raw_logging.h"
51 #include "sandboxed_api/util/status.h"
52 #include "sandboxed_api/util/status.pb.h"
53 #include "sandboxed_api/util/status_macros.h"
54
55 namespace sandbox2 {
56
57 class PotentiallyBlockingRegion {
58 public:
~PotentiallyBlockingRegion()59 ~PotentiallyBlockingRegion() {
60 // Do nothing. Not defaulted to avoid "unused variable" warnings.
61 }
62 };
63
64 namespace {
65
66 using sapi::file_util::fileops::FDCloser;
67
IsFatalError(int saved_errno)68 bool IsFatalError(int saved_errno) {
69 return saved_errno != EAGAIN && saved_errno != EWOULDBLOCK &&
70 saved_errno != EFAULT && saved_errno != EINTR &&
71 saved_errno != EINVAL && saved_errno != ENOMEM;
72 }
73
GetDefaultCommsFd()74 int GetDefaultCommsFd() {
75 if (const char* var = getenv(Comms::kSandbox2CommsFDEnvVar); var) {
76 int fd;
77 SAPI_RAW_CHECK(absl::SimpleAtoi(var, &fd), "cannot parse comms fd var");
78 unsetenv(Comms::kSandbox2CommsFDEnvVar);
79 return fd;
80 }
81 return Comms::kSandbox2ClientCommsFD;
82 }
83
CreateSockaddrUn(const std::string & socket_name,bool abstract_uds,sockaddr_un * sun)84 socklen_t CreateSockaddrUn(const std::string& socket_name, bool abstract_uds,
85 sockaddr_un* sun) {
86 sun->sun_family = AF_UNIX;
87 bzero(sun->sun_path, sizeof(sun->sun_path));
88 socklen_t slen = sizeof(sun->sun_family) + strlen(socket_name.c_str());
89 if (abstract_uds) {
90 // Create an 'abstract socket address' by specifying a leading null byte.
91 // The remainder of the path is used as a unique name, but no file is
92 // created on the filesystem. No need to NUL-terminate the string. See `man
93 // 7 unix` for further explanation.
94 strncpy(&sun->sun_path[1], socket_name.c_str(), sizeof(sun->sun_path) - 1);
95 // Len is complicated - it's essentially size of the path, plus initial
96 // NUL-byte, minus size of the sun.sun_family.
97 slen++;
98 } else {
99 // Create the socket address as it was passed from the constructor.
100 strncpy(&sun->sun_path[0], socket_name.c_str(), sizeof(sun->sun_path));
101 }
102
103 // This takes care of the socket address overflow.
104 if (slen > sizeof(sockaddr_un)) {
105 SAPI_RAW_LOG(ERROR, "Socket address is too long, will be truncated");
106 slen = sizeof(sockaddr_un);
107 }
108 return slen;
109 }
110 } // namespace
111
Comms(int fd,absl::string_view name)112 Comms::Comms(int fd, absl::string_view name) : connection_fd_(fd) {
113 // Generate a unique and meaningful socket name for this FD.
114 // Note: getpid()/gettid() are non-blocking syscalls.
115 if (name.empty()) {
116 name_ = absl::StrFormat("sandbox2::Comms:FD=%d/PID=%d/TID=%ld", fd,
117 getpid(), syscall(__NR_gettid));
118 } else {
119 name_ = std::string(name);
120 }
121
122 // File descriptor is already connected.
123 state_ = State::kConnected;
124 }
125
Comms(Comms::DefaultConnectionTag)126 Comms::Comms(Comms::DefaultConnectionTag) : Comms(GetDefaultCommsFd()) {}
127
~Comms()128 Comms::~Comms() { Terminate(); }
129
GetConnectionFD() const130 int Comms::GetConnectionFD() const {
131 return connection_fd_.get();
132 }
133
Create(absl::string_view socket_name,bool abstract_uds)134 absl::StatusOr<ListeningComms> ListeningComms::Create(
135 absl::string_view socket_name, bool abstract_uds) {
136 ListeningComms comms(std::string(socket_name), abstract_uds);
137 SAPI_RETURN_IF_ERROR(comms.Listen());
138 return comms;
139 }
140
Listen()141 absl::Status ListeningComms::Listen() {
142 bind_fd_ = FDCloser(socket(AF_UNIX, SOCK_STREAM, 0)); // Non-blocking
143 if (bind_fd_.get() == -1) {
144 return absl::ErrnoToStatus(errno, "socket(AF_UNIX) failed");
145 }
146
147 sockaddr_un sus;
148 socklen_t slen = CreateSockaddrUn(socket_name_, abstract_uds_, &sus);
149 // bind() is non-blocking.
150 if (bind(bind_fd_.get(), reinterpret_cast<sockaddr*>(&sus), slen) == -1) {
151 return absl::ErrnoToStatus(errno, "bind failed");
152 }
153
154 // listen() non-blocking.
155 if (listen(bind_fd_.get(), 0) == -1) {
156 return absl::ErrnoToStatus(errno, "listen failed");
157 }
158
159 SAPI_RAW_VLOG(1, "Listening at: %s", socket_name_.c_str());
160 return absl::OkStatus();
161 }
162
Accept()163 absl::StatusOr<Comms> ListeningComms::Accept() {
164 sockaddr_un suc;
165 socklen_t len = sizeof(suc);
166 int connection_fd;
167 {
168 PotentiallyBlockingRegion region;
169 connection_fd = TEMP_FAILURE_RETRY(
170 accept(bind_fd_.get(), reinterpret_cast<sockaddr*>(&suc), &len));
171 }
172 if (connection_fd == -1) {
173 return absl::ErrnoToStatus(errno, "accept failed");
174 }
175 SAPI_RAW_VLOG(1, "Accepted connection at: %s, fd: %d", socket_name_.c_str(),
176 connection_fd);
177 return Comms(connection_fd, socket_name_);
178 }
179
Connect(const std::string & socket_name,bool abstract_uds)180 absl::StatusOr<Comms> Comms::Connect(const std::string& socket_name,
181 bool abstract_uds) {
182 FDCloser connection_fd(socket(AF_UNIX, SOCK_STREAM, 0)); // Non-blocking
183 if (connection_fd.get() == -1) {
184 return absl::ErrnoToStatus(errno, "socket(AF_UNIX)");
185 }
186
187 sockaddr_un suc;
188 socklen_t slen = CreateSockaddrUn(socket_name, abstract_uds, &suc);
189 int ret;
190 {
191 PotentiallyBlockingRegion region;
192 ret = TEMP_FAILURE_RETRY(
193 connect(connection_fd.get(), reinterpret_cast<sockaddr*>(&suc), slen));
194 }
195 if (ret == -1) {
196 return absl::ErrnoToStatus(errno, "connect(connection_fd)");
197 }
198
199 SAPI_RAW_VLOG(1, "Connected to: %s, fd: %d", socket_name.c_str(),
200 connection_fd.get());
201 return Comms(connection_fd.Release(), socket_name);
202 }
203
Terminate()204 void Comms::Terminate() {
205 state_ = State::kTerminated;
206
207 connection_fd_.Close();
208 listening_comms_.reset();
209 }
210
SendTLV(uint32_t tag,size_t length,const void * value)211 bool Comms::SendTLV(uint32_t tag, size_t length, const void* value) {
212 if (length > GetMaxMsgSize()) {
213 SAPI_RAW_LOG(ERROR, "Maximum TLV message size exceeded: (%zu > %zu)",
214 length, GetMaxMsgSize());
215 return false;
216 }
217 if (length > kWarnMsgSize) {
218 // TODO(cblichmann): Use LOG_FIRST_N once Abseil logging is released.
219 static std::atomic<int> times_warned = 0;
220 if (times_warned.fetch_add(1, std::memory_order_relaxed) < 10) {
221 SAPI_RAW_LOG(
222 WARNING,
223 "TLV message of size %zu detected. Please consider switching "
224 "to Buffer API instead.",
225 length);
226 }
227 }
228
229 SAPI_RAW_VLOG(3, "Sending a TLV message, tag: 0x%08x, length: %zu", tag,
230 length);
231
232 // To maintain consistency with `RecvTL()`, we wrap `tag` and `length` in a TL
233 // struct.
234 const InternalTLV tl = {
235 .tag = tag,
236 .len = length,
237 };
238
239 if (length + sizeof(tl) > kSendTLVTempBufferSize) {
240 if (!Send(&tl, sizeof(tl))) {
241 return false;
242 }
243 return Send(value, length);
244 }
245 uint8_t tlv[kSendTLVTempBufferSize];
246 memcpy(tlv, &tl, sizeof(tl));
247 memcpy(reinterpret_cast<uint8_t*>(tlv) + sizeof(tl), value, length);
248
249 return Send(&tlv, sizeof(tl) + length);
250 }
251
RecvString(std::string * v)252 bool Comms::RecvString(std::string* v) {
253 uint32_t tag;
254 if (!RecvTLV(&tag, v)) {
255 return false;
256 }
257
258 if (tag != kTagString) {
259 SAPI_RAW_LOG(ERROR, "Expected (kTagString == 0x%x), got: 0x%x", kTagString,
260 tag);
261 return false;
262 }
263 return true;
264 }
265
SendString(const std::string & v)266 bool Comms::SendString(const std::string& v) {
267 return SendTLV(kTagString, v.length(), v.c_str());
268 }
269
RecvBytes(std::vector<uint8_t> * buffer)270 bool Comms::RecvBytes(std::vector<uint8_t>* buffer) {
271 uint32_t tag;
272 if (!RecvTLV(&tag, buffer)) {
273 return false;
274 }
275 if (tag != kTagBytes) {
276 buffer->clear();
277 SAPI_RAW_LOG(ERROR, "Expected (kTagBytes == 0x%x), got: 0x%u", kTagBytes,
278 tag);
279 return false;
280 }
281 return true;
282 }
283
SendBytes(const uint8_t * v,size_t len)284 bool Comms::SendBytes(const uint8_t* v, size_t len) {
285 return SendTLV(kTagBytes, len, v);
286 }
287
SendBytes(const std::vector<uint8_t> & buffer)288 bool Comms::SendBytes(const std::vector<uint8_t>& buffer) {
289 return SendBytes(buffer.data(), buffer.size());
290 }
291
RecvCreds(pid_t * pid,uid_t * uid,gid_t * gid)292 bool Comms::RecvCreds(pid_t* pid, uid_t* uid, gid_t* gid) {
293 ucred uc;
294 socklen_t sls = sizeof(uc);
295 int rc;
296 {
297 // Not completely sure if getsockopt() can block on SO_PEERCRED, but let's
298 // play it safe.
299 PotentiallyBlockingRegion region;
300 rc = getsockopt(GetConnectionFD(), SOL_SOCKET, SO_PEERCRED, &uc, &sls);
301 }
302 if (rc == -1) {
303 SAPI_RAW_PLOG(ERROR, "getsockopt(SO_PEERCRED)");
304 return false;
305 }
306 *pid = uc.pid;
307 *uid = uc.uid;
308 *gid = uc.gid;
309
310 SAPI_RAW_VLOG(2, "Received credentials from PID/UID/GID: %d/%u/%u", *pid,
311 *uid, *gid);
312 return true;
313 }
314
RecvFD(int * fd)315 bool Comms::RecvFD(int* fd) {
316 char fd_msg[8192];
317 cmsghdr* cmsg = reinterpret_cast<cmsghdr*>(fd_msg);
318
319 InternalTLV tlv;
320 iovec iov = {.iov_base = &tlv, .iov_len = sizeof(tlv)};
321
322 msghdr msg = {
323 .msg_name = nullptr,
324 .msg_namelen = 0,
325 .msg_iov = &iov,
326 .msg_iovlen = 1,
327 .msg_control = cmsg,
328 .msg_controllen = sizeof(fd_msg),
329 .msg_flags = 0,
330 };
331
332 const auto op = [&msg](int fd) -> ssize_t {
333 PotentiallyBlockingRegion region;
334 // Use syscall, otherwise we would need to allow socketcall() on PPC.
335 return TEMP_FAILURE_RETRY(
336 util::Syscall(__NR_recvmsg, fd, reinterpret_cast<uintptr_t>(&msg), 0));
337 };
338 ssize_t len;
339 len = op(connection_fd_.get());
340 if (len < 0) {
341 if (IsFatalError(errno)) {
342 Terminate();
343 }
344 SAPI_RAW_PLOG(ERROR, "recvmsg(SCM_RIGHTS)");
345 return false;
346 }
347 if (len == 0) {
348 Terminate();
349 SAPI_RAW_VLOG(1, "RecvFD: end-point terminated the connection.");
350 return false;
351 }
352 if (len != sizeof(tlv)) {
353 SAPI_RAW_LOG(ERROR, "Expected size: %zu, got %zd", sizeof(tlv), len);
354 return false;
355 }
356 // At this point, we know that op() has been called successfully, therefore
357 // msg struct has been fully populated. Apparently MSAN is not aware of
358 // syscall(__NR_recvmsg) semantics so we need to suppress the error (here and
359 // everywhere below).
360 ABSL_ANNOTATE_MEMORY_IS_INITIALIZED(&tlv, sizeof(tlv));
361
362 if (tlv.tag != kTagFd) {
363 SAPI_RAW_LOG(ERROR, "Expected (kTagFD: 0x%x), got: 0x%x", kTagFd, tlv.tag);
364 return false;
365 }
366
367 cmsg = CMSG_FIRSTHDR(&msg);
368 ABSL_ANNOTATE_MEMORY_IS_INITIALIZED(cmsg, sizeof(cmsghdr));
369 while (cmsg) {
370 if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
371 if (cmsg->cmsg_len != CMSG_LEN(sizeof(int))) {
372 SAPI_RAW_VLOG(1,
373 "recvmsg(SCM_RIGHTS): cmsg->cmsg_len != "
374 "CMSG_LEN(sizeof(int)), skipping");
375 continue;
376 }
377 int* fds = reinterpret_cast<int*>(CMSG_DATA(cmsg));
378 *fd = fds[0];
379 ABSL_ANNOTATE_MEMORY_IS_INITIALIZED(fd, sizeof(int));
380 return true;
381 }
382 cmsg = CMSG_NXTHDR(&msg, cmsg);
383 }
384 SAPI_RAW_LOG(ERROR,
385 "Haven't received the SCM_RIGHTS message, process is probably "
386 "out of free file descriptors");
387 return false;
388 }
389
SendFD(int fd)390 bool Comms::SendFD(int fd) {
391 char fd_msg[CMSG_SPACE(sizeof(int))] = {0};
392 cmsghdr* cmsg = reinterpret_cast<cmsghdr*>(fd_msg);
393 cmsg->cmsg_level = SOL_SOCKET;
394 cmsg->cmsg_type = SCM_RIGHTS;
395 cmsg->cmsg_len = CMSG_LEN(sizeof(int));
396
397 int* fds = reinterpret_cast<int*>(CMSG_DATA(cmsg));
398 fds[0] = fd;
399
400 InternalTLV tlv = {kTagFd, 0};
401
402 iovec iov;
403 iov.iov_base = &tlv;
404 iov.iov_len = sizeof(tlv);
405
406 msghdr msg;
407 msg.msg_name = nullptr;
408 msg.msg_namelen = 0;
409 msg.msg_iov = &iov;
410 msg.msg_iovlen = 1;
411 msg.msg_control = cmsg;
412 msg.msg_controllen = sizeof(fd_msg);
413 msg.msg_flags = 0;
414
415 const auto op = [&msg](int fd) -> ssize_t {
416 PotentiallyBlockingRegion region;
417 // Use syscall, otherwise we would need to whitelist socketcall() on PPC.
418 return TEMP_FAILURE_RETRY(
419 util::Syscall(__NR_sendmsg, fd, reinterpret_cast<uintptr_t>(&msg), 0));
420 };
421 ssize_t len;
422 len = op(connection_fd_.get());
423 if (len == -1 && errno == EPIPE) {
424 Terminate();
425 SAPI_RAW_LOG(ERROR, "sendmsg(SCM_RIGHTS): Peer disconnected");
426 return false;
427 }
428 if (len < 0) {
429 if (IsFatalError(errno)) {
430 Terminate();
431 }
432 SAPI_RAW_PLOG(ERROR, "sendmsg(SCM_RIGHTS)");
433 return false;
434 }
435 if (len != sizeof(tlv)) {
436 SAPI_RAW_LOG(ERROR, "Expected to send %zu bytes, sent %zd", sizeof(tlv),
437 len);
438 return false;
439 }
440 return true;
441 }
442
RecvProtoBuf(google::protobuf::MessageLite * message)443 bool Comms::RecvProtoBuf(google::protobuf::MessageLite* message) {
444 uint32_t tag;
445 std::vector<uint8_t> bytes;
446 if (!RecvTLV(&tag, &bytes)) {
447 if (IsConnected()) {
448 SAPI_RAW_PLOG(ERROR, "RecvProtoBuf failed for (%s)", name_);
449 } else {
450 Terminate();
451 SAPI_RAW_VLOG(2, "Connection terminated (%s)", name_.c_str());
452 }
453 return false;
454 }
455
456 if (tag != kTagProto2) {
457 SAPI_RAW_LOG(ERROR, "Expected tag: 0x%x, got: 0x%u", kTagProto2, tag);
458 return false;
459 }
460 return message->ParseFromArray(bytes.data(), bytes.size());
461 }
462
SendProtoBuf(const google::protobuf::MessageLite & message)463 bool Comms::SendProtoBuf(const google::protobuf::MessageLite& message) {
464 std::string str;
465 if (!message.SerializeToString(&str)) {
466 SAPI_RAW_LOG(ERROR, "Couldn't serialize the ProtoBuf");
467 return false;
468 }
469
470 return SendTLV(kTagProto2, str.length(),
471 reinterpret_cast<const uint8_t*>(str.data()));
472 }
473
474 // *****************************************************************************
475 // All methods below are private, for internal use only.
476 // *****************************************************************************
477
Send(const void * data,size_t len)478 bool Comms::Send(const void* data, size_t len) {
479 size_t total_sent = 0;
480 const char* bytes = reinterpret_cast<const char*>(data);
481 const auto op = [bytes, len, &total_sent](int fd) -> ssize_t {
482 PotentiallyBlockingRegion region;
483 return TEMP_FAILURE_RETRY(write(fd, &bytes[total_sent], len - total_sent));
484 };
485 while (total_sent < len) {
486 ssize_t s;
487 s = op(connection_fd_.get());
488 if (s == -1 && errno == EPIPE) {
489 Terminate();
490 // We do not expect the other end to disappear.
491 SAPI_RAW_LOG(ERROR, "Send: end-point terminated the connection");
492 return false;
493 }
494 if (s == -1) {
495 SAPI_RAW_PLOG(ERROR, "write");
496 if (IsFatalError(errno)) {
497 Terminate();
498 }
499 return false;
500 }
501 if (s == 0) {
502 SAPI_RAW_LOG(ERROR,
503 "Couldn't write more bytes, wrote: %zu, requested: %zu",
504 total_sent, len);
505 return false;
506 }
507 total_sent += s;
508 }
509 return true;
510 }
511
Recv(void * data,size_t len)512 bool Comms::Recv(void* data, size_t len) {
513 size_t total_recv = 0;
514 char* bytes = reinterpret_cast<char*>(data);
515 const auto op = [bytes, len, &total_recv](int fd) -> ssize_t {
516 PotentiallyBlockingRegion region;
517 return TEMP_FAILURE_RETRY(read(fd, &bytes[total_recv], len - total_recv));
518 };
519 while (total_recv < len) {
520 ssize_t s;
521 s = op(connection_fd_.get());
522 if (s == -1) {
523 SAPI_RAW_PLOG(ERROR, "read");
524 if (IsFatalError(errno)) {
525 Terminate();
526 }
527 return false;
528 }
529 if (s == 0) {
530 Terminate();
531 // The other end might have finished its work.
532 SAPI_RAW_VLOG(2, "Recv: end-point terminated the connection.");
533 return false;
534 }
535 total_recv += s;
536 }
537 return true;
538 }
539
540 // Internal helper method (low level).
RecvTL(uint32_t * tag,size_t * length)541 bool Comms::RecvTL(uint32_t* tag, size_t* length) {
542 InternalTLV tl;
543 if (!Recv(reinterpret_cast<uint8_t*>(&tl), sizeof(tl))) {
544 SAPI_RAW_VLOG(2, "RecvTL: Can't read tag and length");
545 return false;
546 }
547 *tag = tl.tag;
548 *length = tl.len;
549 if (*length > GetMaxMsgSize()) {
550 SAPI_RAW_LOG(ERROR, "Maximum TLV message size exceeded: (%zu > %zd)",
551 *length, GetMaxMsgSize());
552 return false;
553 }
554 if (*length > kWarnMsgSize) {
555 static std::atomic<int> times_warned = 0;
556 if (times_warned.fetch_add(1, std::memory_order_relaxed) < 10) {
557 SAPI_RAW_LOG(
558 WARNING,
559 "TLV message of size: %zu detected. Please consider switching to "
560 "Buffer API instead.",
561 *length);
562 }
563 }
564 return true;
565 }
566
RecvTLV(uint32_t * tag,std::vector<uint8_t> * value)567 bool Comms::RecvTLV(uint32_t* tag, std::vector<uint8_t>* value) {
568 return RecvTLVGeneric(tag, value);
569 }
570
RecvTLV(uint32_t * tag,std::string * value)571 bool Comms::RecvTLV(uint32_t* tag, std::string* value) {
572 return RecvTLVGeneric(tag, value);
573 }
574
575 template <typename T>
RecvTLVGeneric(uint32_t * tag,T * value)576 bool Comms::RecvTLVGeneric(uint32_t* tag, T* value) {
577 size_t length;
578 if (!RecvTL(tag, &length)) {
579 return false;
580 }
581
582 value->resize(length);
583 return length == 0 || Recv(reinterpret_cast<uint8_t*>(value->data()), length);
584 }
585
RecvTLV(uint32_t * tag,size_t * length,void * buffer,size_t buffer_size)586 bool Comms::RecvTLV(uint32_t* tag, size_t* length, void* buffer,
587 size_t buffer_size) {
588 if (!RecvTL(tag, length)) {
589 return false;
590 }
591
592 if (*length == 0) {
593 return true;
594 }
595
596 if (*length > buffer_size) {
597 SAPI_RAW_LOG(ERROR, "Buffer size too small (0x%zx > 0x%zx)", *length,
598 buffer_size);
599 return false;
600 }
601
602 return Recv(reinterpret_cast<uint8_t*>(buffer), *length);
603 }
604
RecvInt(void * buffer,size_t len,uint32_t tag)605 bool Comms::RecvInt(void* buffer, size_t len, uint32_t tag) {
606 uint32_t received_tag;
607 size_t received_length;
608 if (!RecvTLV(&received_tag, &received_length, buffer, len)) {
609 return false;
610 }
611
612 if (received_tag != tag) {
613 SAPI_RAW_LOG(ERROR, "Expected tag: 0x%08x, got: 0x%x", tag, received_tag);
614 return false;
615 }
616 if (received_length != len) {
617 SAPI_RAW_LOG(ERROR, "Expected length: %zu, got: %zu", len, received_length);
618 return false;
619 }
620 return true;
621 }
622
RecvStatus(absl::Status * status)623 bool Comms::RecvStatus(absl::Status* status) {
624 sapi::StatusProto proto;
625 if (!RecvProtoBuf(&proto)) {
626 return false;
627 }
628 *status = sapi::MakeStatusFromProto(proto);
629 return true;
630 }
631
SendStatus(const absl::Status & status)632 bool Comms::SendStatus(const absl::Status& status) {
633 sapi::StatusProto proto;
634 sapi::SaveStatusToProto(status, &proto);
635 return SendProtoBuf(proto);
636 }
637
MoveToAnotherFd()638 void Comms::MoveToAnotherFd() {
639 SAPI_RAW_CHECK(connection_fd_.get() != -1,
640 "Cannot move comms fd as it's not connected");
641 FDCloser new_fd(dup(connection_fd_.get()));
642 SAPI_RAW_CHECK(new_fd.get() != -1, "Failed to move comms to another fd");
643 connection_fd_.Swap(new_fd);
644 }
645
646 } // namespace sandbox2
647