tor-browser

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

ipc_channel_win.cc (28247B)


      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) 2006-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_win.h"
      8 
      9 #include <windows.h>
     10 #include <winternl.h>
     11 #include <ntstatus.h>
     12 #include <sstream>
     13 
     14 #include "base/command_line.h"
     15 #include "base/logging.h"
     16 #include "base/process.h"
     17 #include "base/process_util.h"
     18 #include "base/rand_util.h"
     19 #include "base/string_util.h"
     20 #include "base/win_util.h"
     21 #include "chrome/common/chrome_switches.h"
     22 #include "chrome/common/ipc_channel_utils.h"
     23 #include "chrome/common/ipc_message_utils.h"
     24 #include "mozilla/ipc/ProtocolUtils.h"
     25 #include "mozilla/LateWriteChecks.h"
     26 #include "mozilla/RandomNum.h"
     27 #include "nsThreadUtils.h"
     28 
     29 using namespace mozilla::ipc;
     30 
     31 namespace {
     32 
     33 // This logic is borrowed from Chromium's `base/win/win_util.h`. It allows us
     34 // to distinguish pseudo-handle values, such as returned by GetCurrentProcess()
     35 // (-1), GetCurrentThread() (-2), and potentially more. The code there claims
     36 // that fuzzers have found issues up until -12 with DuplicateHandle.
     37 //
     38 // https://source.chromium.org/chromium/chromium/src/+/36dbbf38697dd1e23ef8944bb9e57f6e0b3d41ec:base/win/win_util.h
     39 inline bool IsPseudoHandle(HANDLE handle) {
     40  auto handleValue = static_cast<int32_t>(reinterpret_cast<uintptr_t>(handle));
     41  return -12 <= handleValue && handleValue < 0;
     42 }
     43 
     44 // A real handle is a handle that is not a pseudo-handle. Always preferably use
     45 // this variant over ::DuplicateHandle. Only use stock ::DuplicateHandle if you
     46 // explicitly need the ability to duplicate a pseudo-handle.
     47 inline bool DuplicateRealHandle(HANDLE source_process, HANDLE source_handle,
     48                                HANDLE target_process, LPHANDLE target_handle,
     49                                DWORD desired_access, BOOL inherit_handle,
     50                                DWORD options) {
     51  MOZ_RELEASE_ASSERT(!IsPseudoHandle(source_handle));
     52  return static_cast<bool>(::DuplicateHandle(
     53      source_process, source_handle, target_process, target_handle,
     54      desired_access, inherit_handle, options));
     55 }
     56 
     57 }  // namespace
     58 
     59 namespace IPC {
     60 //------------------------------------------------------------------------------
     61 
     62 ChannelWin::State::State(ChannelWin* channel) {
     63  memset(&context.overlapped, 0, sizeof(context.overlapped));
     64  context.handler = channel;
     65 }
     66 
     67 ChannelWin::State::~State() {
     68  COMPILE_ASSERT(!offsetof(ChannelWin::State, context), starts_with_io_context);
     69 }
     70 
     71 //------------------------------------------------------------------------------
     72 
     73 const Channel::ChannelKind ChannelWin::sKind{
     74    .create_raw_pipe = &ChannelWin::CreateRawPipe,
     75    .num_relayed_attachments = &ChannelWin::NumRelayedAttachments,
     76    .is_valid_handle = &ChannelWin::IsValidHandle,
     77 };
     78 
     79 ChannelWin::ChannelWin(mozilla::UniqueFileHandle pipe, Mode mode,
     80                       base::ProcessId other_pid)
     81    : input_state_(this), output_state_(this), other_pid_(other_pid) {
     82  Init(mode);
     83  MaybeOpenProcessHandle();
     84 
     85  if (!pipe) {
     86    return;
     87  }
     88 
     89  pipe_ = pipe.release();
     90  EnqueueHelloMessage();
     91 }
     92 
     93 void ChannelWin::Init(Mode mode) {
     94  // Verify that we fit in a "quantum-spaced" jemalloc bucket.
     95  static_assert(sizeof(*this) <= 512, "Exceeded expected size class");
     96 
     97  chan_cap_.NoteExclusiveAccess();
     98 
     99  mode_ = mode;
    100  pipe_ = INVALID_HANDLE_VALUE;
    101  waiting_connect_ = true;
    102  processing_incoming_ = false;
    103  input_buf_offset_ = 0;
    104  input_buf_ = mozilla::MakeUnique<char[]>(Channel::kReadBufferSize);
    105  other_process_ = INVALID_HANDLE_VALUE;
    106 }
    107 
    108 void ChannelWin::OutputQueuePush(mozilla::UniquePtr<Message> msg) {
    109  chan_cap_.NoteLockHeld();
    110 
    111  mozilla::LogIPCMessage::LogDispatchWithPid(msg.get(), other_pid_);
    112 
    113  output_queue_.Push(std::move(msg));
    114 }
    115 
    116 void ChannelWin::OutputQueuePop() {
    117  mozilla::UniquePtr<Message> message = output_queue_.Pop();
    118 }
    119 
    120 void ChannelWin::Close() {
    121  IOThread().AssertOnCurrentThread();
    122  mozilla::MutexAutoLock lock(SendMutex());
    123  CloseLocked();
    124 }
    125 
    126 void ChannelWin::CloseLocked() {
    127  chan_cap_.NoteExclusiveAccess();
    128 
    129  // If we still have pending I/O, cancel it. The references inside
    130  // `input_state_` and `output_state_` will keep the buffers alive until they
    131  // complete.
    132  if (input_state_.is_pending || output_state_.is_pending) {
    133    CancelIo(pipe_);
    134  }
    135 
    136  // Closing the handle at this point prevents us from issuing more requests
    137  // form OnIOCompleted().
    138  if (pipe_ != INVALID_HANDLE_VALUE) {
    139    CloseHandle(pipe_);
    140    pipe_ = INVALID_HANDLE_VALUE;
    141  }
    142 
    143  // If we have a connection to the other process, close the handle.
    144  if (other_process_ != INVALID_HANDLE_VALUE) {
    145    CloseHandle(other_process_);
    146    other_process_ = INVALID_HANDLE_VALUE;
    147  }
    148 
    149  // Don't return from `CloseLocked()` until the IO has been completed,
    150  // otherwise the IO thread may exit with outstanding IO, leaking the
    151  // ChannelWin.
    152  //
    153  // It's OK to unlock here, as calls to `Send` from other threads will be
    154  // rejected, due to `pipe_` having been cleared.
    155  while (input_state_.is_pending || output_state_.is_pending) {
    156    mozilla::MutexAutoUnlock unlock(SendMutex());
    157    MessageLoopForIO::current()->WaitForIOCompletion(INFINITE, this);
    158  }
    159 
    160  while (!output_queue_.IsEmpty()) {
    161    OutputQueuePop();
    162  }
    163 }
    164 
    165 bool ChannelWin::Send(mozilla::UniquePtr<Message> message) {
    166  mozilla::MutexAutoLock lock(SendMutex());
    167  chan_cap_.NoteLockHeld();
    168 
    169 #ifdef IPC_MESSAGE_DEBUG_EXTRA
    170  DLOG(INFO) << "sending message @" << message.get() << " on channel @" << this
    171             << " with type " << message->type() << " ("
    172             << output_queue_.Count() << " in queue)";
    173 #endif
    174 
    175  if (pipe_ == INVALID_HANDLE_VALUE) {
    176    if (mozilla::ipc::LoggingEnabled()) {
    177      fprintf(stderr,
    178              "Can't send message %s, because this channel is closed.\n",
    179              message->name());
    180    }
    181    return false;
    182  }
    183 
    184  OutputQueuePush(std::move(message));
    185  // ensure waiting to write
    186  if (!waiting_connect_) {
    187    if (!output_state_.is_pending) {
    188      if (!ProcessOutgoingMessages(NULL, 0, false)) {
    189        return false;
    190      }
    191    }
    192  }
    193 
    194  return true;
    195 }
    196 
    197 bool ChannelWin::EnqueueHelloMessage() {
    198  chan_cap_.NoteExclusiveAccess();
    199 
    200  auto m = mozilla::MakeUnique<Message>(MSG_ROUTING_NONE, HELLO_MESSAGE_TYPE);
    201 
    202  // Also, don't send if the value is zero (for IPC backwards compatability).
    203  if (!m->WriteInt(GetCurrentProcessId())) {
    204    CloseHandle(pipe_);
    205    pipe_ = INVALID_HANDLE_VALUE;
    206    return false;
    207  }
    208 
    209  OutputQueuePush(std::move(m));
    210  return true;
    211 }
    212 
    213 bool ChannelWin::Connect(Listener* listener) {
    214  IOThread().AssertOnCurrentThread();
    215  mozilla::MutexAutoLock lock(SendMutex());
    216  chan_cap_.NoteExclusiveAccess();
    217 
    218  if (pipe_ == INVALID_HANDLE_VALUE) return false;
    219 
    220  listener_ = listener;
    221 
    222  MessageLoopForIO::current()->RegisterIOHandler(pipe_, this);
    223  waiting_connect_ = false;
    224 
    225  DCHECK(!input_state_.is_pending);
    226 
    227  // Complete setup asynchronously. By not setting input_state_.is_pending
    228  // to `this`, we indicate to OnIOCompleted that this is the special
    229  // initialization signal, while keeping a reference through the
    230  // `RunnableMethod`.
    231  IOThread().Dispatch(
    232      mozilla::NewRunnableMethod<MessageLoopForIO::IOContext*, DWORD, DWORD>(
    233          "ContinueConnect", this, &ChannelWin::OnIOCompleted,
    234          &input_state_.context, 0, 0));
    235 
    236  DCHECK(!output_state_.is_pending);
    237  ProcessOutgoingMessages(NULL, 0, false);
    238  return true;
    239 }
    240 
    241 void ChannelWin::SetOtherPid(base::ProcessId other_pid) {
    242  IOThread().AssertOnCurrentThread();
    243  mozilla::MutexAutoLock lock(SendMutex());
    244  chan_cap_.NoteExclusiveAccess();
    245  MOZ_RELEASE_ASSERT(
    246      other_pid_ == base::kInvalidProcessId || other_pid_ == other_pid,
    247      "Multiple sources of SetOtherPid disagree!");
    248  other_pid_ = other_pid;
    249 
    250  MaybeOpenProcessHandle();
    251 }
    252 
    253 void ChannelWin::MaybeOpenProcessHandle() {
    254  chan_cap_.NoteExclusiveAccess();
    255 
    256  // If we know the remote pid and are a broker server, open a privileged handle
    257  // to the child process to transfer handles to/from it.
    258  if (mode_ == MODE_BROKER_SERVER && other_process_ == INVALID_HANDLE_VALUE &&
    259      other_pid_ != base::kInvalidProcessId) {
    260    other_process_ = OpenProcess(PROCESS_DUP_HANDLE, false, other_pid_);
    261    if (!other_process_) {
    262      other_process_ = INVALID_HANDLE_VALUE;
    263      CHROMIUM_LOG(ERROR) << "Failed to acquire privileged handle to "
    264                          << other_pid_ << ", cannot accept handles";
    265    }
    266  }
    267 }
    268 
    269 bool ChannelWin::ProcessIncomingMessages(MessageLoopForIO::IOContext* context,
    270                                         DWORD bytes_read, bool was_pending) {
    271  chan_cap_.NoteOnTarget();
    272 
    273  DCHECK(!input_state_.is_pending);
    274 
    275  if (was_pending) {
    276    DCHECK(context);
    277 
    278    if (!context || !bytes_read) return false;
    279  } else {
    280    // This happens at channel initialization.
    281    DCHECK(!bytes_read && context == &input_state_.context);
    282  }
    283 
    284  for (;;) {
    285    if (bytes_read == 0) {
    286      if (INVALID_HANDLE_VALUE == pipe_) return false;
    287 
    288      // Read from pipe...
    289      BOOL ok = ReadFile(pipe_, input_buf_.get() + input_buf_offset_,
    290                         Channel::kReadBufferSize - input_buf_offset_,
    291                         &bytes_read, &input_state_.context.overlapped);
    292      if (!ok) {
    293        DWORD err = GetLastError();
    294        if (err == ERROR_IO_PENDING) {
    295          input_state_.is_pending = this;
    296          return true;
    297        }
    298        if (err != ERROR_BROKEN_PIPE && err != ERROR_NO_DATA) {
    299          CHROMIUM_LOG(ERROR)
    300              << "pipe error in connection to " << other_pid_ << ": " << err;
    301        }
    302        return false;
    303      }
    304      input_state_.is_pending = this;
    305      return true;
    306    }
    307    DCHECK(bytes_read);
    308 
    309    // Process messages from input buffer.
    310 
    311    const char* p = input_buf_.get();
    312    const char* end = input_buf_.get() + input_buf_offset_ + bytes_read;
    313 
    314    // NOTE: We re-check `pipe_` after each message to make sure we weren't
    315    // closed while calling `OnMessageReceived` or `OnChannelConnected`.
    316    while (p < end && INVALID_HANDLE_VALUE != pipe_) {
    317      // Try to figure out how big the message is. Size is 0 if we haven't read
    318      // enough of the header to know the size.
    319      uint32_t message_length = 0;
    320      if (incoming_message_) {
    321        message_length = incoming_message_->size();
    322      } else {
    323        message_length = Message::MessageSize(p, end);
    324      }
    325 
    326      if (!message_length) {
    327        // We haven't seen the full message header.
    328        MOZ_ASSERT(!incoming_message_);
    329 
    330        // Move everything we have to the start of the buffer. We'll finish
    331        // reading this message when we get more data. For now we leave it in
    332        // input_buf_.
    333        memmove(input_buf_.get(), p, end - p);
    334        input_buf_offset_ = end - p;
    335 
    336        break;
    337      }
    338 
    339      input_buf_offset_ = 0;
    340 
    341      bool partial;
    342      if (incoming_message_) {
    343        // We already have some data for this message stored in
    344        // incoming_message_. We want to append the new data there.
    345        Message& m = *incoming_message_;
    346 
    347        // How much data from this message remains to be added to
    348        // incoming_message_?
    349        MOZ_ASSERT(message_length > m.CurrentSize());
    350        uint32_t remaining = message_length - m.CurrentSize();
    351 
    352        // How much data from this message is stored in input_buf_?
    353        uint32_t in_buf = std::min(remaining, uint32_t(end - p));
    354 
    355        m.InputBytes(p, in_buf);
    356        p += in_buf;
    357 
    358        // Are we done reading this message?
    359        partial = in_buf != remaining;
    360      } else {
    361        // How much data from this message is stored in input_buf_?
    362        uint32_t in_buf = std::min(message_length, uint32_t(end - p));
    363 
    364        incoming_message_ = mozilla::MakeUnique<Message>(p, in_buf);
    365        p += in_buf;
    366 
    367        // Are we done reading this message?
    368        partial = in_buf != message_length;
    369      }
    370 
    371      if (partial) {
    372        break;
    373      }
    374 
    375      Message& m = *incoming_message_;
    376 
    377      // Note: We set other_pid_ below when we receive a Hello message (which
    378      // has no routing ID), but we only emit a profiler marker for messages
    379      // with a routing ID, so there's no conflict here.
    380      AddIPCProfilerMarker(m, other_pid_, MessageDirection::eReceiving,
    381                           MessagePhase::TransferEnd);
    382 
    383 #ifdef IPC_MESSAGE_DEBUG_EXTRA
    384      DLOG(INFO) << "received message on channel @" << this << " with type "
    385                 << m.type();
    386 #endif
    387      if (m.routing_id() == MSG_ROUTING_NONE &&
    388          m.type() == HELLO_MESSAGE_TYPE) {
    389        // The Hello message contains the process id and must include the
    390        // shared secret, if we are waiting for it.
    391        MessageIterator it = MessageIterator(m);
    392        int32_t other_pid = it.NextInt();
    393        SetOtherPid(other_pid);
    394 
    395        listener_->OnChannelConnected(other_pid);
    396      } else {
    397        mozilla::LogIPCMessage::Run run(&m);
    398        if (!AcceptHandles(m)) {
    399          return false;
    400        }
    401        listener_->OnMessageReceived(std::move(incoming_message_));
    402      }
    403 
    404      incoming_message_ = nullptr;
    405    }
    406 
    407    bytes_read = 0;  // Get more data.
    408  }
    409 }
    410 
    411 bool ChannelWin::ProcessOutgoingMessages(MessageLoopForIO::IOContext* context,
    412                                         DWORD bytes_written,
    413                                         bool was_pending) {
    414  chan_cap_.NoteLockHeld();
    415 
    416  DCHECK(!output_state_.is_pending);
    417  DCHECK(!waiting_connect_);  // Why are we trying to send messages if there's
    418                              // no connection?
    419  if (was_pending) {
    420    DCHECK(context);
    421    if (!context || bytes_written == 0) {
    422      DWORD err = GetLastError();
    423      if (err != ERROR_BROKEN_PIPE && err != ERROR_NO_DATA) {
    424        CHROMIUM_LOG(ERROR)
    425            << "pipe error in connection to " << other_pid_ << ": " << err;
    426      }
    427      return false;
    428    }
    429    // Message was sent.
    430    DCHECK(!output_queue_.IsEmpty());
    431    Message* m = output_queue_.FirstElement().get();
    432 
    433    MOZ_RELEASE_ASSERT(partial_write_iter_.isSome());
    434    Pickle::BufferList::IterImpl& iter = partial_write_iter_.ref();
    435    iter.Advance(m->Buffers(), bytes_written);
    436    if (iter.Done()) {
    437      AddIPCProfilerMarker(*m, other_pid_, MessageDirection::eSending,
    438                           MessagePhase::TransferEnd);
    439 
    440      partial_write_iter_.reset();
    441      OutputQueuePop();
    442      // m has been destroyed, so clear the dangling reference.
    443      m = nullptr;
    444    }
    445  }
    446 
    447  if (output_queue_.IsEmpty()) return true;
    448 
    449  if (INVALID_HANDLE_VALUE == pipe_) return false;
    450 
    451  // Write to pipe...
    452  Message* m = output_queue_.FirstElement().get();
    453 
    454  if (partial_write_iter_.isNothing()) {
    455    AddIPCProfilerMarker(*m, other_pid_, MessageDirection::eSending,
    456                         MessagePhase::TransferStart);
    457    if (!TransferHandles(*m)) {
    458      return false;
    459    }
    460    Pickle::BufferList::IterImpl iter(m->Buffers());
    461    partial_write_iter_.emplace(iter);
    462  }
    463 
    464  Pickle::BufferList::IterImpl& iter = partial_write_iter_.ref();
    465 
    466  // Don't count this write for the purposes of late write checking. If this
    467  // message results in a legitimate file write, that will show up when it
    468  // happens.
    469  mozilla::PushSuspendLateWriteChecks();
    470  BOOL ok = WriteFile(pipe_, iter.Data(), iter.RemainingInSegment(),
    471                      &bytes_written, &output_state_.context.overlapped);
    472  mozilla::PopSuspendLateWriteChecks();
    473 
    474  if (!ok) {
    475    DWORD err = GetLastError();
    476    if (err == ERROR_IO_PENDING) {
    477      output_state_.is_pending = this;
    478 
    479 #ifdef IPC_MESSAGE_DEBUG_EXTRA
    480      DLOG(INFO) << "sent pending message @" << m << " on channel @" << this
    481                 << " with type " << m->type();
    482 #endif
    483 
    484      return true;
    485    }
    486    if (err != ERROR_BROKEN_PIPE && err != ERROR_NO_DATA) {
    487      CHROMIUM_LOG(ERROR) << "pipe error in connection to " << other_pid_
    488                          << ": " << err;
    489    }
    490    return false;
    491  }
    492 
    493 #ifdef IPC_MESSAGE_DEBUG_EXTRA
    494  DLOG(INFO) << "sent message @" << m << " on channel @" << this
    495             << " with type " << m->type();
    496 #endif
    497 
    498  output_state_.is_pending = this;
    499  return true;
    500 }
    501 
    502 void ChannelWin::OnIOCompleted(MessageLoopForIO::IOContext* context,
    503                               DWORD bytes_transfered, DWORD error) {
    504  // NOTE: In case the pending reference was the last reference, release it
    505  // outside of the lock.
    506  RefPtr<ChannelWin> was_pending;
    507 
    508  IOThread().AssertOnCurrentThread();
    509  chan_cap_.NoteOnTarget();
    510 
    511  bool ok;
    512  if (context == &input_state_.context) {
    513    was_pending = input_state_.is_pending.forget();
    514    // we don't support recursion through OnMessageReceived yet!
    515    DCHECK(!processing_incoming_);
    516    processing_incoming_ = true;
    517    ok = ProcessIncomingMessages(context, bytes_transfered, was_pending);
    518    processing_incoming_ = false;
    519  } else {
    520    mozilla::MutexAutoLock lock(SendMutex());
    521    DCHECK(context == &output_state_.context);
    522    was_pending = output_state_.is_pending.forget();
    523    ok = ProcessOutgoingMessages(context, bytes_transfered, was_pending);
    524  }
    525  if (!ok && INVALID_HANDLE_VALUE != pipe_) {
    526    // We don't want to re-enter Close().
    527    Close();
    528    listener_->OnChannelError();
    529  }
    530 }
    531 
    532 // This logic is borrowed from Chromium's `base/win/nt_status.cc`, and is used
    533 // to detect and silence DuplicateHandle errors caused due to the other process
    534 // exiting.
    535 //
    536 // https://source.chromium.org/chromium/chromium/src/+/main:base/win/nt_status.cc;drc=e4622aaeccea84652488d1822c28c78b7115684f
    537 static NTSTATUS GetLastNtStatus() {
    538  using GetLastNtStatusFn = NTSTATUS NTAPI (*)();
    539 
    540  static constexpr const wchar_t kNtDllName[] = L"ntdll.dll";
    541  static constexpr const char kLastStatusFnName[] = "RtlGetLastNtStatus";
    542 
    543  // This is equivalent to calling NtCurrentTeb() and extracting
    544  // LastStatusValue from the returned _TEB structure, except that the public
    545  // _TEB struct definition does not actually specify the location of the
    546  // LastStatusValue field. We avoid depending on such a definition by
    547  // internally using RtlGetLastNtStatus() from ntdll.dll instead.
    548  static auto* get_last_nt_status = reinterpret_cast<GetLastNtStatusFn>(
    549      ::GetProcAddress(::GetModuleHandle(kNtDllName), kLastStatusFnName));
    550  return get_last_nt_status();
    551 }
    552 
    553 // ERROR_ACCESS_DENIED may indicate that the remote process (which could be
    554 // either the source or destination process here) is already terminated or has
    555 // begun termination and therefore no longer has a handle table. We don't want
    556 // these cases to crash because we know they happen in practice and are
    557 // largely unavoidable.
    558 //
    559 // https://source.chromium.org/chromium/chromium/src/+/refs/heads/main:mojo/core/platform_handle_in_transit.cc;l=47-53;drc=fdfd85f836e0e59c79ed9bf6d527a2b8f7fdeb6e
    560 static bool WasOtherProcessExitingError(DWORD error) {
    561  return error == ERROR_ACCESS_DENIED &&
    562         GetLastNtStatus() == STATUS_PROCESS_IS_TERMINATING;
    563 }
    564 
    565 static uint32_t HandleToUint32(HANDLE h) {
    566  // Cast through uintptr_t and then unsigned int to make the truncation to
    567  // 32 bits explicit. Handles are size of-pointer but are always 32-bit values.
    568  // https://docs.microsoft.com/en-ca/windows/win32/winprog64/interprocess-communication
    569  // says: 64-bit versions of Windows use 32-bit handles for interoperability.
    570  return static_cast<uint32_t>(reinterpret_cast<uintptr_t>(h));
    571 }
    572 
    573 static HANDLE Uint32ToHandle(uint32_t h) {
    574  return reinterpret_cast<HANDLE>(
    575      static_cast<uintptr_t>(static_cast<int32_t>(h)));
    576 }
    577 
    578 bool ChannelWin::AcceptHandles(Message& msg) {
    579  chan_cap_.NoteOnTarget();
    580 
    581  MOZ_ASSERT(msg.num_handles() == 0);
    582 
    583  uint32_t num_handles = msg.header()->num_handles;
    584  if (num_handles == 0) {
    585    return true;
    586  }
    587 
    588  // Read in the payload from the footer, truncating the message.
    589  nsTArray<uint32_t> payload;
    590  payload.AppendElements(num_handles);
    591  if (!msg.ReadFooter(payload.Elements(), num_handles * sizeof(uint32_t),
    592                      /* truncate */ true)) {
    593    CHROMIUM_LOG(ERROR) << "failed to read handle payload from message";
    594    return false;
    595  }
    596  msg.header()->num_handles = 0;
    597 
    598  // Read in the handles themselves, transferring ownership as required.
    599  nsTArray<mozilla::UniqueFileHandle> handles(num_handles);
    600  for (uint32_t handleValue : payload) {
    601    HANDLE ipc_handle = Uint32ToHandle(handleValue);
    602    if (!ipc_handle || IsPseudoHandle(ipc_handle)) {
    603      CHROMIUM_LOG(ERROR)
    604          << "Attempt to accept invalid or null handle from process "
    605          << other_pid_ << " for message " << msg.name() << " in AcceptHandles";
    606      return false;
    607    }
    608 
    609    // If we're the privileged process, the remote process will have leaked
    610    // the sent handles in its local address space, and be relying on us to
    611    // duplicate them, otherwise the remote privileged side will have
    612    // transferred the handles to us already.
    613    mozilla::UniqueFileHandle local_handle;
    614    switch (mode_) {
    615      case MODE_BROKER_SERVER:
    616        MOZ_ASSERT(other_process_, "other_process_ cannot be null");
    617        if (other_process_ == INVALID_HANDLE_VALUE) {
    618          CHROMIUM_LOG(ERROR) << "other_process_ is invalid in AcceptHandles";
    619          return false;
    620        }
    621        if (!DuplicateRealHandle(
    622                other_process_, ipc_handle, GetCurrentProcess(),
    623                mozilla::getter_Transfers(local_handle), 0, FALSE,
    624                DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE)) {
    625          DWORD err = GetLastError();
    626          // Don't log out a scary looking error if this failed due to the
    627          // target process terminating.
    628          if (!WasOtherProcessExitingError(err)) {
    629            CHROMIUM_LOG(ERROR)
    630                << "DuplicateHandle failed for handle " << ipc_handle
    631                << " from process " << other_pid_ << " for message "
    632                << msg.name() << " in AcceptHandles with error: " << err;
    633          }
    634          return false;
    635        }
    636        break;
    637      case MODE_BROKER_CLIENT:
    638        local_handle.reset(ipc_handle);
    639        break;
    640      default:
    641        CHROMIUM_LOG(ERROR) << "invalid message: " << msg.name()
    642                            << ". channel is not configured to accept handles";
    643        return false;
    644    }
    645 
    646    MOZ_DIAGNOSTIC_ASSERT(
    647        local_handle, "Accepting invalid or null handle from another process");
    648 
    649    // The handle is directly owned by this process now, and can be added to
    650    // our `handles` array.
    651    handles.AppendElement(std::move(local_handle));
    652  }
    653 
    654  // We're done with the handle footer, truncate the message at that point.
    655  msg.SetAttachedFileHandles(std::move(handles));
    656  MOZ_ASSERT(msg.num_handles() == num_handles);
    657  return true;
    658 }
    659 
    660 bool ChannelWin::TransferHandles(Message& msg) {
    661  chan_cap_.NoteLockHeld();
    662 
    663  MOZ_ASSERT(msg.header()->num_handles == 0);
    664 
    665  uint32_t num_handles = msg.num_handles();
    666  if (num_handles == 0) {
    667    return true;
    668  }
    669 
    670 #ifdef DEBUG
    671  uint32_t handles_offset = msg.header()->payload_size;
    672 #endif
    673 
    674  nsTArray<uint32_t> payload(num_handles);
    675  for (uint32_t i = 0; i < num_handles; ++i) {
    676    // Take ownership of the handle.
    677    mozilla::UniqueFileHandle local_handle =
    678        std::move(msg.attached_handles_[i]);
    679    if (!local_handle) {
    680      CHROMIUM_LOG(ERROR)
    681          << "Attempt to transfer invalid or null handle to process "
    682          << other_pid_ << " for message " << msg.name()
    683          << " in TransferHandles";
    684      return false;
    685    }
    686 
    687    // If we're the privileged process, transfer the HANDLE to our remote before
    688    // sending the message. Otherwise, the remote privileged process will
    689    // transfer the handle for us, so leak it.
    690    HANDLE ipc_handle = NULL;
    691    switch (mode_) {
    692      case MODE_BROKER_SERVER:
    693        MOZ_ASSERT(other_process_, "other_process_ cannot be null");
    694        if (other_process_ == INVALID_HANDLE_VALUE) {
    695          CHROMIUM_LOG(ERROR) << "other_process_ is invalid in TransferHandles";
    696          return false;
    697        }
    698        if (!DuplicateRealHandle(GetCurrentProcess(), local_handle.get(),
    699                                 other_process_, &ipc_handle, 0, FALSE,
    700                                 DUPLICATE_SAME_ACCESS)) {
    701          DWORD err = GetLastError();
    702          // Don't log out a scary looking error if this failed due to the
    703          // target process terminating.
    704          if (!WasOtherProcessExitingError(err)) {
    705            CHROMIUM_LOG(ERROR) << "DuplicateHandle failed for handle "
    706                                << (HANDLE)local_handle.get() << " to process "
    707                                << other_pid_ << " for message " << msg.name()
    708                                << " in TransferHandles with error: " << err;
    709          }
    710          return false;
    711        }
    712        break;
    713      case MODE_BROKER_CLIENT:
    714        // Release ownership of the handle. It'll be closed when the parent
    715        // process transfers it with DuplicateHandle in the remote privileged
    716        // process.
    717        ipc_handle = local_handle.release();
    718        break;
    719      default:
    720        CHROMIUM_LOG(ERROR) << "cannot send message: " << msg.name()
    721                            << ". channel is not configured to accept handles";
    722        return false;
    723    }
    724 
    725    MOZ_DIAGNOSTIC_ASSERT(
    726        ipc_handle && ipc_handle != INVALID_HANDLE_VALUE,
    727        "Transferring invalid or null handle to another process");
    728 
    729    payload.AppendElement(HandleToUint32(ipc_handle));
    730  }
    731  msg.attached_handles_.Clear();
    732 
    733  msg.WriteFooter(payload.Elements(), payload.Length() * sizeof(uint32_t));
    734  msg.header()->num_handles = num_handles;
    735 
    736  MOZ_ASSERT(msg.header()->payload_size ==
    737                 handles_offset + (sizeof(uint32_t) * num_handles),
    738             "Unexpected number of bytes written for handles footer?");
    739  return true;
    740 }
    741 
    742 // static
    743 bool ChannelWin::CreateRawPipe(ChannelHandle* server, ChannelHandle* client) {
    744  std::wstring pipe_name =
    745      StringPrintf(L"\\\\.\\pipe\\gecko.%lu.%lu.%I64u", ::GetCurrentProcessId(),
    746                   ::GetCurrentThreadId(), mozilla::RandomUint64OrDie());
    747  const DWORD kOpenMode =
    748      PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED | FILE_FLAG_FIRST_PIPE_INSTANCE;
    749  const DWORD kPipeMode = PIPE_TYPE_BYTE | PIPE_READMODE_BYTE;
    750  auto& serverHandle = server->emplace<mozilla::UniqueFileHandle>(
    751      ::CreateNamedPipeW(pipe_name.c_str(), kOpenMode, kPipeMode,
    752                         1,                         // Max instances.
    753                         Channel::kReadBufferSize,  // Output buffer size.
    754                         Channel::kReadBufferSize,  // Input buffer size.
    755                         5000,                      // Timeout in ms.
    756                         nullptr));  // Default security descriptor.
    757  if (!serverHandle) {
    758    NS_WARNING(
    759        nsPrintfCString("CreateNamedPipeW Failed %lu", ::GetLastError()).get());
    760    return false;
    761  }
    762 
    763  const DWORD kDesiredAccess = GENERIC_READ | GENERIC_WRITE;
    764  // The SECURITY_ANONYMOUS flag means that the server side cannot impersonate
    765  // the client, which is useful as both server & client may be unprivileged.
    766  const DWORD kFlags =
    767      SECURITY_SQOS_PRESENT | SECURITY_ANONYMOUS | FILE_FLAG_OVERLAPPED;
    768  auto& clientHandle = client->emplace<mozilla::UniqueFileHandle>(
    769      ::CreateFileW(pipe_name.c_str(), kDesiredAccess, 0, nullptr,
    770                    OPEN_EXISTING, kFlags, nullptr));
    771  if (!clientHandle) {
    772    NS_WARNING(
    773        nsPrintfCString("CreateFileW Failed %lu", ::GetLastError()).get());
    774    return false;
    775  }
    776 
    777  // Since a client has connected, ConnectNamedPipe() should return zero and
    778  // GetLastError() should return ERROR_PIPE_CONNECTED.
    779  if (::ConnectNamedPipe(serverHandle.get(), nullptr) ||
    780      ::GetLastError() != ERROR_PIPE_CONNECTED) {
    781    NS_WARNING(
    782        nsPrintfCString("ConnectNamedPipe Failed %lu", ::GetLastError()).get());
    783    return false;
    784  }
    785  return true;
    786 }
    787 
    788 // static
    789 uint32_t ChannelWin::NumRelayedAttachments(const Message& message) {
    790  return message.num_handles();
    791 }
    792 
    793 // static
    794 bool ChannelWin::IsValidHandle(const ChannelHandle& handle) {
    795  const auto* fileHandle = std::get_if<mozilla::UniqueFileHandle>(&handle);
    796  return fileHandle && *fileHandle;
    797 }
    798 
    799 }  // namespace IPC