[merge m-65]: macOS V2 Sandbox: Send profile to child process robustly.

This is CL squashes three cherry-picks into one CL for the purpose of
merging to m-65. The CLs make IPC of the seatbelt profile from the
browser to the child process robust by adding logging and handling short
writes or temporarily busy file descriptors.

Original CLs:
78e7405af0e16c10500bc5b923e938ad9f52e735
macOS V2 Sandbox: Add logging to failures in profile communication.
Change-Id: I425bedc92d47a27f90b4d80541d2cbf68e8f6a21
Reviewed-on: https://chromium-review.googlesource.com/916946
Cr-Commit-Position: refs/heads/master@{#536495}

7ea6cf78e3bced2f48c49394493832520cdc97f0
macOS V2 Sandbox: Send serialized seatbelt profile in a loop.
Change-Id: Ib1f4c57c61d3ce68b875b347de480f514100d3c2
Reviewed-on: https://chromium-review.googlesource.com/920765
Cr-Commit-Position: refs/heads/master@{#537360}

fb63be6d346dbbbe47e3edf3a7e8e6b6b7f286d7
macOS V2 Sandbox: Continue sending profile if EAGAIN occurs.
Change-Id: I1a6c91fc348a20804b06d0bb64fbcd17cccea20f
Reviewed-on: https://chromium-review.googlesource.com/924246
Cr-Commit-Position: refs/heads/master@{#538474}

Bug: 801889
Change-Id: I421f83dfa58ad2990e4f6fe304f9713cfa9d6b60
Reviewed-on: https://chromium-review.googlesource.com/935664
Reviewed-by: Robert Sesek <rsesek@chromium.org>
Reviewed-by: Mark Mentovai <mark@chromium.org>
Cr-Commit-Position: refs/branch-heads/3325@{#579}
Cr-Branched-From: bc084a8b5afa3744a74927344e304c02ae54189f-refs/heads/master@{#530369}
diff --git a/content/browser/child_process_launcher_helper_mac.cc b/content/browser/child_process_launcher_helper_mac.cc
index e804ab6..ebd4434 100644
--- a/content/browser/child_process_launcher_helper_mac.cc
+++ b/content/browser/child_process_launcher_helper_mac.cc
@@ -154,7 +154,7 @@
 
     int pipe = seatbelt_exec_client_->SendProfileAndGetFD();
     if (pipe < 0) {
-      LOG(ERROR) << "pipe for sending sandbox profile is an invalid FD";
+      LOG(ERROR) << "Sending the seatbelt profile failed.";
       return false;
     }
 
diff --git a/sandbox/mac/seatbelt_exec.cc b/sandbox/mac/seatbelt_exec.cc
index 53a5e79..df8167c 100644
--- a/sandbox/mac/seatbelt_exec.cc
+++ b/sandbox/mac/seatbelt_exec.cc
@@ -5,6 +5,7 @@
 #include "sandbox/mac/seatbelt_exec.h"
 
 #include <fcntl.h>
+#include <inttypes.h>
 #include <stdarg.h>
 #include <stdio.h>
 #include <sys/socket.h>
@@ -20,6 +21,58 @@
 
 namespace sandbox {
 
+namespace {
+
+struct ReadTraits {
+  using BufferType = uint8_t*;
+  static constexpr char kNameString[] = "read";
+  static ssize_t Operate(int fd, BufferType buffer, size_t size) {
+    return read(fd, buffer, size);
+  }
+};
+constexpr char ReadTraits::kNameString[];
+
+struct WriteTraits {
+  using BufferType = const uint8_t*;
+  static constexpr char kNameString[] = "write";
+  static ssize_t Operate(int fd, BufferType buffer, size_t size) {
+    return write(fd, buffer, size);
+  }
+};
+constexpr char WriteTraits::kNameString[];
+
+template <typename Traits>
+bool ReadOrWrite(int fd,
+                 const typename Traits::BufferType buffer,
+                 const size_t size) {
+  if (size > std::numeric_limits<ssize_t>::max()) {
+    logging::Error("request size is greater than ssize_t::max");
+    return false;
+  }
+
+  ssize_t bytes_to_transact = static_cast<ssize_t>(size);
+
+  while (bytes_to_transact > 0) {
+    ssize_t offset = size - bytes_to_transact;
+    ssize_t transacted_bytes =
+        HANDLE_EINTR(Traits::Operate(fd, buffer + offset, bytes_to_transact));
+    if (transacted_bytes < 0) {
+      if (errno == EAGAIN) {
+        sched_yield();
+        continue;
+      }
+      logging::PError("%s failed", Traits::kNameString);
+      return false;
+    }
+
+    bytes_to_transact -= transacted_bytes;
+  }
+
+  return true;
+}
+
+}  // namespace
+
 SeatbeltExecClient::SeatbeltExecClient() {
   if (pipe(pipe_) != 0)
     logging::PFatal("SeatbeltExecClient: pipe failed");
@@ -59,29 +112,41 @@
 
 int SeatbeltExecClient::SendProfileAndGetFD() {
   std::string serialized_protobuf;
-  if (!policy_.SerializeToString(&serialized_protobuf))
+  if (!policy_.SerializeToString(&serialized_protobuf)) {
+    logging::Error("SeatbeltExecClient: Serializing the profile failed.");
     return -1;
+  }
 
-  if (!WriteString(&serialized_protobuf))
+  if (!WriteString(serialized_protobuf)) {
+    logging::Error(
+        "SeatbeltExecClient: Writing the serialized profile failed.");
     return -1;
+  }
 
   IGNORE_EINTR(close(pipe_[1]));
   pipe_[1] = -1;
 
+  if (pipe_[0] < 0)
+    logging::Error("SeatbeltExecClient: The pipe returned an invalid fd.");
+
   return pipe_[0];
 }
 
-bool SeatbeltExecClient::WriteString(std::string* str) {
-  struct iovec iov[1];
-  iov[0].iov_base = &(*str)[0];
-  iov[0].iov_len = str->size();
-
-  ssize_t written = HANDLE_EINTR(writev(pipe_[1], iov, arraysize(iov)));
-  if (written < 0) {
-    logging::PError("SeatbeltExecClient: writev failed");
+bool SeatbeltExecClient::WriteString(const std::string& str) {
+  uint64_t str_len = static_cast<uint64_t>(str.size());
+  if (!ReadOrWrite<WriteTraits>(pipe_[1], reinterpret_cast<uint8_t*>(&str_len),
+                                sizeof(str_len))) {
+    logging::Error("SeatbeltExecClient: write buffer length failed.");
     return false;
   }
-  return static_cast<uint64_t>(written) == str->size();
+
+  if (!ReadOrWrite<WriteTraits>(
+          pipe_[1], reinterpret_cast<const uint8_t*>(&str[0]), str_len)) {
+    logging::Error("SeatbeltExecClient: write buffer failed.");
+    return false;
+  }
+
+  return true;
 }
 
 SeatbeltExecServer::SeatbeltExecServer(int fd) : fd_(fd), extra_params_() {}
@@ -130,19 +195,21 @@
 }
 
 bool SeatbeltExecServer::ReadString(std::string* str) {
-  // 4 pages of memory is enough to hold the sandbox profiles.
-  std::vector<char> buffer(4096 * 4, '\0');
-
-  struct iovec iov[1];
-  iov[0].iov_base = buffer.data();
-  iov[0].iov_len = buffer.size();
-
-  ssize_t read_length = HANDLE_EINTR(readv(fd_, iov, arraysize(iov)));
-  if (read_length < 0) {
-    logging::PError("SeatbeltExecServer: readv failed");
+  uint64_t buf_len = 0;
+  if (!ReadOrWrite<ReadTraits>(fd_, reinterpret_cast<uint8_t*>(&buf_len),
+                               sizeof(buf_len))) {
+    logging::Error("SeatbeltExecServer: failed to read buffer length.");
     return false;
   }
-  str->assign(buffer.data());
+
+  str->resize(buf_len);
+
+  if (!ReadOrWrite<ReadTraits>(fd_, reinterpret_cast<uint8_t*>(&(*str)[0]),
+                               buf_len)) {
+    logging::Error("SeatbeltExecServer: failed to read buffer.");
+    return false;
+  }
+
   return true;
 }
 
diff --git a/sandbox/mac/seatbelt_exec.h b/sandbox/mac/seatbelt_exec.h
index 08ad5c6..3366d14 100644
--- a/sandbox/mac/seatbelt_exec.h
+++ b/sandbox/mac/seatbelt_exec.h
@@ -45,7 +45,7 @@
 
  private:
   // This writes a string (the serialized protobuf) to the |pipe_|.
-  bool WriteString(std::string* str);
+  bool WriteString(const std::string& str);
 
   // This is the protobuf which contains the sandbox profile and parameters,
   // and is serialized and sent to the other process.