tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

ipc_channel_posix.cc (41981B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 // Copyright (c) 2008 The Chromium Authors. All rights reserved.
      4 // Use of this source code is governed by a BSD-style license that can be
      5 // found in the LICENSE file.
      6 
      7 #include "chrome/common/ipc_channel_posix.h"
      8 
      9 #include <errno.h>
     10 #include <fcntl.h>
     11 #include <limits.h>
     12 #include "mozilla/Mutex.h"
     13 #if defined(XP_DARWIN)
     14 #  include <mach/message.h>
     15 #  include <mach/port.h>
     16 #  include "mozilla/UniquePtrExtensions.h"
     17 #  include "chrome/common/mach_ipc_mac.h"
     18 #endif
     19 #if defined(XP_DARWIN) || defined(XP_NETBSD)
     20 #  include <sched.h>
     21 #endif
     22 #include <stddef.h>
     23 #include <unistd.h>
     24 #include <sys/types.h>
     25 #include <sys/socket.h>
     26 #include <sys/stat.h>
     27 #include <sys/un.h>
     28 #include <sys/uio.h>
     29 
     30 #include "base/command_line.h"
     31 #include "base/eintr_wrapper.h"
     32 #include "base/logging.h"
     33 #include "base/process.h"
     34 #include "base/process_util.h"
     35 #include "base/string_util.h"
     36 #include "chrome/common/chrome_switches.h"
     37 #include "chrome/common/ipc_channel_utils.h"
     38 #include "chrome/common/ipc_message_utils.h"
     39 #include "mozilla/ipc/Endpoint.h"
     40 #include "mozilla/ipc/ProtocolUtils.h"
     41 #include "mozilla/StaticMutex.h"
     42 #include "mozilla/UniquePtr.h"
     43 
     44 // Use OS specific iovec array limit where it's possible.
     45 #if defined(IOV_MAX)
     46 static const size_t kMaxIOVecSize = IOV_MAX;
     47 #elif defined(ANDROID)
     48 static const size_t kMaxIOVecSize = 256;
     49 #else
     50 static const size_t kMaxIOVecSize = 16;
     51 #endif
     52 
     53 using namespace mozilla::ipc;
     54 
     55 namespace IPC {
     56 
     57 //------------------------------------------------------------------------------
     58 namespace {
     59 
     60 bool ErrorIsBrokenPipe(int err) { return err == EPIPE || err == ECONNRESET; }
     61 
     62 // Some Android ARM64 devices appear to have a bug where sendmsg
     63 // sometimes returns 0xFFFFFFFF, which we're assuming is a -1 that was
     64 // incorrectly truncated to 32-bit and then zero-extended.
     65 // See bug 1660826 for details.
     66 //
     67 // This is a workaround to detect that value and replace it with -1
     68 // (and check that there really was an error), because the largest
     69 // amount we'll ever write is Channel::kMaximumMessageSize (256MiB).
     70 //
     71 // The workaround is also enabled on x86_64 Android on debug builds,
     72 // although the bug isn't known to manifest there, so that there will
     73 // be some CI coverage of this code.
     74 
     75 static inline ssize_t corrected_sendmsg(int socket,
     76                                        const struct msghdr* message,
     77                                        int flags) {
     78 #if defined(ANDROID) && \
     79    (defined(__aarch64__) || (defined(DEBUG) && defined(__x86_64__)))
     80  static constexpr auto kBadValue = static_cast<ssize_t>(0xFFFFFFFF);
     81  static_assert(kBadValue > 0);
     82 
     83 #  ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
     84  errno = 0;
     85 #  endif
     86  ssize_t bytes_written = sendmsg(socket, message, flags);
     87  if (bytes_written == kBadValue) {
     88    MOZ_DIAGNOSTIC_ASSERT(errno != 0);
     89    bytes_written = -1;
     90  }
     91  MOZ_DIAGNOSTIC_ASSERT(bytes_written < kBadValue);
     92  return bytes_written;
     93 #else
     94  return sendmsg(socket, message, flags);
     95 #endif
     96 }
     97 
     98 }  // namespace
     99 //------------------------------------------------------------------------------
    100 
    101 const Channel::ChannelKind ChannelPosix::sKind{
    102    .create_raw_pipe = &ChannelPosix::CreateRawPipe,
    103    .num_relayed_attachments = &ChannelPosix::NumRelayedAttachments,
    104    .is_valid_handle = &ChannelPosix::IsValidHandle,
    105 };
    106 
    107 ChannelPosix::ChannelPosix(mozilla::UniqueFileHandle pipe, Mode mode,
    108                           base::ProcessId other_pid)
    109    : other_pid_(other_pid) {
    110  Init(mode);
    111  SetPipe(pipe.release());
    112 
    113  EnqueueHelloMessage();
    114 }
    115 
    116 void ChannelPosix::SetPipe(int fd) {
    117  chan_cap_.NoteExclusiveAccess();
    118 
    119  pipe_ = fd;
    120  pipe_buf_len_ = 0;
    121  if (fd >= 0) {
    122    int buf_len;
    123    socklen_t optlen = sizeof(buf_len);
    124    if (getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &buf_len, &optlen) != 0) {
    125      CHROMIUM_LOG(WARNING)
    126          << "Unable to determine pipe buffer size: " << strerror(errno);
    127      return;
    128    }
    129    CHECK(optlen == sizeof(buf_len));
    130    CHECK(buf_len > 0);
    131    pipe_buf_len_ = static_cast<unsigned>(buf_len);
    132  }
    133 }
    134 
    135 bool ChannelPosix::PipeBufHasSpaceAfter(size_t already_written) {
    136  // If the OS didn't tell us the buffer size for some reason, then
    137  // don't apply this limitation on the amount we try to write.
    138  return pipe_buf_len_ == 0 ||
    139         static_cast<size_t>(pipe_buf_len_) > already_written;
    140 }
    141 
    142 void ChannelPosix::Init(Mode mode) {
    143  // Verify that we fit in a "quantum-spaced" jemalloc bucket.
    144  static_assert(sizeof(*this) <= 512, "Exceeded expected size class");
    145 
    146  MOZ_RELEASE_ASSERT(kControlBufferHeaderSize >= CMSG_SPACE(0));
    147  MOZ_RELEASE_ASSERT(kControlBufferSize >=
    148                     CMSG_SPACE(sizeof(int) * kControlBufferMaxFds));
    149 
    150  chan_cap_.NoteExclusiveAccess();
    151 
    152  mode_ = mode;
    153  is_blocked_on_write_ = false;
    154  partial_write_.reset();
    155  input_buf_offset_ = 0;
    156  input_buf_ = mozilla::MakeUnique<char[]>(Channel::kReadBufferSize);
    157  input_cmsg_buf_ = mozilla::MakeUnique<char[]>(kControlBufferSize);
    158  SetPipe(-1);
    159  waiting_connect_ = true;
    160 #if defined(XP_DARWIN)
    161  last_pending_fd_id_ = 0;
    162  other_task_ = nullptr;
    163 #endif
    164 }
    165 
    166 bool ChannelPosix::EnqueueHelloMessage() {
    167  mozilla::UniquePtr<Message> msg(
    168      new Message(MSG_ROUTING_NONE, HELLO_MESSAGE_TYPE));
    169  if (!msg->WriteInt(base::GetCurrentProcId())) {
    170    CloseLocked();
    171    return false;
    172  }
    173 
    174  OutputQueuePush(std::move(msg));
    175  return true;
    176 }
    177 
    178 bool ChannelPosix::Connect(Listener* listener) {
    179  IOThread().AssertOnCurrentThread();
    180  mozilla::MutexAutoLock lock(SendMutex());
    181  chan_cap_.NoteExclusiveAccess();
    182 
    183  if (pipe_ == -1) {
    184    return false;
    185  }
    186 
    187  listener_ = listener;
    188 
    189  return ContinueConnect();
    190 }
    191 
    192 bool ChannelPosix::ContinueConnect() {
    193  chan_cap_.NoteExclusiveAccess();
    194  MOZ_ASSERT(pipe_ != -1);
    195 
    196 #if defined(XP_DARWIN)
    197  // If we're still waiting for our peer task to be provided, don't start
    198  // listening yet. We'll start receiving messages once the task_t is set.
    199  if (mode_ == MODE_BROKER_SERVER && !other_task_) {
    200    MOZ_ASSERT(waiting_connect_);
    201    return true;
    202  }
    203 #endif
    204 
    205  MessageLoopForIO::current()->WatchFileDescriptor(
    206      pipe_, true, MessageLoopForIO::WATCH_READ, &read_watcher_, this);
    207  waiting_connect_ = false;
    208 
    209  return ProcessOutgoingMessages();
    210 }
    211 
    212 void ChannelPosix::SetOtherPid(base::ProcessId other_pid) {
    213  IOThread().AssertOnCurrentThread();
    214  mozilla::MutexAutoLock lock(SendMutex());
    215  chan_cap_.NoteExclusiveAccess();
    216  MOZ_RELEASE_ASSERT(
    217      other_pid_ == base::kInvalidProcessId || other_pid_ == other_pid,
    218      "Multiple sources of SetOtherPid disagree!");
    219  other_pid_ = other_pid;
    220 }
    221 
    222 bool ChannelPosix::ProcessIncomingMessages() {
    223  chan_cap_.NoteOnTarget();
    224 
    225  struct msghdr msg = {0};
    226  struct iovec iov;
    227 
    228  msg.msg_iov = &iov;
    229  msg.msg_iovlen = 1;
    230  msg.msg_control = input_cmsg_buf_.get();
    231 
    232  for (;;) {
    233    msg.msg_controllen = kControlBufferSize;
    234 
    235    if (pipe_ == -1) return false;
    236 
    237    // In some cases the beginning of a message will be stored in input_buf_. We
    238    // don't want to overwrite that, so we store the new data after it.
    239    iov.iov_base = input_buf_.get() + input_buf_offset_;
    240    iov.iov_len = Channel::kReadBufferSize - input_buf_offset_;
    241 
    242    // Read from pipe.
    243    // recvmsg() returns 0 if the connection has closed or EAGAIN if no data
    244    // is waiting on the pipe.
    245    ssize_t bytes_read = HANDLE_EINTR(recvmsg(pipe_, &msg, MSG_DONTWAIT));
    246 
    247    if (bytes_read < 0) {
    248      if (errno == EAGAIN) {
    249        return true;
    250      } else {
    251        if (!ErrorIsBrokenPipe(errno)) {
    252          CHROMIUM_LOG(ERROR)
    253              << "pipe error (fd " << pipe_ << "): " << strerror(errno);
    254        }
    255        return false;
    256      }
    257    } else if (bytes_read == 0) {
    258      // The pipe has closed...
    259      Close();
    260      return false;
    261    }
    262    DCHECK(bytes_read);
    263 
    264    // a pointer to an array of |num_wire_fds| file descriptors from the read
    265    const int* wire_fds = NULL;
    266    unsigned num_wire_fds = 0;
    267 
    268    // walk the list of control messages and, if we find an array of file
    269    // descriptors, save a pointer to the array
    270 
    271    // This next if statement is to work around an OSX issue where
    272    // CMSG_FIRSTHDR will return non-NULL in the case that controllen == 0.
    273    // Here's a test case:
    274    //
    275    // int main() {
    276    // struct msghdr msg;
    277    //   msg.msg_control = &msg;
    278    //   msg.msg_controllen = 0;
    279    //   if (CMSG_FIRSTHDR(&msg))
    280    //     printf("Bug found!\n");
    281    // }
    282    if (msg.msg_controllen > 0) {
    283      // On OSX, CMSG_FIRSTHDR doesn't handle the case where controllen is 0
    284      // and will return a pointer into nowhere.
    285      for (struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); cmsg;
    286           cmsg = CMSG_NXTHDR(&msg, cmsg)) {
    287        if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
    288          const unsigned payload_len = cmsg->cmsg_len - CMSG_LEN(0);
    289          DCHECK(payload_len % sizeof(int) == 0);
    290          wire_fds = reinterpret_cast<int*>(CMSG_DATA(cmsg));
    291          num_wire_fds = payload_len / 4;
    292 
    293          if (msg.msg_flags & MSG_CTRUNC) {
    294            CHROMIUM_LOG(ERROR)
    295                << "SCM_RIGHTS message was truncated"
    296                << " cmsg_len:" << cmsg->cmsg_len << " fd:" << pipe_;
    297            for (unsigned i = 0; i < num_wire_fds; ++i)
    298              IGNORE_EINTR(close(wire_fds[i]));
    299            return false;
    300          }
    301          break;
    302        }
    303      }
    304    }
    305 
    306    // Process messages from input buffer.
    307    const char* p = input_buf_.get();
    308    const char* end = input_buf_.get() + input_buf_offset_ + bytes_read;
    309 
    310    // A pointer to an array of |num_fds| file descriptors which includes any
    311    // fds that have spilled over from a previous read.
    312    const int* fds;
    313    unsigned num_fds;
    314    unsigned fds_i = 0;  // the index of the first unused descriptor
    315 
    316    if (input_overflow_fds_.empty()) {
    317      fds = wire_fds;
    318      num_fds = num_wire_fds;
    319    } else {
    320      // This code may look like a no-op in the case where
    321      // num_wire_fds == 0, but in fact:
    322      //
    323      // 1. wire_fds will be nullptr, so passing it to memcpy is
    324      // undefined behavior according to the C standard, even though
    325      // the memcpy length is 0.
    326      //
    327      // 2. prev_size will be an out-of-bounds index for
    328      // input_overflow_fds_; this is undefined behavior according to
    329      // the C++ standard, even though the element only has its
    330      // pointer taken and isn't accessed (and the corresponding
    331      // operation on a C array would be defined).
    332      //
    333      // UBSan makes #1 a fatal error, and assertions in libstdc++ do
    334      // the same for #2 if enabled.
    335      if (num_wire_fds > 0) {
    336        const size_t prev_size = input_overflow_fds_.size();
    337        input_overflow_fds_.resize(prev_size + num_wire_fds);
    338        memcpy(&input_overflow_fds_[prev_size], wire_fds,
    339               num_wire_fds * sizeof(int));
    340      }
    341      fds = &input_overflow_fds_[0];
    342      num_fds = input_overflow_fds_.size();
    343    }
    344 
    345    // The data for the message we're currently reading consists of any data
    346    // stored in incoming_message_ followed by data in input_buf_ (followed by
    347    // other messages).
    348 
    349    // NOTE: We re-check `pipe_` after each message to make sure we weren't
    350    // closed while calling `OnMessageReceived` or `OnChannelConnected`.
    351    while (p < end && pipe_ != -1) {
    352      // Try to figure out how big the message is. Size is 0 if we haven't read
    353      // enough of the header to know the size.
    354      uint32_t message_length = 0;
    355      if (incoming_message_) {
    356        message_length = incoming_message_->size();
    357      } else {
    358        message_length = Message::MessageSize(p, end);
    359      }
    360 
    361      if (!message_length) {
    362        // We haven't seen the full message header.
    363        MOZ_ASSERT(!incoming_message_);
    364 
    365        // Move everything we have to the start of the buffer. We'll finish
    366        // reading this message when we get more data. For now we leave it in
    367        // input_buf_.
    368        memmove(input_buf_.get(), p, end - p);
    369        input_buf_offset_ = end - p;
    370 
    371        break;
    372      }
    373 
    374      input_buf_offset_ = 0;
    375 
    376      bool partial;
    377      if (incoming_message_) {
    378        // We already have some data for this message stored in
    379        // incoming_message_. We want to append the new data there.
    380        Message& m = *incoming_message_;
    381 
    382        // How much data from this message remains to be added to
    383        // incoming_message_?
    384        MOZ_DIAGNOSTIC_ASSERT(message_length > m.CurrentSize());
    385        uint32_t remaining = message_length - m.CurrentSize();
    386 
    387        // How much data from this message is stored in input_buf_?
    388        uint32_t in_buf = std::min(remaining, uint32_t(end - p));
    389 
    390        m.InputBytes(p, in_buf);
    391        p += in_buf;
    392 
    393        // Are we done reading this message?
    394        partial = in_buf != remaining;
    395      } else {
    396        // How much data from this message is stored in input_buf_?
    397        uint32_t in_buf = std::min(message_length, uint32_t(end - p));
    398 
    399        incoming_message_ = mozilla::MakeUnique<Message>(p, in_buf);
    400        p += in_buf;
    401 
    402        // Are we done reading this message?
    403        partial = in_buf != message_length;
    404      }
    405 
    406      if (partial) {
    407        break;
    408      }
    409 
    410      Message& m = *incoming_message_;
    411 
    412      if (m.header()->num_handles) {
    413        // the message has file descriptors
    414        const char* error = NULL;
    415        if (m.header()->num_handles > num_fds - fds_i) {
    416          // the message has been completely received, but we didn't get
    417          // enough file descriptors.
    418          error = "Message needs unreceived descriptors";
    419        }
    420 
    421        size_t maxHandles = std::min<size_t>(
    422            m.size(), IPC::Message::MAX_DESCRIPTORS_PER_MESSAGE);
    423        if (m.header()->num_handles > maxHandles) {
    424          // There are too many descriptors in this message
    425          error = "Message requires an excessive number of descriptors";
    426        }
    427 
    428        if (error) {
    429          CHROMIUM_LOG(WARNING)
    430              << error << " channel:" << this << " message-type:" << m.type()
    431              << " header()->num_handles:" << m.header()->num_handles
    432              << " num_fds:" << num_fds << " fds_i:" << fds_i;
    433          // close the existing file descriptors so that we don't leak them
    434          for (unsigned i = fds_i; i < num_fds; ++i)
    435            IGNORE_EINTR(close(fds[i]));
    436          input_overflow_fds_.clear();
    437          // abort the connection
    438          return false;
    439        }
    440 
    441 #if defined(XP_DARWIN)
    442        // Send a message to the other side, indicating that we are now
    443        // responsible for closing the descriptor.
    444        auto fdAck = mozilla::MakeUnique<Message>(MSG_ROUTING_NONE,
    445                                                  RECEIVED_FDS_MESSAGE_TYPE);
    446        DCHECK(m.fd_cookie() != 0);
    447        fdAck->set_fd_cookie(m.fd_cookie());
    448        {
    449          mozilla::MutexAutoLock lock(SendMutex());
    450          OutputQueuePush(std::move(fdAck));
    451        }
    452 #endif
    453 
    454        nsTArray<mozilla::UniqueFileHandle> handles(m.header()->num_handles);
    455        for (unsigned end_i = fds_i + m.header()->num_handles; fds_i < end_i;
    456             ++fds_i) {
    457          handles.AppendElement(mozilla::UniqueFileHandle(fds[fds_i]));
    458        }
    459        m.SetAttachedFileHandles(std::move(handles));
    460      }
    461 
    462      // Note: We set other_pid_ below when we receive a Hello message (which
    463      // has no routing ID), but we only emit a profiler marker for messages
    464      // with a routing ID, so there's no conflict here.
    465      AddIPCProfilerMarker(m, other_pid_, MessageDirection::eReceiving,
    466                           MessagePhase::TransferEnd);
    467 
    468 #ifdef IPC_MESSAGE_DEBUG_EXTRA
    469      DLOG(INFO) << "received message on channel @" << this << " with type "
    470                 << m.type();
    471 #endif
    472 
    473      if (m.routing_id() == MSG_ROUTING_NONE &&
    474          m.type() == HELLO_MESSAGE_TYPE) {
    475        // The Hello message contains only the process id.
    476        int32_t other_pid = MessageIterator(m).NextInt();
    477        SetOtherPid(other_pid);
    478        listener_->OnChannelConnected(other_pid);
    479 #if defined(XP_DARWIN)
    480      } else if (m.routing_id() == MSG_ROUTING_NONE &&
    481                 m.type() == RECEIVED_FDS_MESSAGE_TYPE) {
    482        DCHECK(m.fd_cookie() != 0);
    483        CloseDescriptors(m.fd_cookie());
    484 #endif
    485      } else {
    486        mozilla::LogIPCMessage::Run run(&m);
    487 #if defined(XP_DARWIN)
    488        if (!AcceptMachPorts(m)) {
    489          return false;
    490        }
    491 #endif
    492        listener_->OnMessageReceived(std::move(incoming_message_));
    493      }
    494 
    495      incoming_message_ = nullptr;
    496    }
    497 
    498    input_overflow_fds_ = std::vector<int>(&fds[fds_i], &fds[num_fds]);
    499 
    500    // When the input data buffer is empty, the overflow fds should be too. If
    501    // this is not the case, we probably have a rogue renderer which is trying
    502    // to fill our descriptor table.
    503    if (!incoming_message_ && input_buf_offset_ == 0 &&
    504        !input_overflow_fds_.empty()) {
    505      // We close these descriptors in Close()
    506      return false;
    507    }
    508  }
    509 }
    510 
    511 bool ChannelPosix::ProcessOutgoingMessages() {
    512  // NOTE: This method may be called on threads other than `IOThread()`.
    513  chan_cap_.NoteLockHeld();
    514 
    515  DCHECK(!waiting_connect_);  // Why are we trying to send messages if there's
    516                              // no connection?
    517  is_blocked_on_write_ = false;
    518 
    519  if (output_queue_.IsEmpty()) return true;
    520 
    521  if (pipe_ == -1) return false;
    522 
    523  // Write out all the messages we can till the write blocks or there are no
    524  // more outgoing messages.
    525  while (!output_queue_.IsEmpty()) {
    526    Message* msg = output_queue_.FirstElement().get();
    527 
    528    struct msghdr msgh = {0};
    529 
    530    char cmsgBuf[kControlBufferSize];
    531 
    532    if (partial_write_.isNothing()) {
    533 #if defined(XP_DARWIN)
    534      if (!TransferMachPorts(*msg)) {
    535        return false;
    536      }
    537 #endif
    538 
    539      size_t maxHandles = std::min<size_t>(
    540          msg->size(), IPC::Message::MAX_DESCRIPTORS_PER_MESSAGE);
    541      if (msg->attached_handles_.Length() > maxHandles) {
    542        MOZ_DIAGNOSTIC_CRASH("Too many file descriptors!");
    543        CHROMIUM_LOG(FATAL) << "Too many file descriptors!";
    544        // This should not be reached.
    545        return false;
    546      }
    547 
    548      msg->header()->num_handles = msg->attached_handles_.Length();
    549 #if defined(XP_DARWIN)
    550      if (!msg->attached_handles_.IsEmpty()) {
    551        msg->set_fd_cookie(++last_pending_fd_id_);
    552      }
    553 #endif
    554 
    555      Pickle::BufferList::IterImpl iter(msg->Buffers());
    556      MOZ_DIAGNOSTIC_ASSERT(!iter.Done(), "empty message");
    557      partial_write_.emplace(PartialWrite{iter, msg->attached_handles_});
    558 
    559      AddIPCProfilerMarker(*msg, other_pid_, MessageDirection::eSending,
    560                           MessagePhase::TransferStart);
    561    }
    562 
    563    if (partial_write_->iter_.Done()) {
    564      MOZ_DIAGNOSTIC_CRASH("partial_write_->iter_ should not be done");
    565      // report a send error to our caller, which will close the channel.
    566      return false;
    567    }
    568 
    569    // How much of this message have we written so far?
    570    Pickle::BufferList::IterImpl iter = partial_write_->iter_;
    571    auto handles = partial_write_->handles_;
    572 
    573    // Serialize attached file descriptors into the cmsg header. Only up to
    574    // kControlBufferMaxFds can be serialized at once, so messages with more
    575    // attachments must be sent over multiple `sendmsg` calls.
    576    const size_t num_fds = std::min(handles.Length(), kControlBufferMaxFds);
    577    size_t max_amt_to_write = iter.TotalBytesAvailable(msg->Buffers());
    578    if (num_fds > 0) {
    579      msgh.msg_control = cmsgBuf;
    580      msgh.msg_controllen = CMSG_LEN(sizeof(int) * num_fds);
    581      struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msgh);
    582      cmsg->cmsg_level = SOL_SOCKET;
    583      cmsg->cmsg_type = SCM_RIGHTS;
    584      cmsg->cmsg_len = msgh.msg_controllen;
    585      for (size_t i = 0; i < num_fds; ++i) {
    586        reinterpret_cast<int*>(CMSG_DATA(cmsg))[i] = handles[i].get();
    587      }
    588 
    589      // Avoid writing one byte per remaining handle in excess of
    590      // kControlBufferMaxFds.  Each handle written will consume a minimum of 4
    591      // bytes in the message (to store it's index), so we can depend on there
    592      // being enough data to send every handle.
    593      size_t remaining = handles.Length() - num_fds;
    594      MOZ_ASSERT(max_amt_to_write > remaining,
    595                 "must be at least one byte in the message for each handle");
    596      max_amt_to_write -= remaining;
    597    }
    598 
    599    // Store remaining segments to write into iovec.
    600    //
    601    // Don't add more than kMaxIOVecSize iovecs so that we avoid
    602    // OS-dependent limits.  Also, stop adding iovecs if we've already
    603    // prepared to write at least the full buffer size.
    604    struct iovec iov[kMaxIOVecSize];
    605    size_t iov_count = 0;
    606    size_t amt_to_write = 0;
    607    while (!iter.Done() && iov_count < kMaxIOVecSize &&
    608           PipeBufHasSpaceAfter(amt_to_write) &&
    609           amt_to_write < max_amt_to_write) {
    610      char* data = iter.Data();
    611      size_t size =
    612          std::min(iter.RemainingInSegment(), max_amt_to_write - amt_to_write);
    613 
    614      iov[iov_count].iov_base = data;
    615      iov[iov_count].iov_len = size;
    616      iov_count++;
    617      amt_to_write += size;
    618      iter.Advance(msg->Buffers(), size);
    619    }
    620    MOZ_ASSERT(amt_to_write <= max_amt_to_write);
    621    MOZ_ASSERT(amt_to_write > 0);
    622 
    623    const bool intentional_short_write = !iter.Done();
    624    msgh.msg_iov = iov;
    625    msgh.msg_iovlen = iov_count;
    626 
    627    ssize_t bytes_written =
    628        HANDLE_EINTR(corrected_sendmsg(pipe_, &msgh, MSG_DONTWAIT));
    629 
    630    if (bytes_written < 0) {
    631      switch (errno) {
    632        case EAGAIN:
    633          // Not an error; the sendmsg would have blocked, so return to the
    634          // event loop and try again later.
    635          break;
    636 #if defined(XP_DARWIN) || defined(XP_NETBSD)
    637          // (Note: this comment is copied from https://crrev.com/86c3d9ef4fdf6;
    638          // see also bug 1142693 comment #73.)
    639          //
    640          // On OS X if sendmsg() is trying to send fds between processes and
    641          // there isn't enough room in the output buffer to send the fd
    642          // structure over atomically then EMSGSIZE is returned. The same
    643          // applies to NetBSD as well.
    644          //
    645          // EMSGSIZE presents a problem since the system APIs can only call us
    646          // when there's room in the socket buffer and not when there is
    647          // "enough" room.
    648          //
    649          // The current behavior is to return to the event loop when EMSGSIZE
    650          // is received and hopefull service another FD.  This is however still
    651          // technically a busy wait since the event loop will call us right
    652          // back until the receiver has read enough data to allow passing the
    653          // FD over atomically.
    654        case EMSGSIZE:
    655          // Because this is likely to result in a busy-wait, we'll try to make
    656          // it easier for the receiver to make progress, but only if we're on
    657          // the I/O thread already.
    658          if (IOThread().IsOnCurrentThread()) {
    659            sched_yield();
    660          }
    661          break;
    662 #endif
    663        default:
    664          if (!ErrorIsBrokenPipe(errno)) {
    665            CHROMIUM_LOG(ERROR) << "pipe error: " << strerror(errno);
    666          }
    667          return false;
    668      }
    669    }
    670 
    671    if (intentional_short_write ||
    672        static_cast<size_t>(bytes_written) != amt_to_write) {
    673      // If write() fails with EAGAIN or EMSGSIZE then bytes_written will be -1.
    674      if (bytes_written > 0) {
    675        MOZ_DIAGNOSTIC_ASSERT(intentional_short_write ||
    676                              static_cast<size_t>(bytes_written) <
    677                                  amt_to_write);
    678        partial_write_->iter_.AdvanceAcrossSegments(msg->Buffers(),
    679                                                    bytes_written);
    680        partial_write_->handles_ = handles.From(num_fds);
    681        // We should not hit the end of the buffer.
    682        MOZ_DIAGNOSTIC_ASSERT(!partial_write_->iter_.Done());
    683      }
    684 
    685      is_blocked_on_write_ = true;
    686      if (IOThread().IsOnCurrentThread()) {
    687        // If we're on the I/O thread already, tell libevent to call us back
    688        // when things are unblocked.
    689        MessageLoopForIO::current()->WatchFileDescriptor(
    690            pipe_,
    691            false,  // One shot
    692            MessageLoopForIO::WATCH_WRITE, &write_watcher_, this);
    693      } else {
    694        // Otherwise, emulate being called back from libevent on the I/O thread,
    695        // which will re-try the write, and then potentially start watching if
    696        // still necessary.
    697        IOThread().Dispatch(mozilla::NewRunnableMethod<int>(
    698            "ChannelPosix::ContinueProcessOutgoing", this,
    699            &ChannelPosix::OnFileCanWriteWithoutBlocking, -1));
    700      }
    701      return true;
    702    } else {
    703      MOZ_ASSERT(partial_write_->handles_.Length() == num_fds,
    704                 "not all handles were sent");
    705      partial_write_.reset();
    706 
    707 #if defined(XP_DARWIN)
    708      if (!msg->attached_handles_.IsEmpty()) {
    709        pending_fds_.push_back(PendingDescriptors{
    710            msg->fd_cookie(), std::move(msg->attached_handles_)});
    711      }
    712 #else
    713      if (bytes_written > 0) {
    714        msg->attached_handles_.Clear();
    715      }
    716 #endif
    717 
    718      // Message sent OK!
    719 
    720      AddIPCProfilerMarker(*msg, other_pid_, MessageDirection::eSending,
    721                           MessagePhase::TransferEnd);
    722 
    723 #ifdef IPC_MESSAGE_DEBUG_EXTRA
    724      DLOG(INFO) << "sent message @" << msg << " on channel @" << this
    725                 << " with type " << msg->type();
    726 #endif
    727      OutputQueuePop();
    728      // msg has been destroyed, so clear the dangling reference.
    729      msg = nullptr;
    730    }
    731  }
    732  return true;
    733 }
    734 
    735 bool ChannelPosix::Send(mozilla::UniquePtr<Message> message) {
    736  // NOTE: This method may be called on threads other than `IOThread()`.
    737  mozilla::MutexAutoLock lock(SendMutex());
    738  chan_cap_.NoteLockHeld();
    739 
    740 #ifdef IPC_MESSAGE_DEBUG_EXTRA
    741  DLOG(INFO) << "sending message @" << message.get() << " on channel @" << this
    742             << " with type " << message->type() << " ("
    743             << output_queue_.Count() << " in queue)";
    744 #endif
    745 
    746  // If the channel has been closed, ProcessOutgoingMessages() is never going
    747  // to pop anything off output_queue; output_queue will only get emptied when
    748  // the channel is destructed.  We might as well delete message now, instead
    749  // of waiting for the channel to be destructed.
    750  if (pipe_ == -1) {
    751    if (mozilla::ipc::LoggingEnabled()) {
    752      fprintf(stderr,
    753              "Can't send message %s, because this channel is closed.\n",
    754              message->name());
    755    }
    756    return false;
    757  }
    758 
    759  OutputQueuePush(std::move(message));
    760  if (!waiting_connect_) {
    761    if (!is_blocked_on_write_) {
    762      if (!ProcessOutgoingMessages()) return false;
    763    }
    764  }
    765 
    766  return true;
    767 }
    768 
    769 // Called by libevent when we can read from th pipe without blocking.
    770 void ChannelPosix::OnFileCanReadWithoutBlocking(int fd) {
    771  IOThread().AssertOnCurrentThread();
    772  chan_cap_.NoteOnTarget();
    773 
    774  if (!waiting_connect_ && fd == pipe_ && pipe_ != -1) {
    775    if (!ProcessIncomingMessages()) {
    776      Close();
    777      listener_->OnChannelError();
    778      // The OnChannelError() call may delete this, so we need to exit now.
    779      return;
    780    }
    781  }
    782 }
    783 
    784 #if defined(XP_DARWIN)
    785 void ChannelPosix::CloseDescriptors(uint32_t pending_fd_id) {
    786  mozilla::MutexAutoLock lock(SendMutex());
    787  chan_cap_.NoteExclusiveAccess();
    788 
    789  DCHECK(pending_fd_id != 0);
    790  for (std::list<PendingDescriptors>::iterator i = pending_fds_.begin();
    791       i != pending_fds_.end(); i++) {
    792    if ((*i).id == pending_fd_id) {
    793      pending_fds_.erase(i);
    794      return;
    795    }
    796  }
    797  DCHECK(false) << "pending_fd_id not in our list!";
    798 }
    799 #endif
    800 
    801 void ChannelPosix::OutputQueuePush(mozilla::UniquePtr<Message> msg) {
    802  chan_cap_.NoteLockHeld();
    803 
    804  mozilla::LogIPCMessage::LogDispatchWithPid(msg.get(), other_pid_);
    805 
    806  MOZ_DIAGNOSTIC_ASSERT(pipe_ != -1);
    807  msg->AssertAsLargeAsHeader();
    808  output_queue_.Push(std::move(msg));
    809 }
    810 
    811 void ChannelPosix::OutputQueuePop() {
    812  // Clear any reference to the front of output_queue_ before we destroy it.
    813  partial_write_.reset();
    814 
    815  mozilla::UniquePtr<Message> message = output_queue_.Pop();
    816 }
    817 
    818 // Called by libevent when we can write to the pipe without blocking.
    819 void ChannelPosix::OnFileCanWriteWithoutBlocking(int fd) {
    820  RefPtr<ChannelPosix> grip(this);
    821  IOThread().AssertOnCurrentThread();
    822  mozilla::ReleasableMutexAutoLock lock(SendMutex());
    823  chan_cap_.NoteExclusiveAccess();
    824  if (pipe_ != -1 && !ProcessOutgoingMessages()) {
    825    CloseLocked();
    826    lock.Unlock();
    827    listener_->OnChannelError();
    828  }
    829 }
    830 
    831 void ChannelPosix::Close() {
    832  IOThread().AssertOnCurrentThread();
    833  mozilla::MutexAutoLock lock(SendMutex());
    834  CloseLocked();
    835 }
    836 
    837 void ChannelPosix::CloseLocked() {
    838  chan_cap_.NoteExclusiveAccess();
    839 
    840  // Close can be called multiple times, so we need to make sure we're
    841  // idempotent.
    842 
    843  // Unregister libevent for the FIFO and close it.
    844  read_watcher_.StopWatchingFileDescriptor();
    845  write_watcher_.StopWatchingFileDescriptor();
    846  if (pipe_ != -1) {
    847    IGNORE_EINTR(close(pipe_));
    848    SetPipe(-1);
    849  }
    850 
    851  while (!output_queue_.IsEmpty()) {
    852    OutputQueuePop();
    853  }
    854 
    855  // Close any outstanding, received file descriptors
    856  for (std::vector<int>::iterator i = input_overflow_fds_.begin();
    857       i != input_overflow_fds_.end(); ++i) {
    858    IGNORE_EINTR(close(*i));
    859  }
    860  input_overflow_fds_.clear();
    861 
    862 #if defined(XP_DARWIN)
    863  pending_fds_.clear();
    864 
    865  other_task_ = nullptr;
    866 #endif
    867 }
    868 
    869 #if defined(XP_DARWIN)
    870 void ChannelPosix::SetOtherMachTask(task_t task) {
    871  IOThread().AssertOnCurrentThread();
    872  mozilla::MutexAutoLock lock(SendMutex());
    873  chan_cap_.NoteExclusiveAccess();
    874 
    875  if (NS_WARN_IF(pipe_ == -1)) {
    876    return;
    877  }
    878 
    879  MOZ_ASSERT(mode_ == MODE_BROKER_SERVER && waiting_connect_);
    880  other_task_ = mozilla::RetainMachSendRight(task);
    881  // Now that `other_task_` is provided, we can continue connecting.
    882  ContinueConnect();
    883 }
    884 
    885 //------------------------------------------------------------------------------
    886 // Mach port transferring logic
    887 //
    888 // It is currently not possible to directly transfer a mach send right between
    889 // two content processes using SCM_RIGHTS, unlike how we can handle file
    890 // descriptors. This means that mach ports need to be transferred through a
    891 // separate mechanism. This file only implements support for transferring mach
    892 // ports between a (potentially sandboxed) child process and the parent process.
    893 // Support for transferring mach ports between other process pairs is handled by
    894 // `NodeController`, which is responsible for relaying messages which carry
    895 // handles via the parent process.
    896 //
    897 // The logic which we use for doing this is based on the following from
    898 // Chromium, which pioneered this technique. As of this writing, chromium no
    899 // longer uses this strategy, as all IPC messages are sent using mach ports on
    900 // macOS.
    901 // https://source.chromium.org/chromium/chromium/src/+/9f707e5e04598d8303fa99ca29eb507c839767d8:mojo/core/mach_port_relay.cc
    902 // https://source.chromium.org/chromium/chromium/src/+/9f707e5e04598d8303fa99ca29eb507c839767d8:base/mac/mach_port_util.cc.
    903 //
    904 // As we only need to consider messages between the privileged (parent) and
    905 // unprivileged (child) processes in this code, there are 2 relevant cases which
    906 // we need to handle:
    907 //
    908 // # Unprivileged (child) to Privileged (parent)
    909 //
    910 // As the privileged process has access to the unprivileged process' `task_t`,
    911 // it is possible to directly extract the mach port from the target process'
    912 // address space, given its name, using `mach_port_extract_right`.
    913 //
    914 // To transfer the port, the unprivileged process will leak a reference to the
    915 // send right, and include the port's name in the message footer. The privileged
    916 // process will extract that port right (and drop the reference in the old
    917 // process) using `mach_port_extract_right` with `MACH_MSG_TYPE_MOVE_SEND`. The
    918 // call to `mach_port_extract_right` is handled by `BrokerExtractSendRight`
    919 //
    920 // # Privileged (parent) to Unprivileged (child)
    921 //
    922 // Unfortunately, the process of transferring a right into a target process is
    923 // more complex.  The only well-supported way to transfer a right into a process
    924 // is by sending it with `mach_msg`, and receiving it on the other side [1].
    925 //
    926 // To work around this, the privileged process uses `mach_port_allocate` to
    927 // create a new receive right in the target process using its `task_t`, and
    928 // `mach_port_extract_right` to extract a send-once right to that port. It then
    929 // sends a message to the port with port we're intending to send as an
    930 // attachment. This is handled by `BrokerTransferSendRight`, which returns the
    931 // name of the newly created receive right in the target process to be sent in
    932 // the message footer.
    933 //
    934 // In the unprivileged process, `mach_msg` is used to receive a single message
    935 // from the receive right, which will have the actual port we were trying to
    936 // transfer as an attachment. This is handled by the `MachReceivePortSendRight`
    937 // function.
    938 //
    939 // [1] We cannot use `mach_port_insert_right` to transfer the right into the
    940 // target process, as that method requires explicitly specifying the remote
    941 // port's name, and we do not control the port name allocator.
    942 
    943 // Extract a send right from the given peer task. A reference to the remote
    944 // right will be dropped.  See comment above for details.
    945 static mozilla::UniqueMachSendRight BrokerExtractSendRight(
    946    task_t task, mach_port_name_t name) {
    947  mach_port_t extractedRight = MACH_PORT_NULL;
    948  mach_msg_type_name_t extractedRightType;
    949  kern_return_t kr =
    950      mach_port_extract_right(task, name, MACH_MSG_TYPE_MOVE_SEND,
    951                              &extractedRight, &extractedRightType);
    952  if (kr != KERN_SUCCESS) {
    953    CHROMIUM_LOG(ERROR) << "failed to extract port right from other process. "
    954                        << mach_error_string(kr);
    955    return nullptr;
    956  }
    957  MOZ_ASSERT(extractedRightType == MACH_MSG_TYPE_PORT_SEND,
    958             "We asked the OS for a send port");
    959  return mozilla::UniqueMachSendRight(extractedRight);
    960 }
    961 
    962 // Transfer a send right to the given peer task. The name of a receive right in
    963 // the remote process will be returned if successful. The sent port can be
    964 // obtained from that port in the peer task using `MachReceivePortSendRight`.
    965 // See comment above for details.
    966 static mozilla::Maybe<mach_port_name_t> BrokerTransferSendRight(
    967    task_t task, mozilla::UniqueMachSendRight port_to_send) {
    968  mach_port_name_t endpoint;
    969  kern_return_t kr =
    970      mach_port_allocate(task, MACH_PORT_RIGHT_RECEIVE, &endpoint);
    971  if (kr != KERN_SUCCESS) {
    972    CHROMIUM_LOG(ERROR)
    973        << "Unable to create receive right in TransferMachPorts. "
    974        << mach_error_string(kr);
    975    return mozilla::Nothing();
    976  }
    977 
    978  // Clean up the endpoint on error.
    979  auto destroyEndpoint =
    980      mozilla::MakeScopeExit([&] { mach_port_deallocate(task, endpoint); });
    981 
    982  // Change its message queue limit so that it accepts one message.
    983  mach_port_limits limits = {};
    984  limits.mpl_qlimit = 1;
    985  kr = mach_port_set_attributes(task, endpoint, MACH_PORT_LIMITS_INFO,
    986                                reinterpret_cast<mach_port_info_t>(&limits),
    987                                MACH_PORT_LIMITS_INFO_COUNT);
    988  if (kr != KERN_SUCCESS) {
    989    CHROMIUM_LOG(ERROR)
    990        << "Unable configure receive right in TransferMachPorts. "
    991        << mach_error_string(kr);
    992    return mozilla::Nothing();
    993  }
    994 
    995  // Get a send right.
    996  mach_port_t send_once_right;
    997  mach_msg_type_name_t send_right_type;
    998  kr = mach_port_extract_right(task, endpoint, MACH_MSG_TYPE_MAKE_SEND_ONCE,
    999                               &send_once_right, &send_right_type);
   1000  if (kr != KERN_SUCCESS) {
   1001    CHROMIUM_LOG(ERROR) << "Unable extract send right in TransferMachPorts. "
   1002                        << mach_error_string(kr);
   1003    return mozilla::Nothing();
   1004  }
   1005  MOZ_ASSERT(MACH_MSG_TYPE_PORT_SEND_ONCE == send_right_type);
   1006 
   1007  kr = MachSendPortSendRight(send_once_right, port_to_send.get(),
   1008                             mozilla::Some(0), MACH_MSG_TYPE_MOVE_SEND_ONCE);
   1009  if (kr != KERN_SUCCESS) {
   1010    // This right will be destroyed due to being a SEND_ONCE right if we
   1011    // succeed.
   1012    mach_port_deallocate(mach_task_self(), send_once_right);
   1013    CHROMIUM_LOG(ERROR) << "Unable to transfer right in TransferMachPorts. "
   1014                        << mach_error_string(kr);
   1015    return mozilla::Nothing();
   1016  }
   1017 
   1018  destroyEndpoint.release();
   1019  return mozilla::Some(endpoint);
   1020 }
   1021 
   1022 // Process footer information attached to the message, and acquire owning
   1023 // references to any transferred mach ports. See comment above for details.
   1024 bool ChannelPosix::AcceptMachPorts(Message& msg) {
   1025  chan_cap_.NoteOnTarget();
   1026 
   1027  uint32_t num_send_rights = msg.header()->num_send_rights;
   1028  if (num_send_rights == 0) {
   1029    return true;
   1030  }
   1031 
   1032  // Read in the payload from the footer, truncating the message.
   1033  nsTArray<uint32_t> payload;
   1034  payload.AppendElements(num_send_rights);
   1035  if (!msg.ReadFooter(payload.Elements(), num_send_rights * sizeof(uint32_t),
   1036                      /* truncate */ true)) {
   1037    CHROMIUM_LOG(ERROR) << "failed to read mach port payload from message";
   1038    return false;
   1039  }
   1040  msg.header()->num_send_rights = 0;
   1041 
   1042  // Read in the handles themselves, transferring ownership as required.
   1043  nsTArray<mozilla::UniqueMachSendRight> rights(num_send_rights);
   1044  for (uint32_t name : payload) {
   1045    mozilla::UniqueMachSendRight right;
   1046    switch (mode_) {
   1047      case MODE_BROKER_SERVER:
   1048        if (!other_task_) {
   1049          CHROMIUM_LOG(ERROR) << "other_task_ is invalid in AcceptMachPorts";
   1050          return false;
   1051        }
   1052        right = BrokerExtractSendRight(other_task_.get(), name);
   1053        break;
   1054      case MODE_BROKER_CLIENT: {
   1055        kern_return_t kr = MachReceivePortSendRight(
   1056            mozilla::UniqueMachReceiveRight(name), mozilla::Some(0), &right);
   1057        if (kr != KERN_SUCCESS) {
   1058          CHROMIUM_LOG(ERROR)
   1059              << "failed to receive mach send right. " << mach_error_string(kr);
   1060          return false;
   1061        }
   1062        break;
   1063      }
   1064      default:
   1065        CHROMIUM_LOG(ERROR)
   1066            << "invalid message: " << msg.name()
   1067            << ". channel is not configured to accept mach ports";
   1068        return false;
   1069    }
   1070    if (!right) {
   1071      return false;
   1072    }
   1073    rights.AppendElement(std::move(right));
   1074  }
   1075 
   1076  // We're done with the handle footer, truncate the message at that point.
   1077  msg.attached_send_rights_ = std::move(rights);
   1078  MOZ_ASSERT(msg.num_send_rights() == num_send_rights);
   1079  return true;
   1080 }
   1081 
   1082 // Transfer ownership of any attached mach ports to the peer task, and add the
   1083 // required information for AcceptMachPorts to the message footer. See comment
   1084 // above for details.
   1085 bool ChannelPosix::TransferMachPorts(Message& msg) {
   1086  uint32_t num_receive_rights = msg.num_receive_rights();
   1087  if (num_receive_rights != 0) {
   1088    CHROMIUM_LOG(ERROR) << "ChannelPosix does not support receive rights";
   1089    return false;
   1090  }
   1091 
   1092  uint32_t num_send_rights = msg.num_send_rights();
   1093  if (num_send_rights == 0) {
   1094    return true;
   1095  }
   1096 
   1097 #  ifdef DEBUG
   1098  uint32_t rights_offset = msg.header()->payload_size;
   1099 #  endif
   1100 
   1101  nsTArray<uint32_t> payload(num_send_rights);
   1102  for (auto& port_to_send : msg.attached_send_rights_) {
   1103    switch (mode_) {
   1104      case MODE_BROKER_SERVER: {
   1105        if (!other_task_) {
   1106          CHROMIUM_LOG(ERROR) << "other_task_ is invalid in TransferMachPorts";
   1107          return false;
   1108        }
   1109        mozilla::Maybe<mach_port_name_t> endpoint =
   1110            BrokerTransferSendRight(other_task_.get(), std::move(port_to_send));
   1111        if (!endpoint) {
   1112          return false;
   1113        }
   1114        payload.AppendElement(*endpoint);
   1115        break;
   1116      }
   1117      case MODE_BROKER_CLIENT:
   1118        payload.AppendElement(port_to_send.release());
   1119        break;
   1120      default:
   1121        CHROMIUM_LOG(ERROR)
   1122            << "cannot send message: " << msg.name()
   1123            << ". channel is not configured to accept mach ports";
   1124        return false;
   1125    }
   1126  }
   1127  msg.attached_send_rights_.Clear();
   1128 
   1129  msg.WriteFooter(payload.Elements(), payload.Length() * sizeof(uint32_t));
   1130  msg.header()->num_send_rights = num_send_rights;
   1131 
   1132  MOZ_ASSERT(msg.header()->payload_size ==
   1133                 rights_offset + (sizeof(uint32_t) * num_send_rights),
   1134             "Unexpected number of bytes written for send rights footer?");
   1135  return true;
   1136 }
   1137 #endif
   1138 
   1139 // static
   1140 bool ChannelPosix::CreateRawPipe(ChannelHandle* server, ChannelHandle* client) {
   1141  int fds[2];
   1142  if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) < 0) {
   1143    mozilla::ipc::AnnotateCrashReportWithErrno(
   1144        CrashReporter::Annotation::IpcCreatePipeSocketPairErrno, errno);
   1145    return false;
   1146  }
   1147 
   1148  auto configureFd = [](int fd) -> bool {
   1149    // Mark the endpoints as non-blocking
   1150    if (fcntl(fd, F_SETFL, O_NONBLOCK) == -1) {
   1151      mozilla::ipc::AnnotateCrashReportWithErrno(
   1152          CrashReporter::Annotation::IpcCreatePipeFcntlErrno, errno);
   1153      return false;
   1154    }
   1155 
   1156    // Mark the pipes as FD_CLOEXEC
   1157    int flags = fcntl(fd, F_GETFD);
   1158    if (flags == -1) {
   1159      mozilla::ipc::AnnotateCrashReportWithErrno(
   1160          CrashReporter::Annotation::IpcCreatePipeCloExecErrno, errno);
   1161      return false;
   1162    }
   1163    flags |= FD_CLOEXEC;
   1164    if (fcntl(fd, F_SETFD, flags) == -1) {
   1165      mozilla::ipc::AnnotateCrashReportWithErrno(
   1166          CrashReporter::Annotation::IpcCreatePipeCloExecErrno, errno);
   1167      return false;
   1168    }
   1169    return true;
   1170  };
   1171 
   1172  if (!configureFd(fds[0]) || !configureFd(fds[1])) {
   1173    IGNORE_EINTR(close(fds[0]));
   1174    IGNORE_EINTR(close(fds[1]));
   1175    return false;
   1176  }
   1177 
   1178  server->emplace<mozilla::UniqueFileHandle>(fds[0]);
   1179  client->emplace<mozilla::UniqueFileHandle>(fds[1]);
   1180  return true;
   1181 }
   1182 
   1183 // static
   1184 uint32_t ChannelPosix::NumRelayedAttachments(const Message& message) {
   1185 #ifdef XP_DARWIN
   1186  return message.num_send_rights();
   1187 #else
   1188  return 0;
   1189 #endif
   1190 }
   1191 
   1192 // static
   1193 bool ChannelPosix::IsValidHandle(const ChannelHandle& handle) {
   1194  const auto* fileHandle = std::get_if<mozilla::UniqueFileHandle>(&handle);
   1195  return fileHandle && *fileHandle;
   1196 }
   1197 
   1198 }  // namespace IPC