tor-browser

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

MessagePortService.cpp (12003B)


      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
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "MessagePortService.h"
      8 
      9 #include "MessagePortParent.h"
     10 #include "mozilla/StaticPtr.h"
     11 #include "mozilla/WeakPtr.h"
     12 #include "mozilla/dom/RefMessageBodyService.h"
     13 #include "mozilla/dom/SharedMessageBody.h"
     14 #include "mozilla/dom/quota/CheckedUnsafePtr.h"
     15 #include "mozilla/ipc/BackgroundParent.h"
     16 #include "nsTArray.h"
     17 
     18 using mozilla::ipc::AssertIsOnBackgroundThread;
     19 
     20 namespace mozilla::dom {
     21 
     22 namespace {
     23 
     24 StaticRefPtr<MessagePortService> gInstance;
     25 
     26 void AssertIsInMainProcess() {
     27  MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
     28 }
     29 
     30 }  // namespace
     31 
     32 struct MessagePortService::NextParent {
     33  uint32_t mSequenceID;
     34  // MessagePortParent keeps the service alive, and we don't want a cycle.
     35  WeakPtr<MessagePortParent> mParent;
     36 };
     37 
     38 }  // namespace mozilla::dom
     39 
     40 namespace mozilla::dom {
     41 
     42 class MessagePortService::MessagePortServiceData final {
     43 public:
     44  explicit MessagePortServiceData(const nsID& aDestinationUUID)
     45      : mDestinationUUID(aDestinationUUID),
     46        mSequenceID(1),
     47        mParent(nullptr)
     48        // By default we don't know the next parent.
     49        ,
     50        mWaitingForNewParent(true),
     51        mNextStepCloseAll(false) {
     52    MOZ_COUNT_CTOR(MessagePortServiceData);
     53  }
     54 
     55  MessagePortServiceData(const MessagePortServiceData& aOther) = delete;
     56  MessagePortServiceData& operator=(const MessagePortServiceData&) = delete;
     57 
     58  MOZ_COUNTED_DTOR(MessagePortServiceData)
     59 
     60  nsID mDestinationUUID;
     61 
     62  uint32_t mSequenceID;
     63  CheckedUnsafePtr<MessagePortParent> mParent;
     64 
     65  FallibleTArray<NextParent> mNextParents;
     66  FallibleTArray<RefPtr<SharedMessageBody>> mMessages;
     67 
     68  bool mWaitingForNewParent;
     69  bool mNextStepCloseAll;
     70 };
     71 
     72 /* static */
     73 MessagePortService* MessagePortService::Get() {
     74  AssertIsInMainProcess();
     75  AssertIsOnBackgroundThread();
     76 
     77  return gInstance;
     78 }
     79 
     80 /* static */
     81 MessagePortService* MessagePortService::GetOrCreate() {
     82  AssertIsInMainProcess();
     83  AssertIsOnBackgroundThread();
     84 
     85  if (!gInstance) {
     86    gInstance = new MessagePortService();
     87  }
     88 
     89  return gInstance;
     90 }
     91 
     92 bool MessagePortService::RequestEntangling(MessagePortParent* aParent,
     93                                           const nsID& aDestinationUUID,
     94                                           const uint32_t& aSequenceID) {
     95  MOZ_ASSERT(aParent);
     96  MessagePortServiceData* data;
     97 
     98  // If we don't have a MessagePortServiceData, we must create 2 of them for
     99  // both ports.
    100  if (!mPorts.Get(aParent->ID(), &data)) {
    101    // Create the MessagePortServiceData for the destination.
    102    if (mPorts.Get(aDestinationUUID, nullptr)) {
    103      MOZ_ASSERT(false, "The creation of the 2 ports should be in sync.");
    104      return false;
    105    }
    106 
    107    mPorts.InsertOrUpdate(aDestinationUUID,
    108                          MakeUnique<MessagePortServiceData>(aParent->ID()));
    109 
    110    data = mPorts
    111               .InsertOrUpdate(
    112                   aParent->ID(),
    113                   MakeUnique<MessagePortServiceData>(aDestinationUUID))
    114               .get();
    115  }
    116 
    117  // This is a security check.
    118  if (!data->mDestinationUUID.Equals(aDestinationUUID)) {
    119    MOZ_ASSERT(false, "DestinationUUIDs do not match!");
    120    CloseAll(aParent->ID());
    121    return false;
    122  }
    123 
    124  if (aSequenceID < data->mSequenceID) {
    125    MOZ_ASSERT(false, "Invalid sequence ID!");
    126    CloseAll(aParent->ID());
    127    return false;
    128  }
    129 
    130  if (aSequenceID == data->mSequenceID) {
    131    if (data->mParent) {
    132      MOZ_ASSERT(false, "Two ports cannot have the same sequenceID.");
    133      CloseAll(aParent->ID());
    134      return false;
    135    }
    136 
    137    // We activate this port, sending all the messages.
    138    data->mParent = aParent;
    139    data->mWaitingForNewParent = false;
    140 
    141    // We want to ensure we clear data->mMessages even if we early return, while
    142    // also ensuring that its contents remain alive until after array's contents
    143    // are destroyed because of JSStructuredCloneData borrowing.  So we use
    144    // Move to initialize things swapped and do it before we declare `array` so
    145    // that reverse destruction order works for us.
    146    FallibleTArray<RefPtr<SharedMessageBody>> messages(
    147        std::move(data->mMessages));
    148    nsTArray<MessageData> array;
    149    if (!SharedMessageBody::FromSharedToMessagesParent(aParent->Manager(),
    150                                                       messages, array)) {
    151      CloseAll(aParent->ID());
    152      return false;
    153    }
    154 
    155    // We can entangle the port.
    156    if (!aParent->Entangled(std::move(array))) {
    157      CloseAll(aParent->ID());
    158      return false;
    159    }
    160 
    161    // If we were waiting for this parent in order to close this channel, this
    162    // is the time to do it.
    163    if (data->mNextStepCloseAll) {
    164      CloseAll(aParent->ID());
    165    }
    166 
    167    return true;
    168  }
    169 
    170  // This new parent will be the next one when a Disentangle request is
    171  // received from the current parent.
    172  auto nextParent = data->mNextParents.AppendElement(mozilla::fallible);
    173  if (!nextParent) {
    174    CloseAll(aParent->ID());
    175    return false;
    176  }
    177 
    178  nextParent->mSequenceID = aSequenceID;
    179  nextParent->mParent = aParent;
    180 
    181  return true;
    182 }
    183 
    184 bool MessagePortService::DisentanglePort(
    185    MessagePortParent* aParent,
    186    FallibleTArray<RefPtr<SharedMessageBody>> aMessages) {
    187  MessagePortServiceData* data;
    188  if (!mPorts.Get(aParent->ID(), &data)) {
    189    MOZ_ASSERT(false, "Unknown MessagePortParent should not happen.");
    190    return false;
    191  }
    192 
    193  if (data->mParent != aParent) {
    194    MOZ_ASSERT(
    195        false,
    196        "DisentanglePort() should be called just from the correct parent.");
    197    return false;
    198  }
    199 
    200  // Let's put the messages in the correct order. |aMessages| contains the
    201  // unsent messages so they have to go first.
    202  if (!aMessages.AppendElements(std::move(data->mMessages),
    203                                mozilla::fallible)) {
    204    return false;
    205  }
    206 
    207  ++data->mSequenceID;
    208 
    209  // If we don't have a parent, we have to store the pending messages and wait.
    210  uint32_t index = 0;
    211  MessagePortParent* nextParent = nullptr;
    212  for (; index < data->mNextParents.Length(); ++index) {
    213    if (data->mNextParents[index].mSequenceID == data->mSequenceID) {
    214      nextParent = data->mNextParents[index].mParent;
    215      break;
    216    }
    217  }
    218 
    219  // We didn't find the parent.
    220  if (!nextParent) {
    221    data->mMessages = std::move(aMessages);
    222    data->mWaitingForNewParent = true;
    223    data->mParent = nullptr;
    224    return true;
    225  }
    226 
    227  data->mParent = nextParent;
    228  data->mNextParents.RemoveElementAt(index);
    229 
    230  nsTArray<MessageData> array;
    231  if (!SharedMessageBody::FromSharedToMessagesParent(data->mParent->Manager(),
    232                                                     aMessages, array)) {
    233    return false;
    234  }
    235 
    236  (void)data->mParent->Entangled(std::move(array));
    237  return true;
    238 }
    239 
    240 bool MessagePortService::ClosePort(MessagePortParent* aParent) {
    241  MessagePortServiceData* data;
    242  if (!mPorts.Get(aParent->ID(), &data)) {
    243    MOZ_ASSERT(false, "Unknown MessagePortParent should not happend.");
    244    return false;
    245  }
    246 
    247  if (data->mParent != aParent) {
    248    MOZ_ASSERT(false,
    249               "ClosePort() should be called just from the correct parent.");
    250    return false;
    251  }
    252 
    253  if (!data->mNextParents.IsEmpty()) {
    254    MOZ_ASSERT(false,
    255               "ClosePort() should be called when there are not next parents.");
    256    return false;
    257  }
    258 
    259  // We don't want to send a message to this parent.
    260  data->mParent = nullptr;
    261 
    262  CloseAll(aParent->ID());
    263  return true;
    264 }
    265 
    266 void MessagePortService::CloseAll(const nsID& aUUID, bool aForced) {
    267  MessagePortServiceData* data;
    268  if (!mPorts.Get(aUUID, &data)) {
    269    MaybeShutdown();
    270    return;
    271  }
    272 
    273  if (data->mParent) {
    274    data->mParent->Close();
    275    data->mParent = nullptr;
    276  }
    277 
    278  for (const auto& nextParent : data->mNextParents) {
    279    MessagePortParent* const parent = nextParent.mParent;
    280    if (parent) {
    281      parent->CloseAndDelete();
    282    }
    283  }
    284  data->mNextParents.Clear();
    285 
    286  nsID destinationUUID = data->mDestinationUUID;
    287 
    288  // If we have informations about the other port and that port has some
    289  // pending messages to deliver but the parent has not processed them yet,
    290  // because its entangling request didn't arrive yet), we cannot close this
    291  // channel.
    292  MessagePortServiceData* destinationData;
    293  if (!aForced && mPorts.Get(destinationUUID, &destinationData) &&
    294      !destinationData->mMessages.IsEmpty() &&
    295      destinationData->mWaitingForNewParent) {
    296    MOZ_ASSERT(!destinationData->mNextStepCloseAll);
    297    destinationData->mNextStepCloseAll = true;
    298    return;
    299  }
    300 
    301  mPorts.Remove(aUUID);
    302 
    303  CloseAll(destinationUUID, aForced);
    304 
    305  // CloseAll calls itself recursively and it can happen that it deletes
    306  // itself. Before continuing we must check if we are still alive.
    307  if (!gInstance) {
    308    return;
    309  }
    310 
    311  MOZ_ASSERT(!mPorts.Contains(aUUID));
    312 
    313  MaybeShutdown();
    314 }
    315 
    316 // This service can be dismissed when there are not active ports.
    317 void MessagePortService::MaybeShutdown() {
    318  if (mPorts.Count() == 0) {
    319    gInstance = nullptr;
    320  }
    321 }
    322 
    323 bool MessagePortService::PostMessages(
    324    MessagePortParent* aParent,
    325    FallibleTArray<RefPtr<SharedMessageBody>> aMessages) {
    326  MessagePortServiceData* data;
    327  if (!mPorts.Get(aParent->ID(), &data)) {
    328    MOZ_ASSERT(false, "Unknown MessagePortParent should not happend.");
    329    return false;
    330  }
    331 
    332  if (data->mParent != aParent) {
    333    MOZ_ASSERT(false,
    334               "PostMessages() should be called just from the correct parent.");
    335    return false;
    336  }
    337 
    338  MOZ_ALWAYS_TRUE(mPorts.Get(data->mDestinationUUID, &data));
    339 
    340  if (!data->mMessages.AppendElements(std::move(aMessages),
    341                                      mozilla::fallible)) {
    342    return false;
    343  }
    344 
    345  // If the parent can send data to the child, let's proceed.
    346  if (data->mParent && data->mParent->CanSendData()) {
    347    {
    348      nsTArray<MessageData> messages;
    349      if (!SharedMessageBody::FromSharedToMessagesParent(
    350              data->mParent->Manager(), data->mMessages, messages)) {
    351        return false;
    352      }
    353 
    354      (void)data->mParent->SendReceiveData(messages);
    355    }
    356    // `messages` borrows the underlying JSStructuredCloneData so we need to
    357    // avoid destroying the `mMessages` until after we've destroyed `messages`.
    358    data->mMessages.Clear();
    359  }
    360 
    361  return true;
    362 }
    363 
    364 void MessagePortService::ParentDestroy(MessagePortParent* aParent) {
    365  // This port has already been destroyed.
    366  MessagePortServiceData* data;
    367  if (!mPorts.Get(aParent->ID(), &data)) {
    368    return;
    369  }
    370 
    371  if (data->mParent != aParent) {
    372    // We don't want to send a message to this parent.
    373    for (uint32_t i = 0; i < data->mNextParents.Length(); ++i) {
    374      if (aParent == data->mNextParents[i].mParent) {
    375        data->mNextParents.RemoveElementAt(i);
    376        break;
    377      }
    378    }
    379  }
    380 
    381  CloseAll(aParent->ID());
    382 }
    383 
    384 bool MessagePortService::ForceClose(const nsID& aUUID,
    385                                    const nsID& aDestinationUUID,
    386                                    const uint32_t& aSequenceID) {
    387  MessagePortServiceData* data;
    388  if (!mPorts.Get(aUUID, &data)) {
    389    NS_WARNING("Unknown MessagePort in ForceClose()");
    390    // There is nothing to close so we are ok.
    391    return true;
    392  }
    393 
    394  NS_ENSURE_TRUE(data->mDestinationUUID.Equals(aDestinationUUID), false);
    395 
    396  // If StructuredCloneData includes a MessagePort, StructuredCloneData
    397  // serialization failure in postMessage can trigger MessagePort::ForceClose().
    398  // And since the serialized port transfered has started but not finished yet,
    399  // the SequenceID will not be synchronized to the parent side, which will
    400  // cause the SequenceID to mismatch here. See bug 1872770.
    401  NS_WARNING_ASSERTION(data->mSequenceID == aSequenceID,
    402                       "sequence IDs do not match");
    403 
    404  CloseAll(aUUID, true);
    405  return true;
    406 }
    407 
    408 }  // namespace mozilla::dom