Coverage Report

Created: 2018-09-25 14:53

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