LCOV - code coverage report
Current view: top level - ipc/glue - MessageLink.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 91 177 51.4 %
Date: 2017-07-14 16:53:18 Functions: 10 28 35.7 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
       2             :  * vim: sw=4 ts=4 et :
       3             :  */
       4             : /* This Source Code Form is subject to the terms of the Mozilla Public
       5             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       6             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       7             : 
       8             : #include "mozilla/ipc/MessageLink.h"
       9             : #include "mozilla/ipc/MessageChannel.h"
      10             : #include "mozilla/ipc/BrowserProcessSubThread.h"
      11             : #include "mozilla/ipc/ProtocolUtils.h"
      12             : #include "chrome/common/ipc_channel.h"
      13             : 
      14             : #include "mozilla/Assertions.h"
      15             : #include "mozilla/DebugOnly.h"
      16             : #include "nsDebug.h"
      17             : #ifdef MOZ_CRASHREPORTER
      18             : #include "nsExceptionHandler.h"
      19             : #endif
      20             : #include "nsISupportsImpl.h"
      21             : #include "nsPrintfCString.h"
      22             : #include "nsXULAppAPI.h"
      23             : 
      24             : using namespace mozilla;
      25             : using namespace std;
      26             : 
      27             : // We rely on invariants about the lifetime of the transport:
      28             : //
      29             : //  - outlives this MessageChannel
      30             : //  - deleted on the IO thread
      31             : //
      32             : // These invariants allow us to send messages directly through the
      33             : // transport without having to worry about orphaned Send() tasks on
      34             : // the IO thread touching MessageChannel memory after it's been deleted
      35             : // on the worker thread.  We also don't need to refcount the
      36             : // Transport, because whatever task triggers its deletion only runs on
      37             : // the IO thread, and only runs after this MessageChannel is done with
      38             : // the Transport.
      39             : 
      40             : namespace mozilla {
      41             : namespace ipc {
      42             : 
      43          38 : MessageLink::MessageLink(MessageChannel *aChan)
      44          38 :   : mChan(aChan)
      45             : {
      46          38 : }
      47             : 
      48           0 : MessageLink::~MessageLink()
      49             : {
      50             : #ifdef DEBUG
      51           0 :     mChan = nullptr;
      52             : #endif
      53           0 : }
      54             : 
      55          28 : ProcessLink::ProcessLink(MessageChannel *aChan)
      56             :   : MessageLink(aChan)
      57             :   , mTransport(nullptr)
      58             :   , mIOLoop(nullptr)
      59          28 :   , mExistingListener(nullptr)
      60             : {
      61          28 : }
      62             : 
      63           0 : ProcessLink::~ProcessLink()
      64             : {
      65             : #ifdef DEBUG
      66           0 :     mTransport = nullptr;
      67           0 :     mIOLoop = nullptr;
      68           0 :     mExistingListener = nullptr;
      69             : #endif
      70           0 : }
      71             : 
      72             : void
      73          28 : ProcessLink::Open(mozilla::ipc::Transport* aTransport, MessageLoop *aIOLoop, Side aSide)
      74             : {
      75          28 :     NS_PRECONDITION(aTransport, "need transport layer");
      76             : 
      77             :     // FIXME need to check for valid channel
      78             : 
      79          28 :     mTransport = aTransport;
      80             : 
      81             :     // FIXME figure out whether we're in parent or child, grab IO loop
      82             :     // appropriately
      83          28 :     bool needOpen = true;
      84          28 :     if(aIOLoop) {
      85             :         // We're a child or using the new arguments.  Either way, we
      86             :         // need an open.
      87          26 :         needOpen = true;
      88          26 :         mChan->mSide = (aSide == UnknownSide) ? ChildSide : aSide;
      89             :     } else {
      90           2 :         NS_PRECONDITION(aSide == UnknownSide, "expected default side arg");
      91             : 
      92             :         // parent
      93           2 :         mChan->mSide = ParentSide;
      94           2 :         needOpen = false;
      95           2 :         aIOLoop = XRE_GetIOMessageLoop();
      96             :     }
      97             : 
      98          28 :     mIOLoop = aIOLoop;
      99             : 
     100          28 :     NS_ASSERTION(mIOLoop, "need an IO loop");
     101          28 :     NS_ASSERTION(mChan->mWorkerLoop, "need a worker loop");
     102             : 
     103             :     // If we were never able to open the transport, immediately post an error message.
     104          28 :     if (mTransport->Unsound_IsClosed()) {
     105           0 :       mIOLoop->PostTask(
     106           0 :         NewNonOwningRunnableMethod("ipc::ProcessLink::OnChannelConnectError",
     107             :                                    this,
     108           0 :                                    &ProcessLink::OnChannelConnectError));
     109           0 :       return;
     110             :     }
     111             : 
     112             :     {
     113          56 :         MonitorAutoLock lock(*mChan->mMonitor);
     114             : 
     115          28 :         if (needOpen) {
     116             :             // Transport::Connect() has not been called.  Call it so
     117             :             // we start polling our pipe and processing outgoing
     118             :             // messages.
     119          52 :             mIOLoop->PostTask(
     120          52 :               NewNonOwningRunnableMethod("ipc::ProcessLink::OnChannelOpened",
     121             :                                          this,
     122          26 :                                          &ProcessLink::OnChannelOpened));
     123             :         } else {
     124             :             // Transport::Connect() has already been called.  Take
     125             :             // over the channel from the previous listener and process
     126             :             // any queued messages.
     127           4 :             mIOLoop->PostTask(NewNonOwningRunnableMethod(
     128             :               "ipc::ProcessLink::OnTakeConnectedChannel",
     129             :               this,
     130           2 :               &ProcessLink::OnTakeConnectedChannel));
     131             :         }
     132             : 
     133             :         // Should not wait here if something goes wrong with the channel.
     134          84 :         while (!mChan->Connected() && mChan->mChannelState != ChannelError) {
     135          28 :             mChan->mMonitor->Wait();
     136             :         }
     137             :     }
     138             : }
     139             : 
     140             : void
     141           0 : ProcessLink::EchoMessage(Message *msg)
     142             : {
     143           0 :     mChan->AssertWorkerThread();
     144           0 :     mChan->mMonitor->AssertCurrentThreadOwns();
     145             : 
     146           0 :     mIOLoop->PostTask(
     147           0 :       NewNonOwningRunnableMethod<Message*>("ipc::ProcessLink::OnEchoMessage",
     148             :                                            this,
     149             :                                            &ProcessLink::OnEchoMessage,
     150           0 :                                            msg));
     151             :     // OnEchoMessage takes ownership of |msg|
     152           0 : }
     153             : 
     154             : void
     155         360 : ProcessLink::SendMessage(Message *msg)
     156             : {
     157         360 :     if (msg->size() > IPC::Channel::kMaximumMessageSize) {
     158             : #ifdef MOZ_CRASHREPORTER
     159           0 :       CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("IPCMessageName"), nsDependentCString(msg->name()));
     160           0 :       CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("IPCMessageSize"), nsPrintfCString("%d", msg->size()));
     161             : #endif
     162           0 :       MOZ_CRASH("IPC message size is too large");
     163             :     }
     164             : 
     165         360 :     if (!mChan->mIsPostponingSends) {
     166         360 :         mChan->AssertWorkerThread();
     167             :     }
     168         360 :     mChan->mMonitor->AssertCurrentThreadOwns();
     169             : 
     170         720 :     mIOLoop->PostTask(NewNonOwningRunnableMethod<Message*>(
     171         360 :       "IPC::Channel::Send", mTransport, &Transport::Send, msg));
     172         360 : }
     173             : 
     174             : void
     175           0 : ProcessLink::SendClose()
     176             : {
     177           0 :     mChan->AssertWorkerThread();
     178           0 :     mChan->mMonitor->AssertCurrentThreadOwns();
     179             : 
     180           0 :     mIOLoop->PostTask(NewNonOwningRunnableMethod(
     181           0 :       "ipc::ProcessLink::OnCloseChannel", this, &ProcessLink::OnCloseChannel));
     182           0 : }
     183             : 
     184          10 : ThreadLink::ThreadLink(MessageChannel *aChan, MessageChannel *aTargetChan)
     185             :   : MessageLink(aChan),
     186          10 :     mTargetChan(aTargetChan)
     187             : {
     188          10 : }
     189             : 
     190           0 : ThreadLink::~ThreadLink()
     191             : {
     192           0 :     MOZ_ASSERT(mChan);
     193           0 :     MOZ_ASSERT(mChan->mMonitor);
     194           0 :     MonitorAutoLock lock(*mChan->mMonitor);
     195             : 
     196             :     // Bug 848949: We need to prevent the other side
     197             :     // from sending us any more messages to avoid Use-After-Free.
     198             :     // The setup here is as shown:
     199             :     //
     200             :     //          (Us)         (Them)
     201             :     //       MessageChannel  MessageChannel
     202             :     //         |  ^     \ /     ^ |
     203             :     //         |  |      X      | |
     204             :     //         v  |     / \     | v
     205             :     //        ThreadLink   ThreadLink
     206             :     //
     207             :     // We want to null out the diagonal link from their ThreadLink
     208             :     // to our MessageChannel.  Note that we must hold the monitor so
     209             :     // that we do this atomically with respect to them trying to send
     210             :     // us a message.  Since the channels share the same monitor this
     211             :     // also protects against the two ~ThreadLink() calls racing.
     212           0 :     if (mTargetChan) {
     213           0 :         MOZ_ASSERT(mTargetChan->mLink);
     214           0 :         static_cast<ThreadLink*>(mTargetChan->mLink)->mTargetChan = nullptr;
     215             :     }
     216           0 :     mTargetChan = nullptr;
     217           0 : }
     218             : 
     219             : void
     220           0 : ThreadLink::EchoMessage(Message *msg)
     221             : {
     222           0 :     mChan->AssertWorkerThread();
     223           0 :     mChan->mMonitor->AssertCurrentThreadOwns();
     224             : 
     225           0 :     mChan->OnMessageReceivedFromLink(Move(*msg));
     226           0 :     delete msg;
     227           0 : }
     228             : 
     229             : void
     230         166 : ThreadLink::SendMessage(Message *msg)
     231             : {
     232         166 :     if (!mChan->mIsPostponingSends) {
     233         166 :         mChan->AssertWorkerThread();
     234             :     }
     235         166 :     mChan->mMonitor->AssertCurrentThreadOwns();
     236             : 
     237         166 :     if (mTargetChan)
     238         166 :         mTargetChan->OnMessageReceivedFromLink(Move(*msg));
     239         166 :     delete msg;
     240         166 : }
     241             : 
     242             : void
     243           0 : ThreadLink::SendClose()
     244             : {
     245           0 :     mChan->AssertWorkerThread();
     246           0 :     mChan->mMonitor->AssertCurrentThreadOwns();
     247             : 
     248           0 :     mChan->mChannelState = ChannelClosed;
     249             : 
     250             :     // In a ProcessLink, we would close our half the channel.  This
     251             :     // would show up on the other side as an error on the I/O thread.
     252             :     // The I/O thread would then invoke OnChannelErrorFromLink().
     253             :     // As usual, we skip that process and just invoke the
     254             :     // OnChannelErrorFromLink() method directly.
     255           0 :     if (mTargetChan)
     256           0 :         mTargetChan->OnChannelErrorFromLink();
     257           0 : }
     258             : 
     259             : bool
     260           0 : ThreadLink::Unsound_IsClosed() const
     261             : {
     262           0 :     MonitorAutoLock lock(*mChan->mMonitor);
     263           0 :     return mChan->mChannelState == ChannelClosed;
     264             : }
     265             : 
     266             : uint32_t
     267           0 : ThreadLink::Unsound_NumQueuedMessages() const
     268             : {
     269             :     // ThreadLinks don't have a message queue.
     270           0 :     return 0;
     271             : }
     272             : 
     273             : //
     274             : // The methods below run in the context of the IO thread
     275             : //
     276             : 
     277             : void
     278         365 : ProcessLink::OnMessageReceived(Message&& msg)
     279             : {
     280         365 :     AssertIOThread();
     281         365 :     NS_ASSERTION(mChan->mChannelState != ChannelError, "Shouldn't get here!");
     282         730 :     MonitorAutoLock lock(*mChan->mMonitor);
     283         365 :     mChan->OnMessageReceivedFromLink(Move(msg));
     284         365 : }
     285             : 
     286             : void
     287           0 : ProcessLink::OnEchoMessage(Message* msg)
     288             : {
     289           0 :     AssertIOThread();
     290           0 :     OnMessageReceived(Move(*msg));
     291           0 :     delete msg;
     292           0 : }
     293             : 
     294             : void
     295          26 : ProcessLink::OnChannelOpened()
     296             : {
     297          26 :     AssertIOThread();
     298             : 
     299             :     {
     300          52 :         MonitorAutoLock lock(*mChan->mMonitor);
     301             : 
     302          26 :         mExistingListener = mTransport->set_listener(this);
     303             : #ifdef DEBUG
     304          26 :         if (mExistingListener) {
     305           4 :             std::queue<Message> pending;
     306           2 :             mExistingListener->GetQueuedMessages(pending);
     307           2 :             MOZ_ASSERT(pending.empty());
     308             :         }
     309             : #endif  // DEBUG
     310             : 
     311          26 :         mChan->mChannelState = ChannelOpening;
     312          26 :         lock.Notify();
     313             :     }
     314          26 :     /*assert*/mTransport->Connect();
     315          26 : }
     316             : 
     317             : void
     318           2 : ProcessLink::OnTakeConnectedChannel()
     319             : {
     320           2 :     AssertIOThread();
     321             : 
     322           4 :     std::queue<Message> pending;
     323             :     {
     324           4 :         MonitorAutoLock lock(*mChan->mMonitor);
     325             : 
     326           2 :         mChan->mChannelState = ChannelConnected;
     327             : 
     328           2 :         mExistingListener = mTransport->set_listener(this);
     329           2 :         if (mExistingListener) {
     330           2 :             mExistingListener->GetQueuedMessages(pending);
     331             :         }
     332           2 :         lock.Notify();
     333             :     }
     334             : 
     335             :     // Dispatch whatever messages the previous listener had queued up.
     336           2 :     while (!pending.empty()) {
     337           0 :         OnMessageReceived(Move(pending.front()));
     338           0 :         pending.pop();
     339             :     }
     340           2 : }
     341             : 
     342             : void
     343          23 : ProcessLink::OnChannelConnected(int32_t peer_pid)
     344             : {
     345          23 :     AssertIOThread();
     346             : 
     347          23 :     bool notifyChannel = false;
     348             : 
     349             :     {
     350          46 :         MonitorAutoLock lock(*mChan->mMonitor);
     351             :         // Only update channel state if its still thinks its opening.  Do not
     352             :         // force it into connected if it has errored out, started closing, etc.
     353          23 :         if (mChan->mChannelState == ChannelOpening) {
     354          21 :           mChan->mChannelState = ChannelConnected;
     355          21 :           mChan->mMonitor->Notify();
     356          21 :           notifyChannel = true;
     357             :         }
     358             :     }
     359             : 
     360          23 :     if (mExistingListener)
     361           4 :         mExistingListener->OnChannelConnected(peer_pid);
     362             : 
     363          23 :     if (notifyChannel) {
     364          21 :       mChan->OnChannelConnected(peer_pid);
     365             :     }
     366          23 : }
     367             : 
     368             : void
     369           0 : ProcessLink::OnChannelConnectError()
     370             : {
     371           0 :     AssertIOThread();
     372             : 
     373           0 :     MonitorAutoLock lock(*mChan->mMonitor);
     374             : 
     375           0 :     mChan->OnChannelErrorFromLink();
     376           0 : }
     377             : 
     378             : void
     379           0 : ProcessLink::OnChannelError()
     380             : {
     381           0 :     AssertIOThread();
     382             : 
     383           0 :     MonitorAutoLock lock(*mChan->mMonitor);
     384             : 
     385           0 :     MOZ_ALWAYS_TRUE(this == mTransport->set_listener(mExistingListener));
     386             : 
     387           0 :     mChan->OnChannelErrorFromLink();
     388           0 : }
     389             : 
     390             : void
     391           0 : ProcessLink::OnCloseChannel()
     392             : {
     393           0 :     AssertIOThread();
     394             : 
     395           0 :     mTransport->Close();
     396             : 
     397           0 :     MonitorAutoLock lock(*mChan->mMonitor);
     398             : 
     399             :     DebugOnly<IPC::Channel::Listener*> previousListener =
     400           0 :       mTransport->set_listener(mExistingListener);
     401             : 
     402             :     // OnChannelError may have reset the listener already.
     403           0 :     MOZ_ASSERT(previousListener == this ||
     404             :                previousListener == mExistingListener);
     405             : 
     406           0 :     mChan->mChannelState = ChannelClosed;
     407           0 :     mChan->mMonitor->Notify();
     408           0 : }
     409             : 
     410             : bool
     411           0 : ProcessLink::Unsound_IsClosed() const
     412             : {
     413           0 :     return mTransport->Unsound_IsClosed();
     414             : }
     415             : 
     416             : uint32_t
     417           0 : ProcessLink::Unsound_NumQueuedMessages() const
     418             : {
     419           0 :     return mTransport->Unsound_NumQueuedMessages();
     420             : }
     421             : 
     422             : } // namespace ipc
     423             : } // namespace mozilla

Generated by: LCOV version 1.13