tor-browser

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

MiniTransceiver.cpp (7778B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
      2 /* vim: set ts=8 sts=4 et sw=4 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 #include "mozilla/ipc/MiniTransceiver.h"
      7 #include "chrome/common/ipc_message.h"
      8 #include "chrome/common/ipc_message_utils.h"
      9 #include "base/eintr_wrapper.h"
     10 #include "mozilla/UniquePtr.h"
     11 #include "mozilla/DebugOnly.h"
     12 #include "mozilla/Sprintf.h"
     13 #include "mozilla/ScopeExit.h"
     14 #include "nsDebug.h"
     15 
     16 #include <sys/types.h>
     17 #include <sys/socket.h>
     18 #include <string.h>
     19 #include <errno.h>
     20 
     21 namespace mozilla::ipc {
     22 
     23 static const size_t kMaxIOVecSize = 64;
     24 static const size_t kMaxDataSize = 8 * 1024;
     25 static const size_t kMaxNumFds = 16;
     26 
     27 MiniTransceiver::MiniTransceiver(int aFd, DataBufferClear aDataBufClear)
     28    : mFd(aFd),
     29 #ifdef DEBUG
     30      mState(STATE_NONE),
     31 #endif
     32      mDataBufClear(aDataBufClear) {
     33 }
     34 
     35 namespace {
     36 
     37 /**
     38 * Initialize the IO vector for sending data and the control buffer for sending
     39 * FDs.
     40 */
     41 static void InitMsgHdr(msghdr* aHdr, int aIOVSize, size_t aMaxNumFds) {
     42  aHdr->msg_name = nullptr;
     43  aHdr->msg_namelen = 0;
     44  aHdr->msg_flags = 0;
     45 
     46  // Prepare the IO vector to receive the content of message.
     47  auto* iov = new iovec[aIOVSize];
     48  aHdr->msg_iov = iov;
     49  aHdr->msg_iovlen = aIOVSize;
     50 
     51  // Prepare the control buffer to receive file descriptors.
     52  const size_t cbufSize = CMSG_SPACE(sizeof(int) * aMaxNumFds);
     53  auto* cbuf = new char[cbufSize];
     54  // Avoid valgrind complaints about uninitialized padding (but also,
     55  // fill with a value that isn't a valid fd, just in case).
     56  memset(cbuf, 255, cbufSize);
     57  aHdr->msg_control = cbuf;
     58  aHdr->msg_controllen = cbufSize;
     59 }
     60 
     61 /**
     62 * Delete resources allocated by InitMsgHdr().
     63 */
     64 static void DeinitMsgHdr(msghdr* aHdr) {
     65  delete aHdr->msg_iov;
     66  delete static_cast<char*>(aHdr->msg_control);
     67 }
     68 
     69 }  // namespace
     70 
     71 void MiniTransceiver::PrepareFDs(msghdr* aHdr, IPC::Message& aMsg) {
     72  // Set control buffer to send file descriptors of the Message.
     73  size_t num_fds = aMsg.attached_handles_.Length();
     74 
     75  cmsghdr* cmsg = CMSG_FIRSTHDR(aHdr);
     76  cmsg->cmsg_level = SOL_SOCKET;
     77  cmsg->cmsg_type = SCM_RIGHTS;
     78  cmsg->cmsg_len = CMSG_LEN(sizeof(int) * num_fds);
     79  for (size_t i = 0; i < num_fds; ++i) {
     80    reinterpret_cast<int*>(CMSG_DATA(cmsg))[i] =
     81        aMsg.attached_handles_[i].get();
     82  }
     83 
     84  // This number will be sent in the header of the message. So, we
     85  // can check it at the other side.
     86  aMsg.header()->num_handles = num_fds;
     87 }
     88 
     89 size_t MiniTransceiver::PrepareBuffers(msghdr* aHdr, IPC::Message& aMsg) {
     90  // Set iovec to send for all buffers of the Message.
     91  iovec* iov = aHdr->msg_iov;
     92  size_t iovlen = 0;
     93  size_t bytes_to_send = 0;
     94  for (Pickle::BufferList::IterImpl iter(aMsg.Buffers()); !iter.Done();
     95       iter.Advance(aMsg.Buffers(), iter.RemainingInSegment())) {
     96    char* data = iter.Data();
     97    size_t size = iter.RemainingInSegment();
     98    iov[iovlen].iov_base = data;
     99    iov[iovlen].iov_len = size;
    100    iovlen++;
    101    MOZ_ASSERT(iovlen <= kMaxIOVecSize);
    102    bytes_to_send += size;
    103  }
    104  MOZ_ASSERT(bytes_to_send <= kMaxDataSize);
    105  aHdr->msg_iovlen = iovlen;
    106 
    107  return bytes_to_send;
    108 }
    109 
    110 bool MiniTransceiver::Send(IPC::Message& aMsg) {
    111 #ifdef DEBUG
    112  if (mState == STATE_SENDING) {
    113    MOZ_CRASH(
    114        "STATE_SENDING: It violates of request-response and no concurrent "
    115        "rules");
    116  }
    117  mState = STATE_SENDING;
    118 #endif
    119 
    120  auto clean_fdset = MakeScopeExit([&] { aMsg.attached_handles_.Clear(); });
    121 
    122  size_t num_fds = aMsg.attached_handles_.Length();
    123  msghdr hdr{};
    124  InitMsgHdr(&hdr, kMaxIOVecSize, num_fds);
    125 
    126  UniquePtr<msghdr, decltype(&DeinitMsgHdr)> uniq(&hdr, &DeinitMsgHdr);
    127 
    128  PrepareFDs(&hdr, aMsg);
    129  DebugOnly<size_t> bytes_to_send = PrepareBuffers(&hdr, aMsg);
    130 
    131  ssize_t bytes_written = HANDLE_EINTR(sendmsg(mFd, &hdr, 0));
    132 
    133  if (bytes_written < 0) {
    134    char error[128];
    135    SprintfLiteral(error, "sendmsg: %s", strerror(errno));
    136    NS_WARNING(error);
    137    return false;
    138  }
    139  MOZ_ASSERT(bytes_written == (ssize_t)bytes_to_send,
    140             "The message is too big?!");
    141 
    142  return true;
    143 }
    144 
    145 unsigned MiniTransceiver::RecvFDs(msghdr* aHdr, int* aAllFds,
    146                                  unsigned aMaxFds) {
    147  if (aHdr->msg_controllen == 0) {
    148    return 0;
    149  }
    150 
    151  unsigned num_all_fds = 0;
    152  for (cmsghdr* cmsg = CMSG_FIRSTHDR(aHdr); cmsg;
    153       cmsg = CMSG_NXTHDR(aHdr, cmsg)) {
    154    MOZ_ASSERT(cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS,
    155               "Accept only SCM_RIGHTS to receive file descriptors");
    156 
    157    unsigned payload_sz = cmsg->cmsg_len - CMSG_LEN(0);
    158    MOZ_ASSERT(payload_sz % sizeof(int) == 0);
    159 
    160    // Add fds to |aAllFds|
    161    unsigned num_part_fds = payload_sz / sizeof(int);
    162    int* part_fds = reinterpret_cast<int*>(CMSG_DATA(cmsg));
    163    MOZ_ASSERT(num_all_fds + num_part_fds <= aMaxFds);
    164 
    165    memcpy(aAllFds + num_all_fds, part_fds, num_part_fds * sizeof(int));
    166    num_all_fds += num_part_fds;
    167  }
    168  return num_all_fds;
    169 }
    170 
    171 bool MiniTransceiver::RecvData(char* aDataBuf, size_t aBufSize,
    172                               uint32_t* aMsgSize, int* aFdsBuf,
    173                               unsigned aMaxFds, unsigned* aNumFds) {
    174  msghdr hdr;
    175  InitMsgHdr(&hdr, 1, aMaxFds);
    176 
    177  UniquePtr<msghdr, decltype(&DeinitMsgHdr)> uniq(&hdr, &DeinitMsgHdr);
    178 
    179  // The buffer to collect all fds received from the socket.
    180  int* all_fds = aFdsBuf;
    181  unsigned num_all_fds = 0;
    182 
    183  size_t total_readed = 0;
    184  uint32_t msgsz = 0;
    185  while (msgsz == 0 || total_readed < msgsz) {
    186    // Set IO vector with the begin of the unused buffer.
    187    hdr.msg_iov->iov_base = aDataBuf + total_readed;
    188    hdr.msg_iov->iov_len = (msgsz == 0 ? aBufSize : msgsz) - total_readed;
    189 
    190    // Read the socket
    191    ssize_t bytes_readed = HANDLE_EINTR(recvmsg(mFd, &hdr, 0));
    192    if (bytes_readed <= 0) {
    193      // Closed or error!
    194      return false;
    195    }
    196    total_readed += bytes_readed;
    197    MOZ_ASSERT(total_readed <= aBufSize);
    198 
    199    if (msgsz == 0) {
    200      // Parse the size of the message.
    201      // Get 0 if data in the buffer is no enough to get message size.
    202      msgsz = IPC::Message::MessageSize(aDataBuf, aDataBuf + total_readed);
    203    }
    204 
    205    num_all_fds += RecvFDs(&hdr, all_fds + num_all_fds, aMaxFds - num_all_fds);
    206  }
    207 
    208  *aMsgSize = msgsz;
    209  *aNumFds = num_all_fds;
    210  return true;
    211 }
    212 
    213 bool MiniTransceiver::Recv(UniquePtr<IPC::Message>& aMsg) {
    214 #ifdef DEBUG
    215  if (mState == STATE_RECEIVING) {
    216    MOZ_CRASH(
    217        "STATE_RECEIVING: It violates of request-response and no concurrent "
    218        "rules");
    219  }
    220  mState = STATE_RECEIVING;
    221 #endif
    222 
    223  UniquePtr<char[]> databuf = MakeUnique<char[]>(kMaxDataSize);
    224  uint32_t msgsz = 0;
    225  int all_fds[kMaxNumFds];
    226  unsigned num_all_fds = 0;
    227 
    228  if (!RecvData(databuf.get(), kMaxDataSize, &msgsz, all_fds, kMaxDataSize,
    229                &num_all_fds)) {
    230    return false;
    231  }
    232 
    233  // Create Message from databuf & all_fds.
    234  UniquePtr<IPC::Message> msg = MakeUnique<IPC::Message>(databuf.get(), msgsz);
    235  nsTArray<UniqueFileHandle> handles(num_all_fds);
    236  for (unsigned i = 0; i < num_all_fds; ++i) {
    237    handles.AppendElement(UniqueFileHandle(all_fds[i]));
    238  }
    239  msg->SetAttachedFileHandles(std::move(handles));
    240 
    241  if (mDataBufClear == DataBufferClear::AfterReceiving) {
    242    // Avoid content processes from reading the content of
    243    // messages.
    244    memset(databuf.get(), 0, msgsz);
    245  }
    246 
    247  MOZ_ASSERT(msg->header()->num_handles == msg->attached_handles_.Length(),
    248             "The number of file descriptors in the header is different from"
    249             " the number actually received");
    250 
    251  aMsg = std::move(msg);
    252  return true;
    253 }
    254 
    255 }  // namespace mozilla::ipc