tor-browser

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

NodeChannel.cpp (9853B)


      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 /* 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 file,
      5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "mozilla/ipc/NodeChannel.h"
      8 #include "chrome/common/ipc_message.h"
      9 #include "chrome/common/ipc_message_utils.h"
     10 #include "mojo/core/ports/name.h"
     11 #include "mozilla/ipc/IOThread.h"
     12 #include "mozilla/ipc/GeckoChildProcessHost.h"
     13 #include "mozilla/ipc/ProtocolMessageUtils.h"
     14 #include "mozilla/ipc/ProtocolUtils.h"
     15 #include "nsThreadUtils.h"
     16 #include "nsXULAppAPI.h"
     17 
     18 #ifdef FUZZING_SNAPSHOT
     19 #  include "mozilla/fuzzing/IPCFuzzController.h"
     20 #endif
     21 
     22 template <>
     23 struct IPC::ParamTraits<mozilla::ipc::NodeChannel::Introduction> {
     24  using paramType = mozilla::ipc::NodeChannel::Introduction;
     25  static void Write(MessageWriter* aWriter, paramType&& aParam) {
     26    WriteParam(aWriter, aParam.mName);
     27    WriteParam(aWriter, std::move(aParam.mHandle));
     28    WriteParam(aWriter, aParam.mMyPid);
     29    WriteParam(aWriter, aParam.mOtherPid);
     30  }
     31  static bool Read(MessageReader* aReader, paramType* aResult) {
     32    return ReadParam(aReader, &aResult->mName) &&
     33           ReadParam(aReader, &aResult->mHandle) &&
     34           ReadParam(aReader, &aResult->mMyPid) &&
     35           ReadParam(aReader, &aResult->mOtherPid);
     36  }
     37 };
     38 
     39 namespace mozilla::ipc {
     40 
     41 NodeChannel::NodeChannel(const NodeName& aName, IPC::Channel* aChannel,
     42                         Listener* aListener, base::ProcessId aPid,
     43                         GeckoChildProcessHost* aChildProcessHost)
     44    : mListener(aListener),
     45      mName(aName),
     46      mOtherPid(aPid),
     47      mChannel(std::move(aChannel)),
     48      mChildProcessHost(aChildProcessHost) {}
     49 
     50 NodeChannel::~NodeChannel() { Close(); }
     51 
     52 // Called when the NodeChannel's refcount drops to `0`.
     53 void NodeChannel::Destroy() {
     54  // We want to dispatch the `delete` operation to the IO thread. We need to do
     55  // this even if we're already on the IO thread, as we could be in an
     56  // `IPC::Channel` callback which unfortunately will not hold a strong
     57  // reference to keep `this` alive.
     58  nsISerialEventTarget* ioThread = XRE_GetAsyncIOEventTarget();
     59 
     60  // Synchronously invoke `FinalDestroy` if we're already shutting the IO thread
     61  // down to ensure we're cleaned up before the thread dies. This is safe as we
     62  // can't be in a non-owning IPC::Channel callback at this point.
     63  if (ioThread->IsOnCurrentThread() && MessageLoop::current() &&
     64      !MessageLoop::current()->IsAcceptingTasks()) {
     65    FinalDestroy();
     66    return;
     67  }
     68 
     69  MOZ_ALWAYS_SUCCEEDS(ioThread->Dispatch(NewNonOwningRunnableMethod(
     70      "NodeChannel::Destroy", this, &NodeChannel::FinalDestroy)));
     71 }
     72 
     73 void NodeChannel::FinalDestroy() {
     74  AssertIOThread();
     75  delete this;
     76 }
     77 
     78 void NodeChannel::Start() {
     79  AssertIOThread();
     80 
     81  if (!mChannel->Connect(this)) {
     82    OnChannelError();
     83  }
     84 }
     85 
     86 void NodeChannel::Close() {
     87  AssertIOThread();
     88 
     89  if (mState.exchange(State::Closed) != State::Closed) {
     90    mChannel->Close();
     91  }
     92 }
     93 
     94 void NodeChannel::SetOtherPid(base::ProcessId aNewPid) {
     95  AssertIOThread();
     96  MOZ_ASSERT(aNewPid != base::kInvalidProcessId);
     97 
     98  base::ProcessId previousPid = base::kInvalidProcessId;
     99  if (!mOtherPid.compare_exchange_strong(previousPid, aNewPid)) {
    100    // The PID was already set before this call, double-check that it's correct.
    101    MOZ_RELEASE_ASSERT(previousPid == aNewPid,
    102                       "Different sources disagree on the correct pid?");
    103  }
    104 
    105  mChannel->SetOtherPid(aNewPid);
    106 }
    107 
    108 #ifdef XP_DARWIN
    109 void NodeChannel::SetMachTaskPort(task_t aTask) {
    110  AssertIOThread();
    111 
    112  if (mState != State::Closed) {
    113    mChannel->SetOtherMachTask(aTask);
    114  }
    115 }
    116 #endif
    117 
    118 void NodeChannel::SendEventMessage(UniquePtr<IPC::Message> aMessage) {
    119  // Make sure we're not sending a message with one of our special internal
    120  // types ,as those should only be sent using the corresponding methods on
    121  // NodeChannel.
    122  MOZ_DIAGNOSTIC_ASSERT(aMessage->type() != BROADCAST_MESSAGE_TYPE &&
    123                        aMessage->type() != INTRODUCE_MESSAGE_TYPE &&
    124                        aMessage->type() != REQUEST_INTRODUCTION_MESSAGE_TYPE &&
    125                        aMessage->type() != ACCEPT_INVITE_MESSAGE_TYPE);
    126  SendMessage(std::move(aMessage));
    127 }
    128 
    129 void NodeChannel::RequestIntroduction(const NodeName& aPeerName) {
    130  MOZ_ASSERT(aPeerName != mojo::core::ports::kInvalidNodeName);
    131  auto message = MakeUnique<IPC::Message>(MSG_ROUTING_CONTROL,
    132                                          REQUEST_INTRODUCTION_MESSAGE_TYPE);
    133  IPC::MessageWriter writer(*message);
    134  WriteParam(&writer, aPeerName);
    135  SendMessage(std::move(message));
    136 }
    137 
    138 void NodeChannel::Introduce(Introduction aIntroduction) {
    139  auto message =
    140      MakeUnique<IPC::Message>(MSG_ROUTING_CONTROL, INTRODUCE_MESSAGE_TYPE);
    141  IPC::MessageWriter writer(*message);
    142  WriteParam(&writer, std::move(aIntroduction));
    143  SendMessage(std::move(message));
    144 }
    145 
    146 void NodeChannel::Broadcast(UniquePtr<IPC::Message> aMessage) {
    147  MOZ_DIAGNOSTIC_ASSERT(aMessage->type() == BROADCAST_MESSAGE_TYPE,
    148                        "Can only broadcast messages with the correct type");
    149  SendMessage(std::move(aMessage));
    150 }
    151 
    152 void NodeChannel::AcceptInvite(const NodeName& aRealName,
    153                               const PortName& aInitialPort) {
    154  MOZ_ASSERT(aRealName != mojo::core::ports::kInvalidNodeName);
    155  MOZ_ASSERT(aInitialPort != mojo::core::ports::kInvalidPortName);
    156  auto message =
    157      MakeUnique<IPC::Message>(MSG_ROUTING_CONTROL, ACCEPT_INVITE_MESSAGE_TYPE);
    158  IPC::MessageWriter writer(*message);
    159  WriteParam(&writer, aRealName);
    160  WriteParam(&writer, aInitialPort);
    161  SendMessage(std::move(message));
    162 }
    163 
    164 void NodeChannel::SendMessage(UniquePtr<IPC::Message> aMessage) {
    165  if (aMessage->size() > IPC::Channel::kMaximumMessageSize) {
    166    CrashReporter::RecordAnnotationCString(
    167        CrashReporter::Annotation::IPCMessageName, aMessage->name());
    168    CrashReporter::RecordAnnotationU32(
    169        CrashReporter::Annotation::IPCMessageSize, aMessage->size());
    170    CrashReporter::RecordAnnotationU32(
    171        CrashReporter::Annotation::IPCMessageLargeBufferShmemFailureSize,
    172        aMessage->LargeBufferShmemFailureSize());
    173    MOZ_CRASH("IPC message size is too large");
    174  }
    175  aMessage->AssertAsLargeAsHeader();
    176 
    177 #ifdef FUZZING_SNAPSHOT
    178  if (mBlockSendRecv) {
    179    return;
    180  }
    181 #endif
    182 
    183  if (mState != State::Active) {
    184    NS_WARNING("Dropping message as channel has been closed");
    185    return;
    186  }
    187 
    188  // NOTE: As this is not guaranteed to be running on the I/O thread, the
    189  // channel may have become closed since we checked above. IPC::Channel will
    190  // handle that and return `false` here, so we can re-check `mState`.
    191  if (!mChannel->Send(std::move(aMessage))) {
    192    NS_WARNING("Call to Send() failed");
    193 
    194    // If we're still active, update `mState` to `State::Closing`, and dispatch
    195    // a runnable to actually close our channel.
    196    State expected = State::Active;
    197    if (mState.compare_exchange_strong(expected, State::Closing)) {
    198      XRE_GetAsyncIOEventTarget()->Dispatch(
    199          NewRunnableMethod("NodeChannel::CloseForSendError", this,
    200                            &NodeChannel::OnChannelError));
    201    }
    202  }
    203 }
    204 
    205 void NodeChannel::OnMessageReceived(UniquePtr<IPC::Message> aMessage) {
    206  AssertIOThread();
    207 
    208 #ifdef FUZZING_SNAPSHOT
    209  if (mBlockSendRecv && !aMessage->IsFuzzMsg()) {
    210    return;
    211  }
    212 #endif
    213 
    214  IPC::MessageReader reader(*aMessage);
    215  switch (aMessage->type()) {
    216    case REQUEST_INTRODUCTION_MESSAGE_TYPE: {
    217      NodeName name;
    218      if (IPC::ReadParam(&reader, &name)) {
    219        mListener->OnRequestIntroduction(mName, name);
    220        return;
    221      }
    222      break;
    223    }
    224    case INTRODUCE_MESSAGE_TYPE: {
    225      Introduction introduction;
    226      if (IPC::ReadParam(&reader, &introduction)) {
    227        mListener->OnIntroduce(mName, std::move(introduction));
    228        return;
    229      }
    230      break;
    231    }
    232    case BROADCAST_MESSAGE_TYPE: {
    233      mListener->OnBroadcast(mName, std::move(aMessage));
    234      return;
    235    }
    236    case ACCEPT_INVITE_MESSAGE_TYPE: {
    237      NodeName realName;
    238      PortName initialPort;
    239      if (IPC::ReadParam(&reader, &realName) &&
    240          IPC::ReadParam(&reader, &initialPort)) {
    241        mListener->OnAcceptInvite(mName, realName, initialPort);
    242        return;
    243      }
    244      break;
    245    }
    246    // Assume all unrecognized types are intended as user event messages, and
    247    // deliver them to our listener as such. This allows us to use the same type
    248    // field for both internal messages and protocol messages.
    249    //
    250    // FIXME: Consider doing something cleaner in the future?
    251    case EVENT_MESSAGE_TYPE:
    252    default: {
    253 #ifdef FUZZING_SNAPSHOT
    254      if (!fuzzing::IPCFuzzController::instance().ObserveIPCMessage(
    255              this, *aMessage)) {
    256        return;
    257      }
    258 #endif
    259 
    260      mListener->OnEventMessage(mName, std::move(aMessage));
    261      return;
    262    }
    263  }
    264 
    265  // If we got to this point without early returning the message was malformed
    266  // in some way. Report an error.
    267 
    268  NS_WARNING("NodeChannel received a malformed message");
    269  OnChannelError();
    270 }
    271 
    272 void NodeChannel::OnChannelConnected(base::ProcessId aPeerPid) {
    273  AssertIOThread();
    274 
    275  SetOtherPid(aPeerPid);
    276 
    277  // We may need to tell the GeckoChildProcessHost which we were created by that
    278  // the channel has been connected to unblock completing the process launch.
    279  if (mChildProcessHost) {
    280    mChildProcessHost->OnChannelConnected(aPeerPid);
    281  }
    282 }
    283 
    284 void NodeChannel::OnChannelError() {
    285  AssertIOThread();
    286 
    287  State prev = mState.exchange(State::Closed);
    288  if (prev == State::Closed) {
    289    return;
    290  }
    291 
    292  // Clean up the channel.
    293  mChannel->Close();
    294 
    295  // Tell our listener about the error.
    296  mListener->OnChannelError(mName);
    297 }
    298 
    299 }  // namespace mozilla::ipc