1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: sw=2 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/MessageChannel.h"
9
10 #include <math.h>
11
12 #include <utility>
13
14 #include "CrashAnnotations.h"
15 #include "mozilla/Assertions.h"
16 #include "mozilla/CycleCollectedJSContext.h"
17 #include "mozilla/DebugOnly.h"
18 #include "mozilla/IntentionalCrash.h"
19 #include "mozilla/Logging.h"
20 #include "mozilla/Mutex.h"
21 #include "mozilla/ProfilerMarkers.h"
22 #include "mozilla/ScopeExit.h"
23 #include "mozilla/Sprintf.h"
24 #include "mozilla/StaticMutex.h"
25 #include "mozilla/Telemetry.h"
26 #include "mozilla/TimeStamp.h"
27 #include "mozilla/UniquePtrExtensions.h"
28 #include "mozilla/dom/ScriptSettings.h"
29 #include "mozilla/ipc/NodeController.h"
30 #include "mozilla/ipc/ProcessChild.h"
31 #include "mozilla/ipc/ProtocolUtils.h"
32 #include "nsAppRunner.h"
33 #include "nsContentUtils.h"
34 #include "nsTHashMap.h"
35 #include "nsDebug.h"
36 #include "nsExceptionHandler.h"
37 #include "nsIMemoryReporter.h"
38 #include "nsISupportsImpl.h"
39 #include "nsPrintfCString.h"
40 #include "nsThreadUtils.h"
41
42 #ifdef OS_WIN
43 # include "mozilla/gfx/Logging.h"
44 #endif
45
46 // Undo the damage done by mozzconf.h
47 #undef compress
48
49 static mozilla::LazyLogModule sLogModule("ipc");
50 #define IPC_LOG(...) MOZ_LOG(sLogModule, LogLevel::Debug, (__VA_ARGS__))
51
52 /*
53 * IPC design:
54 *
55 * There are three kinds of messages: async, sync, and intr. Sync and intr
56 * messages are blocking.
57 *
58 * Terminology: To dispatch a message Foo is to run the RecvFoo code for
59 * it. This is also called "handling" the message.
60 *
61 * Sync and async messages can sometimes "nest" inside other sync messages
62 * (i.e., while waiting for the sync reply, we can dispatch the inner
63 * message). Intr messages cannot nest. The three possible nesting levels are
64 * NOT_NESTED, NESTED_INSIDE_SYNC, and NESTED_INSIDE_CPOW. The intended uses
65 * are:
66 * NOT_NESTED - most messages.
67 * NESTED_INSIDE_SYNC - CPOW-related messages, which are always sync
68 * and can go in either direction.
69 * NESTED_INSIDE_CPOW - messages where we don't want to dispatch
70 * incoming CPOWs while waiting for the response.
71 * These nesting levels are ordered: NOT_NESTED, NESTED_INSIDE_SYNC,
72 * NESTED_INSIDE_CPOW. Async messages cannot be NESTED_INSIDE_SYNC but they can
73 * be NESTED_INSIDE_CPOW.
74 *
75 * To avoid jank, the parent process is not allowed to send NOT_NESTED sync
76 * messages. When a process is waiting for a response to a sync message M0, it
77 * will dispatch an incoming message M if:
78 * 1. M has a higher nesting level than M0, or
79 * 2. if M has the same nesting level as M0 and we're in the child, or
80 * 3. if M has the same nesting level as M0 and it was sent by the other side
81 * while dispatching M0.
82 * The idea is that messages with higher nesting should take precendence. The
83 * purpose of rule 2 is to handle a race where both processes send to each other
84 * simultaneously. In this case, we resolve the race in favor of the parent (so
85 * the child dispatches first).
86 *
87 * Messages satisfy the following properties:
88 * A. When waiting for a response to a sync message, we won't dispatch any
89 * messages of nesting level.
90 * B. Messages of the same nesting level will be dispatched roughly in the
91 * order they were sent. The exception is when the parent and child send
92 * sync messages to each other simulataneously. In this case, the parent's
93 * message is dispatched first. While it is dispatched, the child may send
94 * further nested messages, and these messages may be dispatched before the
95 * child's original message. We can consider ordering to be preserved here
96 * because we pretend that the child's original message wasn't sent until
97 * after the parent's message is finished being dispatched.
98 *
99 * When waiting for a sync message reply, we dispatch an async message only if
100 * it is NESTED_INSIDE_CPOW. Normally NESTED_INSIDE_CPOW async
101 * messages are sent only from the child. However, the parent can send
102 * NESTED_INSIDE_CPOW async messages when it is creating a bridged protocol.
103 *
104 * Intr messages are blocking and can nest, but they don't participate in the
105 * nesting levels. While waiting for an intr response, all incoming messages are
106 * dispatched until a response is received. When two intr messages race with
107 * each other, a similar scheme is used to ensure that one side wins. The
108 * winning side is chosen based on the message type.
109 *
110 * Intr messages differ from sync messages in that, while sending an intr
111 * message, we may dispatch an async message. This causes some additional
112 * complexity. One issue is that replies can be received out of order. It's also
113 * more difficult to determine whether one message is nested inside
114 * another. Consequently, intr handling uses mOutOfTurnReplies and
115 * mRemoteStackDepthGuess, which are not needed for sync messages.
116 */
117
118 using namespace mozilla;
119 using namespace mozilla::ipc;
120
121 using mozilla::MonitorAutoLock;
122 using mozilla::MonitorAutoUnlock;
123 using mozilla::dom::AutoNoJSAPI;
124
125 #define IPC_ASSERT(_cond, ...) \
126 do { \
127 if (!(_cond)) DebugAbort(__FILE__, __LINE__, #_cond, ##__VA_ARGS__); \
128 } while (0)
129
130 static MessageChannel* gParentProcessBlocker;
131
132 namespace mozilla {
133 namespace ipc {
134
135 static const uint32_t kMinTelemetryMessageSize = 4096;
136
137 // Note: we round the time we spend to the nearest millisecond. So a min value
138 // of 1 ms actually captures from 500us and above.
139 static const uint32_t kMinTelemetryIPCWriteLatencyMs = 1;
140
141 // Note: we round the time we spend waiting for a response to the nearest
142 // millisecond. So a min value of 1 ms actually captures from 500us and above.
143 // This is used for both the sending and receiving side telemetry for sync IPC,
144 // (IPC_SYNC_MAIN_LATENCY_MS and IPC_SYNC_RECEIVE_MS).
145 static const uint32_t kMinTelemetrySyncIPCLatencyMs = 1;
146
147 const int32_t MessageChannel::kNoTimeout = INT32_MIN;
148
149 // static
150 bool MessageChannel::sIsPumpingMessages = false;
151
152 enum Direction { IN_MESSAGE, OUT_MESSAGE };
153
154 class MessageChannel::InterruptFrame {
155 private:
156 enum Semantics { INTR_SEMS, SYNC_SEMS, ASYNC_SEMS };
157
158 public:
InterruptFrame(Direction direction,const Message * msg)159 InterruptFrame(Direction direction, const Message* msg)
160 : mMessageName(msg->name()),
161 mMessageRoutingId(msg->routing_id()),
162 mMesageSemantics(msg->is_interrupt() ? INTR_SEMS
163 : msg->is_sync() ? SYNC_SEMS
164 : ASYNC_SEMS),
165 mDirection(direction),
166 mMoved(false) {
167 MOZ_RELEASE_ASSERT(mMessageName);
168 }
169
InterruptFrame(InterruptFrame && aOther)170 InterruptFrame(InterruptFrame&& aOther) {
171 MOZ_RELEASE_ASSERT(aOther.mMessageName);
172 mMessageName = aOther.mMessageName;
173 aOther.mMessageName = nullptr;
174 mMoved = aOther.mMoved;
175 aOther.mMoved = true;
176
177 mMessageRoutingId = aOther.mMessageRoutingId;
178 mMesageSemantics = aOther.mMesageSemantics;
179 mDirection = aOther.mDirection;
180 }
181
~InterruptFrame()182 ~InterruptFrame() { MOZ_RELEASE_ASSERT(mMessageName || mMoved); }
183
operator =(InterruptFrame && aOther)184 InterruptFrame& operator=(InterruptFrame&& aOther) {
185 MOZ_RELEASE_ASSERT(&aOther != this);
186 this->~InterruptFrame();
187 new (this) InterruptFrame(std::move(aOther));
188 return *this;
189 }
190
IsInterruptIncall() const191 bool IsInterruptIncall() const {
192 return INTR_SEMS == mMesageSemantics && IN_MESSAGE == mDirection;
193 }
194
IsInterruptOutcall() const195 bool IsInterruptOutcall() const {
196 return INTR_SEMS == mMesageSemantics && OUT_MESSAGE == mDirection;
197 }
198
IsOutgoingSync() const199 bool IsOutgoingSync() const {
200 return (mMesageSemantics == INTR_SEMS || mMesageSemantics == SYNC_SEMS) &&
201 mDirection == OUT_MESSAGE;
202 }
203
Describe(int32_t * id,const char ** dir,const char ** sems,const char ** name) const204 void Describe(int32_t* id, const char** dir, const char** sems,
205 const char** name) const {
206 *id = mMessageRoutingId;
207 *dir = (IN_MESSAGE == mDirection) ? "in" : "out";
208 *sems = (INTR_SEMS == mMesageSemantics) ? "intr"
209 : (SYNC_SEMS == mMesageSemantics) ? "sync"
210 : "async";
211 *name = mMessageName;
212 }
213
GetRoutingId() const214 int32_t GetRoutingId() const { return mMessageRoutingId; }
215
216 private:
217 const char* mMessageName;
218 int32_t mMessageRoutingId;
219 Semantics mMesageSemantics;
220 Direction mDirection;
221 bool mMoved;
222
223 // Disable harmful methods.
224 InterruptFrame(const InterruptFrame& aOther) = delete;
225 InterruptFrame& operator=(const InterruptFrame&) = delete;
226 };
227
228 class MOZ_STACK_CLASS MessageChannel::CxxStackFrame {
229 public:
CxxStackFrame(MessageChannel & that,Direction direction,const Message * msg)230 CxxStackFrame(MessageChannel& that, Direction direction, const Message* msg)
231 : mThat(that) {
232 mThat.AssertWorkerThread();
233
234 if (mThat.mCxxStackFrames.empty()) mThat.EnteredCxxStack();
235
236 if (!mThat.mCxxStackFrames.append(InterruptFrame(direction, msg)))
237 MOZ_CRASH();
238
239 const InterruptFrame& frame = mThat.mCxxStackFrames.back();
240
241 if (frame.IsInterruptIncall()) mThat.EnteredCall();
242
243 if (frame.IsOutgoingSync()) mThat.EnteredSyncSend();
244
245 mThat.mSawInterruptOutMsg |= frame.IsInterruptOutcall();
246 }
247
~CxxStackFrame()248 ~CxxStackFrame() {
249 mThat.AssertWorkerThread();
250
251 MOZ_RELEASE_ASSERT(!mThat.mCxxStackFrames.empty());
252
253 const InterruptFrame& frame = mThat.mCxxStackFrames.back();
254 bool exitingSync = frame.IsOutgoingSync();
255 bool exitingCall = frame.IsInterruptIncall();
256 mThat.mCxxStackFrames.shrinkBy(1);
257
258 bool exitingStack = mThat.mCxxStackFrames.empty();
259
260 // According how lifetime is declared, mListener on MessageChannel
261 // lives longer than MessageChannel itself. Hence is expected to
262 // be alive. There is nothing to even assert here, there is no place
263 // we would be nullifying mListener on MessageChannel.
264
265 if (exitingCall) mThat.ExitedCall();
266
267 if (exitingSync) mThat.ExitedSyncSend();
268
269 if (exitingStack) mThat.ExitedCxxStack();
270 }
271
272 private:
273 MessageChannel& mThat;
274
275 // Disable harmful methods.
276 CxxStackFrame() = delete;
277 CxxStackFrame(const CxxStackFrame&) = delete;
278 CxxStackFrame& operator=(const CxxStackFrame&) = delete;
279 };
280
281 class AutoEnterTransaction {
282 public:
AutoEnterTransaction(MessageChannel * aChan,int32_t aMsgSeqno,int32_t aTransactionID,int aNestedLevel)283 explicit AutoEnterTransaction(MessageChannel* aChan, int32_t aMsgSeqno,
284 int32_t aTransactionID, int aNestedLevel)
285 : mChan(aChan),
286 mActive(true),
287 mOutgoing(true),
288 mNestedLevel(aNestedLevel),
289 mSeqno(aMsgSeqno),
290 mTransaction(aTransactionID),
291 mNext(mChan->mTransactionStack) {
292 mChan->mMonitor->AssertCurrentThreadOwns();
293 mChan->mTransactionStack = this;
294 }
295
AutoEnterTransaction(MessageChannel * aChan,const IPC::Message & aMessage)296 explicit AutoEnterTransaction(MessageChannel* aChan,
297 const IPC::Message& aMessage)
298 : mChan(aChan),
299 mActive(true),
300 mOutgoing(false),
301 mNestedLevel(aMessage.nested_level()),
302 mSeqno(aMessage.seqno()),
303 mTransaction(aMessage.transaction_id()),
304 mNext(mChan->mTransactionStack) {
305 mChan->mMonitor->AssertCurrentThreadOwns();
306
307 if (!aMessage.is_sync()) {
308 mActive = false;
309 return;
310 }
311
312 mChan->mTransactionStack = this;
313 }
314
~AutoEnterTransaction()315 ~AutoEnterTransaction() {
316 mChan->mMonitor->AssertCurrentThreadOwns();
317 if (mActive) {
318 mChan->mTransactionStack = mNext;
319 }
320 }
321
Cancel()322 void Cancel() {
323 AutoEnterTransaction* cur = mChan->mTransactionStack;
324 MOZ_RELEASE_ASSERT(cur == this);
325 while (cur && cur->mNestedLevel != IPC::Message::NOT_NESTED) {
326 // Note that, in the following situation, we will cancel multiple
327 // transactions:
328 // 1. Parent sends NESTED_INSIDE_SYNC message P1 to child.
329 // 2. Child sends NESTED_INSIDE_SYNC message C1 to child.
330 // 3. Child dispatches P1, parent blocks.
331 // 4. Child cancels.
332 // In this case, both P1 and C1 are cancelled. The parent will
333 // remove C1 from its queue when it gets the cancellation message.
334 MOZ_RELEASE_ASSERT(cur->mActive);
335 cur->mActive = false;
336 cur = cur->mNext;
337 }
338
339 mChan->mTransactionStack = cur;
340
341 MOZ_RELEASE_ASSERT(IsComplete());
342 }
343
AwaitingSyncReply() const344 bool AwaitingSyncReply() const {
345 MOZ_RELEASE_ASSERT(mActive);
346 if (mOutgoing) {
347 return true;
348 }
349 return mNext ? mNext->AwaitingSyncReply() : false;
350 }
351
AwaitingSyncReplyNestedLevel() const352 int AwaitingSyncReplyNestedLevel() const {
353 MOZ_RELEASE_ASSERT(mActive);
354 if (mOutgoing) {
355 return mNestedLevel;
356 }
357 return mNext ? mNext->AwaitingSyncReplyNestedLevel() : 0;
358 }
359
DispatchingSyncMessage() const360 bool DispatchingSyncMessage() const {
361 MOZ_RELEASE_ASSERT(mActive);
362 if (!mOutgoing) {
363 return true;
364 }
365 return mNext ? mNext->DispatchingSyncMessage() : false;
366 }
367
DispatchingSyncMessageNestedLevel() const368 int DispatchingSyncMessageNestedLevel() const {
369 MOZ_RELEASE_ASSERT(mActive);
370 if (!mOutgoing) {
371 return mNestedLevel;
372 }
373 return mNext ? mNext->DispatchingSyncMessageNestedLevel() : 0;
374 }
375
NestedLevel() const376 int NestedLevel() const {
377 MOZ_RELEASE_ASSERT(mActive);
378 return mNestedLevel;
379 }
380
SequenceNumber() const381 int32_t SequenceNumber() const {
382 MOZ_RELEASE_ASSERT(mActive);
383 return mSeqno;
384 }
385
TransactionID() const386 int32_t TransactionID() const {
387 MOZ_RELEASE_ASSERT(mActive);
388 return mTransaction;
389 }
390
ReceivedReply(IPC::Message && aMessage)391 void ReceivedReply(IPC::Message&& aMessage) {
392 MOZ_RELEASE_ASSERT(aMessage.seqno() == mSeqno);
393 MOZ_RELEASE_ASSERT(aMessage.transaction_id() == mTransaction);
394 MOZ_RELEASE_ASSERT(!mReply);
395 IPC_LOG("Reply received on worker thread: seqno=%d", mSeqno);
396 mReply = MakeUnique<IPC::Message>(std::move(aMessage));
397 MOZ_RELEASE_ASSERT(IsComplete());
398 }
399
HandleReply(IPC::Message && aMessage)400 void HandleReply(IPC::Message&& aMessage) {
401 AutoEnterTransaction* cur = mChan->mTransactionStack;
402 MOZ_RELEASE_ASSERT(cur == this);
403 while (cur) {
404 MOZ_RELEASE_ASSERT(cur->mActive);
405 if (aMessage.seqno() == cur->mSeqno) {
406 cur->ReceivedReply(std::move(aMessage));
407 break;
408 }
409 cur = cur->mNext;
410 MOZ_RELEASE_ASSERT(cur);
411 }
412 }
413
IsComplete()414 bool IsComplete() { return !mActive || mReply; }
415
IsOutgoing()416 bool IsOutgoing() { return mOutgoing; }
417
IsCanceled()418 bool IsCanceled() { return !mActive; }
419
IsBottom() const420 bool IsBottom() const { return !mNext; }
421
IsError()422 bool IsError() {
423 MOZ_RELEASE_ASSERT(mReply);
424 return mReply->is_reply_error();
425 }
426
GetReply()427 UniquePtr<IPC::Message> GetReply() { return std::move(mReply); }
428
429 private:
430 MessageChannel* mChan;
431
432 // Active is true if this transaction is on the mChan->mTransactionStack
433 // stack. Generally we're not on the stack if the transaction was canceled
434 // or if it was for a message that doesn't require transactions (an async
435 // message).
436 bool mActive;
437
438 // Is this stack frame for an outgoing message?
439 bool mOutgoing;
440
441 // Properties of the message being sent/received.
442 int mNestedLevel;
443 int32_t mSeqno;
444 int32_t mTransaction;
445
446 // Next item in mChan->mTransactionStack.
447 AutoEnterTransaction* mNext;
448
449 // Pointer the a reply received for this message, if one was received.
450 UniquePtr<IPC::Message> mReply;
451 };
452
453 class PendingResponseReporter final : public nsIMemoryReporter {
454 ~PendingResponseReporter() = default;
455
456 public:
457 NS_DECL_THREADSAFE_ISUPPORTS
458
459 NS_IMETHOD
CollectReports(nsIHandleReportCallback * aHandleReport,nsISupports * aData,bool aAnonymize)460 CollectReports(nsIHandleReportCallback* aHandleReport, nsISupports* aData,
461 bool aAnonymize) override {
462 MOZ_COLLECT_REPORT(
463 "unresolved-ipc-responses", KIND_OTHER, UNITS_COUNT,
464 MessageChannel::gUnresolvedResponses,
465 "Outstanding IPC async message responses that are still not resolved.");
466 return NS_OK;
467 }
468 };
469
470 NS_IMPL_ISUPPORTS(PendingResponseReporter, nsIMemoryReporter)
471
472 class ChannelCountReporter final : public nsIMemoryReporter {
473 ~ChannelCountReporter() = default;
474
475 struct ChannelCounts {
476 size_t mNow;
477 size_t mMax;
478
ChannelCountsmozilla::ipc::ChannelCountReporter::ChannelCounts479 ChannelCounts() : mNow(0), mMax(0) {}
480
Incmozilla::ipc::ChannelCountReporter::ChannelCounts481 void Inc() {
482 ++mNow;
483 if (mMax < mNow) {
484 mMax = mNow;
485 }
486 }
487
Decmozilla::ipc::ChannelCountReporter::ChannelCounts488 void Dec() {
489 MOZ_ASSERT(mNow > 0);
490 --mNow;
491 }
492 };
493
494 using CountTable = nsTHashMap<nsDepCharHashKey, ChannelCounts>;
495
496 static StaticMutex sChannelCountMutex;
497 static CountTable* sChannelCounts;
498
499 public:
500 NS_DECL_THREADSAFE_ISUPPORTS
501
502 NS_IMETHOD
CollectReports(nsIHandleReportCallback * aHandleReport,nsISupports * aData,bool aAnonymize)503 CollectReports(nsIHandleReportCallback* aHandleReport, nsISupports* aData,
504 bool aAnonymize) override {
505 AutoTArray<std::pair<const char*, ChannelCounts>, 16> counts;
506 {
507 StaticMutexAutoLock countLock(sChannelCountMutex);
508 if (!sChannelCounts) {
509 return NS_OK;
510 }
511 counts.SetCapacity(sChannelCounts->Count());
512 for (const auto& entry : *sChannelCounts) {
513 counts.AppendElement(std::pair{entry.GetKey(), entry.GetData()});
514 }
515 }
516
517 for (const auto& entry : counts) {
518 nsPrintfCString pathNow("ipc-channels/%s", entry.first);
519 nsPrintfCString pathMax("ipc-channels-peak/%s", entry.first);
520 nsPrintfCString descNow(
521 "Number of IPC channels for"
522 " top-level actor type %s",
523 entry.first);
524 nsPrintfCString descMax(
525 "Peak number of IPC channels for"
526 " top-level actor type %s",
527 entry.first);
528
529 aHandleReport->Callback(""_ns, pathNow, KIND_OTHER, UNITS_COUNT,
530 entry.second.mNow, descNow, aData);
531 aHandleReport->Callback(""_ns, pathMax, KIND_OTHER, UNITS_COUNT,
532 entry.second.mMax, descMax, aData);
533 }
534 return NS_OK;
535 }
536
Increment(const char * aName)537 static void Increment(const char* aName) {
538 StaticMutexAutoLock countLock(sChannelCountMutex);
539 if (!sChannelCounts) {
540 sChannelCounts = new CountTable;
541 }
542 sChannelCounts->LookupOrInsert(aName).Inc();
543 }
544
Decrement(const char * aName)545 static void Decrement(const char* aName) {
546 StaticMutexAutoLock countLock(sChannelCountMutex);
547 MOZ_ASSERT(sChannelCounts);
548 sChannelCounts->LookupOrInsert(aName).Dec();
549 }
550 };
551
552 StaticMutex ChannelCountReporter::sChannelCountMutex;
553 ChannelCountReporter::CountTable* ChannelCountReporter::sChannelCounts;
554
NS_IMPL_ISUPPORTS(ChannelCountReporter,nsIMemoryReporter)555 NS_IMPL_ISUPPORTS(ChannelCountReporter, nsIMemoryReporter)
556
557 // In child processes, the first MessageChannel is created before
558 // XPCOM is initialized enough to construct the memory reporter
559 // manager. This retries every time a MessageChannel is constructed,
560 // which is good enough in practice.
561 template <class Reporter>
562 static void TryRegisterStrongMemoryReporter() {
563 static Atomic<bool> registered;
564 if (registered.compareExchange(false, true)) {
565 RefPtr<Reporter> reporter = new Reporter();
566 if (NS_FAILED(RegisterStrongMemoryReporter(reporter))) {
567 registered = false;
568 }
569 }
570 }
571
572 Atomic<size_t> MessageChannel::gUnresolvedResponses;
573
MessageChannel(const char * aName,IToplevelProtocol * aListener)574 MessageChannel::MessageChannel(const char* aName, IToplevelProtocol* aListener)
575 : mName(aName),
576 mListener(aListener),
577 mChannelState(ChannelClosed),
578 mSide(UnknownSide),
579 mIsCrossProcess(false),
580 mChannelErrorTask(nullptr),
581 mTimeoutMs(kNoTimeout),
582 mInTimeoutSecondHalf(false),
583 mNextSeqno(0),
584 mLastSendError(SyncSendError::SendSuccess),
585 mDispatchingAsyncMessage(false),
586 mDispatchingAsyncMessageNestedLevel(0),
587 mTransactionStack(nullptr),
588 mTimedOutMessageSeqno(0),
589 mTimedOutMessageNestedLevel(0),
590 mMaybeDeferredPendingCount(0),
591 mRemoteStackDepthGuess(0),
592 mSawInterruptOutMsg(false),
593 mIsWaitingForIncoming(false),
594 mAbortOnError(false),
595 mNotifiedChannelDone(false),
596 mFlags(REQUIRE_DEFAULT),
597 mIsPostponingSends(false),
598 mBuildIDsConfirmedMatch(false),
599 mIsSameThreadChannel(false) {
600 MOZ_COUNT_CTOR(ipc::MessageChannel);
601
602 #ifdef OS_WIN
603 mTopFrame = nullptr;
604 mIsSyncWaitingOnNonMainThread = false;
605
606 mEvent = CreateEventW(nullptr, TRUE, FALSE, nullptr);
607 MOZ_RELEASE_ASSERT(mEvent, "CreateEvent failed! Nothing is going to work!");
608 #endif
609
610 TryRegisterStrongMemoryReporter<PendingResponseReporter>();
611 TryRegisterStrongMemoryReporter<ChannelCountReporter>();
612 }
613
~MessageChannel()614 MessageChannel::~MessageChannel() {
615 MOZ_COUNT_DTOR(ipc::MessageChannel);
616 IPC_ASSERT(mCxxStackFrames.empty(), "mismatched CxxStackFrame ctor/dtors");
617 #ifdef OS_WIN
618 if (mEvent) {
619 BOOL ok = CloseHandle(mEvent);
620 mEvent = nullptr;
621
622 if (!ok) {
623 gfxDevCrash(mozilla::gfx::LogReason::MessageChannelCloseFailure)
624 << "MessageChannel failed to close. GetLastError: " << GetLastError();
625 }
626 MOZ_RELEASE_ASSERT(ok);
627 } else {
628 gfxDevCrash(mozilla::gfx::LogReason::MessageChannelCloseFailure)
629 << "MessageChannel destructor ran without an mEvent Handle";
630 }
631 #endif
632 Clear();
633 }
634
635 #ifdef DEBUG
AssertMaybeDeferredCountCorrect()636 void MessageChannel::AssertMaybeDeferredCountCorrect() {
637 size_t count = 0;
638 for (MessageTask* task : mPending) {
639 if (!IsAlwaysDeferred(task->Msg())) {
640 count++;
641 }
642 }
643
644 MOZ_ASSERT(count == mMaybeDeferredPendingCount);
645 }
646 #endif
647
648 // This function returns the current transaction ID. Since the notion of a
649 // "current transaction" can be hard to define when messages race with each
650 // other and one gets canceled and the other doesn't, we require that this
651 // function is only called when the current transaction is known to be for a
652 // NESTED_INSIDE_SYNC message. In that case, we know for sure what the caller is
653 // looking for.
CurrentNestedInsideSyncTransaction() const654 int32_t MessageChannel::CurrentNestedInsideSyncTransaction() const {
655 mMonitor->AssertCurrentThreadOwns();
656 if (!mTransactionStack) {
657 return 0;
658 }
659 MOZ_RELEASE_ASSERT(mTransactionStack->NestedLevel() ==
660 IPC::Message::NESTED_INSIDE_SYNC);
661 return mTransactionStack->TransactionID();
662 }
663
AwaitingSyncReply() const664 bool MessageChannel::AwaitingSyncReply() const {
665 mMonitor->AssertCurrentThreadOwns();
666 return mTransactionStack ? mTransactionStack->AwaitingSyncReply() : false;
667 }
668
AwaitingSyncReplyNestedLevel() const669 int MessageChannel::AwaitingSyncReplyNestedLevel() const {
670 mMonitor->AssertCurrentThreadOwns();
671 return mTransactionStack ? mTransactionStack->AwaitingSyncReplyNestedLevel()
672 : 0;
673 }
674
DispatchingSyncMessage() const675 bool MessageChannel::DispatchingSyncMessage() const {
676 mMonitor->AssertCurrentThreadOwns();
677 return mTransactionStack ? mTransactionStack->DispatchingSyncMessage()
678 : false;
679 }
680
DispatchingSyncMessageNestedLevel() const681 int MessageChannel::DispatchingSyncMessageNestedLevel() const {
682 mMonitor->AssertCurrentThreadOwns();
683 return mTransactionStack
684 ? mTransactionStack->DispatchingSyncMessageNestedLevel()
685 : 0;
686 }
687
PrintErrorMessage(Side side,const char * channelName,const char * msg)688 static void PrintErrorMessage(Side side, const char* channelName,
689 const char* msg) {
690 const char* from = (side == ChildSide)
691 ? "Child"
692 : ((side == ParentSide) ? "Parent" : "Unknown");
693 printf_stderr("\n###!!! [%s][%s] Error: %s\n\n", from, channelName, msg);
694 }
695
Connected() const696 bool MessageChannel::Connected() const {
697 mMonitor->AssertCurrentThreadOwns();
698
699 // The transport layer allows us to send messages before
700 // receiving the "connected" ack from the remote side.
701 return (ChannelOpening == mChannelState || ChannelConnected == mChannelState);
702 }
703
CanSend() const704 bool MessageChannel::CanSend() const {
705 if (!mMonitor) {
706 return false;
707 }
708 MonitorAutoLock lock(*mMonitor);
709 return Connected();
710 }
711
Clear()712 void MessageChannel::Clear() {
713 // Don't clear mWorkerThread; we use it in AssertWorkerThread().
714 //
715 // Also don't clear mListener. If we clear it, then sending a message
716 // through this channel after it's Clear()'ed can cause this process to
717 // crash.
718 //
719 // In practice, mListener owns the channel, so the channel gets deleted
720 // before mListener. But just to be safe, mListener is a weak pointer.
721
722 #if !defined(ANDROID)
723 if (!Unsound_IsClosed()) {
724 CrashReporter::AnnotateCrashReport(
725 CrashReporter::Annotation::IPCFatalErrorProtocol,
726 nsDependentCString(mName));
727 switch (mChannelState) {
728 case ChannelOpening:
729 MOZ_CRASH(
730 "MessageChannel destroyed without being closed "
731 "(mChannelState == ChannelOpening).");
732 break;
733 case ChannelConnected:
734 MOZ_CRASH(
735 "MessageChannel destroyed without being closed "
736 "(mChannelState == ChannelConnected).");
737 break;
738 case ChannelTimeout:
739 MOZ_CRASH(
740 "MessageChannel destroyed without being closed "
741 "(mChannelState == ChannelTimeout).");
742 break;
743 case ChannelClosing:
744 MOZ_CRASH(
745 "MessageChannel destroyed without being closed "
746 "(mChannelState == ChannelClosing).");
747 break;
748 case ChannelError:
749 MOZ_CRASH(
750 "MessageChannel destroyed without being closed "
751 "(mChannelState == ChannelError).");
752 break;
753 default:
754 MOZ_CRASH("MessageChannel destroyed without being closed.");
755 }
756 }
757 #endif
758
759 if (gParentProcessBlocker == this) {
760 gParentProcessBlocker = nullptr;
761 }
762
763 gUnresolvedResponses -= mPendingResponses.size();
764 for (auto& pair : mPendingResponses) {
765 pair.second.get()->Reject(ResponseRejectReason::ChannelClosed);
766 }
767 mPendingResponses.clear();
768
769 if (mLink != nullptr && mIsCrossProcess) {
770 ChannelCountReporter::Decrement(mName);
771 }
772
773 if (mLink) {
774 mLink->PrepareToDestroy();
775 mLink = nullptr;
776 }
777
778 if (mChannelErrorTask) {
779 mChannelErrorTask->Cancel();
780 mChannelErrorTask = nullptr;
781 }
782
783 // Free up any memory used by pending messages.
784 mPending.clear();
785
786 mMaybeDeferredPendingCount = 0;
787
788 mOutOfTurnReplies.clear();
789 while (!mDeferred.empty()) {
790 mDeferred.pop();
791 }
792 }
793
Open(ScopedPort aPort,Side aSide,nsISerialEventTarget * aEventTarget)794 bool MessageChannel::Open(ScopedPort aPort, Side aSide,
795 nsISerialEventTarget* aEventTarget) {
796 MOZ_ASSERT(!mLink, "Open() called > once");
797
798 mMonitor = new RefCountedMonitor();
799 mWorkerThread = aEventTarget ? aEventTarget : GetCurrentSerialEventTarget();
800 MOZ_ASSERT(mWorkerThread, "We should always be on a nsISerialEventTarget");
801 mListener->OnIPCChannelOpened();
802
803 mLink = MakeUnique<PortLink>(this, std::move(aPort));
804 mSide = aSide;
805 return true;
806 }
807
GetOppSide(Side aSide)808 static Side GetOppSide(Side aSide) {
809 switch (aSide) {
810 case ChildSide:
811 return ParentSide;
812 case ParentSide:
813 return ChildSide;
814 default:
815 return UnknownSide;
816 }
817 }
818
Open(MessageChannel * aTargetChan,nsISerialEventTarget * aEventTarget,Side aSide)819 bool MessageChannel::Open(MessageChannel* aTargetChan,
820 nsISerialEventTarget* aEventTarget, Side aSide) {
821 // Opens a connection to another thread in the same process.
822
823 MOZ_ASSERT(aTargetChan, "Need a target channel");
824 MOZ_ASSERT(ChannelClosed == mChannelState, "Not currently closed");
825
826 std::pair<ScopedPort, ScopedPort> ports =
827 NodeController::GetSingleton()->CreatePortPair();
828
829 // NOTE: This dispatch must be sync as it captures locals by non-owning
830 // reference, however we can't use `NS_DISPATCH_SYNC` as that will spin a
831 // nested event loop, and doesn't work with certain types of calling event
832 // targets.
833 base::WaitableEvent event(/* manual_reset */ true,
834 /* initially_signaled */ false);
835 MOZ_ALWAYS_SUCCEEDS(aEventTarget->Dispatch(NS_NewCancelableRunnableFunction(
836 "ipc::MessageChannel::OpenAsOtherThread", [&]() {
837 aTargetChan->Open(std::move(ports.second), GetOppSide(aSide),
838 aEventTarget);
839 event.Signal();
840 })));
841 bool ok = event.Wait();
842 MOZ_RELEASE_ASSERT(ok);
843
844 // Now that the other side has connected, open the port on our side.
845 return Open(std::move(ports.first), aSide);
846 }
847
OpenOnSameThread(MessageChannel * aTargetChan,mozilla::ipc::Side aSide)848 bool MessageChannel::OpenOnSameThread(MessageChannel* aTargetChan,
849 mozilla::ipc::Side aSide) {
850 auto [porta, portb] = NodeController::GetSingleton()->CreatePortPair();
851
852 aTargetChan->mIsSameThreadChannel = true;
853 mIsSameThreadChannel = true;
854
855 auto* currentThread = GetCurrentSerialEventTarget();
856 return aTargetChan->Open(std::move(portb), GetOppSide(aSide),
857 currentThread) &&
858 Open(std::move(porta), aSide, currentThread);
859 }
860
Send(UniquePtr<Message> aMsg)861 bool MessageChannel::Send(UniquePtr<Message> aMsg) {
862 if (aMsg->size() >= kMinTelemetryMessageSize) {
863 Telemetry::Accumulate(Telemetry::IPC_MESSAGE_SIZE2, aMsg->size());
864 }
865
866 // If the message was created by the IPC bindings, the create time will be
867 // recorded. Use this information to report the
868 // IPC_WRITE_MAIN_THREAD_LATENCY_MS (time from message creation to it being
869 // sent).
870 if (NS_IsMainThread() && aMsg->create_time()) {
871 uint32_t latencyMs = round(
872 (mozilla::TimeStamp::Now() - aMsg->create_time()).ToMilliseconds());
873 if (latencyMs >= kMinTelemetryIPCWriteLatencyMs) {
874 mozilla::Telemetry::Accumulate(
875 mozilla::Telemetry::IPC_WRITE_MAIN_THREAD_LATENCY_MS,
876 nsDependentCString(aMsg->name()), latencyMs);
877 }
878 }
879
880 MOZ_RELEASE_ASSERT(!aMsg->is_sync());
881 MOZ_RELEASE_ASSERT(aMsg->nested_level() != IPC::Message::NESTED_INSIDE_SYNC);
882
883 CxxStackFrame frame(*this, OUT_MESSAGE, aMsg.get());
884
885 AssertWorkerThread();
886 mMonitor->AssertNotCurrentThreadOwns();
887 if (MSG_ROUTING_NONE == aMsg->routing_id()) {
888 ReportMessageRouteError("MessageChannel::Send");
889 return false;
890 }
891
892 if (aMsg->seqno() == 0) {
893 aMsg->set_seqno(NextSeqno());
894 }
895
896 MonitorAutoLock lock(*mMonitor);
897 if (!Connected()) {
898 ReportConnectionError("MessageChannel", aMsg.get());
899 return false;
900 }
901
902 AddProfilerMarker(*aMsg, MessageDirection::eSending);
903 SendMessageToLink(std::move(aMsg));
904 return true;
905 }
906
SendMessageToLink(UniquePtr<Message> aMsg)907 void MessageChannel::SendMessageToLink(UniquePtr<Message> aMsg) {
908 if (mIsPostponingSends) {
909 mPostponedSends.push_back(std::move(aMsg));
910 return;
911 }
912 mLink->SendMessage(std::move(aMsg));
913 }
914
BeginPostponingSends()915 void MessageChannel::BeginPostponingSends() {
916 AssertWorkerThread();
917 mMonitor->AssertNotCurrentThreadOwns();
918
919 MonitorAutoLock lock(*mMonitor);
920 {
921 MOZ_ASSERT(!mIsPostponingSends);
922 mIsPostponingSends = true;
923 }
924 }
925
StopPostponingSends()926 void MessageChannel::StopPostponingSends() {
927 // Note: this can be called from any thread.
928 MonitorAutoLock lock(*mMonitor);
929
930 MOZ_ASSERT(mIsPostponingSends);
931
932 for (UniquePtr<Message>& iter : mPostponedSends) {
933 mLink->SendMessage(std::move(iter));
934 }
935
936 // We unset this after SendMessage so we can make correct thread
937 // assertions in MessageLink.
938 mIsPostponingSends = false;
939 mPostponedSends.clear();
940 }
941
PopCallback(const Message & aMsg)942 UniquePtr<MessageChannel::UntypedCallbackHolder> MessageChannel::PopCallback(
943 const Message& aMsg) {
944 auto iter = mPendingResponses.find(aMsg.seqno());
945 if (iter != mPendingResponses.end()) {
946 UniquePtr<MessageChannel::UntypedCallbackHolder> ret =
947 std::move(iter->second);
948 mPendingResponses.erase(iter);
949 gUnresolvedResponses--;
950 return ret;
951 }
952 return nullptr;
953 }
954
RejectPendingResponsesForActor(ActorIdType aActorId)955 void MessageChannel::RejectPendingResponsesForActor(ActorIdType aActorId) {
956 auto itr = mPendingResponses.begin();
957 while (itr != mPendingResponses.end()) {
958 if (itr->second.get()->mActorId != aActorId) {
959 ++itr;
960 continue;
961 }
962 itr->second.get()->Reject(ResponseRejectReason::ActorDestroyed);
963 // Take special care of advancing the iterator since we are
964 // removing it while iterating.
965 itr = mPendingResponses.erase(itr);
966 gUnresolvedResponses--;
967 }
968 }
969
970 class BuildIDsMatchMessage : public IPC::Message {
971 public:
BuildIDsMatchMessage()972 BuildIDsMatchMessage()
973 : IPC::Message(MSG_ROUTING_NONE, BUILD_IDS_MATCH_MESSAGE_TYPE) {}
Log(const std::string & aPrefix,FILE * aOutf) const974 void Log(const std::string& aPrefix, FILE* aOutf) const {
975 fputs("(special `Build IDs match' message)", aOutf);
976 }
977 };
978
979 // Send the parent a special async message to confirm when the parent and child
980 // are of the same buildID. Skips sending the message and returns false if the
981 // buildIDs don't match. This is a minor variation on
982 // MessageChannel::Send(Message* aMsg).
SendBuildIDsMatchMessage(const char * aParentBuildID)983 bool MessageChannel::SendBuildIDsMatchMessage(const char* aParentBuildID) {
984 MOZ_ASSERT(!XRE_IsParentProcess());
985
986 nsCString parentBuildID(aParentBuildID);
987 nsCString childBuildID(mozilla::PlatformBuildID());
988
989 if (parentBuildID != childBuildID) {
990 // The build IDs didn't match, usually because an update occurred in the
991 // background.
992 return false;
993 }
994
995 auto msg = MakeUnique<BuildIDsMatchMessage>();
996
997 MOZ_RELEASE_ASSERT(!msg->is_sync());
998 MOZ_RELEASE_ASSERT(msg->nested_level() != IPC::Message::NESTED_INSIDE_SYNC);
999
1000 AssertWorkerThread();
1001 mMonitor->AssertNotCurrentThreadOwns();
1002 // Don't check for MSG_ROUTING_NONE.
1003
1004 MonitorAutoLock lock(*mMonitor);
1005 if (!Connected()) {
1006 ReportConnectionError("MessageChannel", msg.get());
1007 return false;
1008 }
1009
1010 #if defined(MOZ_DEBUG) && defined(ENABLE_TESTS)
1011 // Technically, the behavior is interesting for any kind of process
1012 // but when exercising tests, we want to crash only a content process and
1013 // avoid making noise with other kind of processes crashing
1014 if (const char* dontSend = PR_GetEnv("MOZ_BUILDID_MATCH_DONTSEND")) {
1015 if (dontSend[0] == '1') {
1016 // Bug 1732999: We are going to crash, so we need to advise leak check
1017 // tooling to avoid intermittent missing leakcheck
1018 NoteIntentionalCrash(XRE_GetProcessTypeString());
1019 if (XRE_IsContentProcess()) {
1020 // Make sure we do not die too early, as this causes weird behavior
1021 PR_Sleep(PR_MillisecondsToInterval(1000));
1022 return false;
1023 }
1024 }
1025 }
1026 #endif
1027
1028 mLink->SendMessage(std::move(msg));
1029 return true;
1030 }
1031
1032 class CancelMessage : public IPC::Message {
1033 public:
CancelMessage(int transaction)1034 explicit CancelMessage(int transaction)
1035 : IPC::Message(MSG_ROUTING_NONE, CANCEL_MESSAGE_TYPE) {
1036 set_transaction_id(transaction);
1037 }
Read(const Message * msg)1038 static bool Read(const Message* msg) { return true; }
Log(const std::string & aPrefix,FILE * aOutf) const1039 void Log(const std::string& aPrefix, FILE* aOutf) const {
1040 fputs("(special `Cancel' message)", aOutf);
1041 }
1042 };
1043
MaybeInterceptSpecialIOMessage(const Message & aMsg)1044 bool MessageChannel::MaybeInterceptSpecialIOMessage(const Message& aMsg) {
1045 mMonitor->AssertCurrentThreadOwns();
1046
1047 if (MSG_ROUTING_NONE == aMsg.routing_id()) {
1048 if (GOODBYE_MESSAGE_TYPE == aMsg.type()) {
1049 // :TODO: Sort out Close() on this side racing with Close() on the
1050 // other side
1051 mChannelState = ChannelClosing;
1052 if (LoggingEnabled()) {
1053 printf("NOTE: %s process received `Goodbye', closing down\n",
1054 (mSide == ChildSide) ? "child" : "parent");
1055 }
1056 return true;
1057 } else if (CANCEL_MESSAGE_TYPE == aMsg.type()) {
1058 IPC_LOG("Cancel from message");
1059 CancelTransaction(aMsg.transaction_id());
1060 NotifyWorkerThread();
1061 return true;
1062 } else if (BUILD_IDS_MATCH_MESSAGE_TYPE == aMsg.type()) {
1063 IPC_LOG("Build IDs match message");
1064 mBuildIDsConfirmedMatch = true;
1065 return true;
1066 } else if (IMPENDING_SHUTDOWN_MESSAGE_TYPE == aMsg.type()) {
1067 IPC_LOG("Impending Shutdown received");
1068 ProcessChild::NotifyImpendingShutdown();
1069 return true;
1070 }
1071 }
1072 return false;
1073 }
1074
1075 /* static */
IsAlwaysDeferred(const Message & aMsg)1076 bool MessageChannel::IsAlwaysDeferred(const Message& aMsg) {
1077 // If a message is not NESTED_INSIDE_CPOW and not sync, then we always defer
1078 // it.
1079 return aMsg.nested_level() != IPC::Message::NESTED_INSIDE_CPOW &&
1080 !aMsg.is_sync();
1081 }
1082
ShouldDeferMessage(const Message & aMsg)1083 bool MessageChannel::ShouldDeferMessage(const Message& aMsg) {
1084 // Never defer messages that have the highest nested level, even async
1085 // ones. This is safe because only the child can send these messages, so
1086 // they can never nest.
1087 if (aMsg.nested_level() == IPC::Message::NESTED_INSIDE_CPOW) {
1088 MOZ_ASSERT(!IsAlwaysDeferred(aMsg));
1089 return false;
1090 }
1091
1092 // Unless they're NESTED_INSIDE_CPOW, we always defer async messages.
1093 // Note that we never send an async NESTED_INSIDE_SYNC message.
1094 if (!aMsg.is_sync()) {
1095 MOZ_RELEASE_ASSERT(aMsg.nested_level() == IPC::Message::NOT_NESTED);
1096 MOZ_ASSERT(IsAlwaysDeferred(aMsg));
1097 return true;
1098 }
1099
1100 MOZ_ASSERT(!IsAlwaysDeferred(aMsg));
1101
1102 int msgNestedLevel = aMsg.nested_level();
1103 int waitingNestedLevel = AwaitingSyncReplyNestedLevel();
1104
1105 // Always defer if the nested level of the incoming message is less than the
1106 // nested level of the message we're awaiting.
1107 if (msgNestedLevel < waitingNestedLevel) return true;
1108
1109 // Never defer if the message has strictly greater nested level.
1110 if (msgNestedLevel > waitingNestedLevel) return false;
1111
1112 // When both sides send sync messages of the same nested level, we resolve the
1113 // race by dispatching in the child and deferring the incoming message in
1114 // the parent. However, the parent still needs to dispatch nested sync
1115 // messages.
1116 //
1117 // Deferring in the parent only sort of breaks message ordering. When the
1118 // child's message comes in, we can pretend the child hasn't quite
1119 // finished sending it yet. Since the message is sync, we know that the
1120 // child hasn't moved on yet.
1121 return mSide == ParentSide &&
1122 aMsg.transaction_id() != CurrentNestedInsideSyncTransaction();
1123 }
1124
OnMessageReceivedFromLink(Message && aMsg)1125 void MessageChannel::OnMessageReceivedFromLink(Message&& aMsg) {
1126 mMonitor->AssertCurrentThreadOwns();
1127
1128 if (MaybeInterceptSpecialIOMessage(aMsg)) {
1129 return;
1130 }
1131
1132 mListener->OnChannelReceivedMessage(aMsg);
1133
1134 // Regardless of the Interrupt stack, if we're awaiting a sync reply,
1135 // we know that it needs to be immediately handled to unblock us.
1136 if (aMsg.is_sync() && aMsg.is_reply()) {
1137 IPC_LOG("Received reply seqno=%d xid=%d", aMsg.seqno(),
1138 aMsg.transaction_id());
1139
1140 if (aMsg.seqno() == mTimedOutMessageSeqno) {
1141 // Drop the message, but allow future sync messages to be sent.
1142 IPC_LOG("Received reply to timedout message; igoring; xid=%d",
1143 mTimedOutMessageSeqno);
1144 EndTimeout();
1145 return;
1146 }
1147
1148 MOZ_RELEASE_ASSERT(AwaitingSyncReply());
1149 MOZ_RELEASE_ASSERT(!mTimedOutMessageSeqno);
1150
1151 mTransactionStack->HandleReply(std::move(aMsg));
1152 NotifyWorkerThread();
1153 return;
1154 }
1155
1156 // Nested messages cannot be compressed.
1157 MOZ_RELEASE_ASSERT(aMsg.compress_type() == IPC::Message::COMPRESSION_NONE ||
1158 aMsg.nested_level() == IPC::Message::NOT_NESTED);
1159
1160 bool reuseTask = false;
1161 if (aMsg.compress_type() == IPC::Message::COMPRESSION_ENABLED) {
1162 bool compress =
1163 (!mPending.isEmpty() &&
1164 mPending.getLast()->Msg().type() == aMsg.type() &&
1165 mPending.getLast()->Msg().routing_id() == aMsg.routing_id());
1166 if (compress) {
1167 // This message type has compression enabled, and the back of the
1168 // queue was the same message type and routed to the same destination.
1169 // Replace it with the newer message.
1170 MOZ_RELEASE_ASSERT(mPending.getLast()->Msg().compress_type() ==
1171 IPC::Message::COMPRESSION_ENABLED);
1172 mPending.getLast()->Msg() = std::move(aMsg);
1173
1174 reuseTask = true;
1175 }
1176 } else if (aMsg.compress_type() == IPC::Message::COMPRESSION_ALL &&
1177 !mPending.isEmpty()) {
1178 for (MessageTask* p = mPending.getLast(); p; p = p->getPrevious()) {
1179 if (p->Msg().type() == aMsg.type() &&
1180 p->Msg().routing_id() == aMsg.routing_id()) {
1181 // This message type has compression enabled, and the queue
1182 // holds a message with the same message type and routed to the
1183 // same destination. Erase it. Note that, since we always
1184 // compress these redundancies, There Can Be Only One.
1185 MOZ_RELEASE_ASSERT(p->Msg().compress_type() ==
1186 IPC::Message::COMPRESSION_ALL);
1187 MOZ_RELEASE_ASSERT(IsAlwaysDeferred(p->Msg()));
1188 p->remove();
1189 break;
1190 }
1191 }
1192 }
1193
1194 bool alwaysDeferred = IsAlwaysDeferred(aMsg);
1195
1196 bool wakeUpSyncSend = AwaitingSyncReply() && !ShouldDeferMessage(aMsg);
1197
1198 bool shouldWakeUp =
1199 AwaitingInterruptReply() || wakeUpSyncSend || AwaitingIncomingMessage();
1200
1201 // Although we usually don't need to post a message task if
1202 // shouldWakeUp is true, it's easier to post anyway than to have to
1203 // guarantee that every Send call processes everything it's supposed to
1204 // before returning.
1205 bool shouldPostTask = !shouldWakeUp || wakeUpSyncSend;
1206
1207 IPC_LOG("Receive from link; seqno=%d, xid=%d, shouldWakeUp=%d", aMsg.seqno(),
1208 aMsg.transaction_id(), shouldWakeUp);
1209
1210 if (reuseTask) {
1211 return;
1212 }
1213
1214 // There are three cases we're concerned about, relating to the state of the
1215 // main thread:
1216 //
1217 // (1) We are waiting on a sync reply - main thread is blocked on the
1218 // IPC monitor.
1219 // - If the message is NESTED_INSIDE_SYNC, we wake up the main thread to
1220 // deliver the message depending on ShouldDeferMessage. Otherwise, we
1221 // leave it in the mPending queue, posting a task to the main event
1222 // loop, where it will be processed once the synchronous reply has been
1223 // received.
1224 //
1225 // (2) We are waiting on an Interrupt reply - main thread is blocked on the
1226 // IPC monitor.
1227 // - Always notify and wake up the main thread.
1228 //
1229 // (3) We are not waiting on a reply.
1230 // - We post a task to the main event loop.
1231 //
1232 // Note that, we may notify the main thread even though the monitor is not
1233 // blocked. This is okay, since we always check for pending events before
1234 // blocking again.
1235
1236 RefPtr<MessageTask> task = new MessageTask(this, std::move(aMsg));
1237 mPending.insertBack(task);
1238
1239 if (!alwaysDeferred) {
1240 mMaybeDeferredPendingCount++;
1241 }
1242
1243 if (shouldWakeUp) {
1244 NotifyWorkerThread();
1245 }
1246
1247 if (shouldPostTask) {
1248 task->Post();
1249 }
1250 }
1251
PeekMessages(const std::function<bool (const Message & aMsg)> & aInvoke)1252 void MessageChannel::PeekMessages(
1253 const std::function<bool(const Message& aMsg)>& aInvoke) {
1254 // FIXME: We shouldn't be holding the lock for aInvoke!
1255 MonitorAutoLock lock(*mMonitor);
1256
1257 for (MessageTask* it : mPending) {
1258 const Message& msg = it->Msg();
1259 if (!aInvoke(msg)) {
1260 break;
1261 }
1262 }
1263 }
1264
ProcessPendingRequests(AutoEnterTransaction & aTransaction)1265 void MessageChannel::ProcessPendingRequests(
1266 AutoEnterTransaction& aTransaction) {
1267 mMonitor->AssertCurrentThreadOwns();
1268
1269 AssertMaybeDeferredCountCorrect();
1270 if (mMaybeDeferredPendingCount == 0) {
1271 return;
1272 }
1273
1274 IPC_LOG("ProcessPendingRequests for seqno=%d, xid=%d",
1275 aTransaction.SequenceNumber(), aTransaction.TransactionID());
1276
1277 // Loop until there aren't any more nested messages to process.
1278 for (;;) {
1279 // If we canceled during ProcessPendingRequest, then we need to leave
1280 // immediately because the results of ShouldDeferMessage will be
1281 // operating with weird state (as if no Send is in progress). That could
1282 // cause even NOT_NESTED sync messages to be processed (but not
1283 // NOT_NESTED async messages), which would break message ordering.
1284 if (aTransaction.IsCanceled()) {
1285 return;
1286 }
1287
1288 mozilla::Vector<Message> toProcess;
1289
1290 for (MessageTask* p = mPending.getFirst(); p;) {
1291 Message& msg = p->Msg();
1292
1293 MOZ_RELEASE_ASSERT(!aTransaction.IsCanceled(),
1294 "Calling ShouldDeferMessage when cancelled");
1295 bool defer = ShouldDeferMessage(msg);
1296
1297 // Only log the interesting messages.
1298 if (msg.is_sync() ||
1299 msg.nested_level() == IPC::Message::NESTED_INSIDE_CPOW) {
1300 IPC_LOG("ShouldDeferMessage(seqno=%d) = %d", msg.seqno(), defer);
1301 }
1302
1303 if (!defer) {
1304 MOZ_ASSERT(!IsAlwaysDeferred(msg));
1305
1306 if (!toProcess.append(std::move(msg))) MOZ_CRASH();
1307
1308 mMaybeDeferredPendingCount--;
1309
1310 p = p->removeAndGetNext();
1311 continue;
1312 }
1313 p = p->getNext();
1314 }
1315
1316 if (toProcess.empty()) {
1317 break;
1318 }
1319
1320 // Processing these messages could result in more messages, so we
1321 // loop around to check for more afterwards.
1322
1323 for (auto it = toProcess.begin(); it != toProcess.end(); it++) {
1324 ProcessPendingRequest(std::move(*it));
1325 }
1326 }
1327
1328 AssertMaybeDeferredCountCorrect();
1329 }
1330
Send(UniquePtr<Message> aMsg,Message * aReply)1331 bool MessageChannel::Send(UniquePtr<Message> aMsg, Message* aReply) {
1332 mozilla::TimeStamp start = TimeStamp::Now();
1333 if (aMsg->size() >= kMinTelemetryMessageSize) {
1334 Telemetry::Accumulate(Telemetry::IPC_MESSAGE_SIZE2, aMsg->size());
1335 }
1336
1337 // Sanity checks.
1338 AssertWorkerThread();
1339 mMonitor->AssertNotCurrentThreadOwns();
1340 MOZ_RELEASE_ASSERT(!mIsSameThreadChannel,
1341 "sync send over same-thread channel will deadlock!");
1342
1343 #ifdef OS_WIN
1344 SyncStackFrame frame(this, false);
1345 NeuteredWindowRegion neuteredRgn(mFlags &
1346 REQUIRE_DEFERRED_MESSAGE_PROTECTION);
1347 #endif
1348
1349 CxxStackFrame f(*this, OUT_MESSAGE, aMsg.get());
1350
1351 MonitorAutoLock lock(*mMonitor);
1352
1353 if (mTimedOutMessageSeqno) {
1354 // Don't bother sending another sync message if a previous one timed out
1355 // and we haven't received a reply for it. Once the original timed-out
1356 // message receives a reply, we'll be able to send more sync messages
1357 // again.
1358 IPC_LOG("Send() failed due to previous timeout");
1359 mLastSendError = SyncSendError::PreviousTimeout;
1360 return false;
1361 }
1362
1363 if (DispatchingSyncMessageNestedLevel() == IPC::Message::NOT_NESTED &&
1364 aMsg->nested_level() > IPC::Message::NOT_NESTED) {
1365 // Don't allow sending CPOWs while we're dispatching a sync message.
1366 IPC_LOG("Nested level forbids send");
1367 mLastSendError = SyncSendError::SendingCPOWWhileDispatchingSync;
1368 return false;
1369 }
1370
1371 if (DispatchingSyncMessageNestedLevel() == IPC::Message::NESTED_INSIDE_CPOW ||
1372 DispatchingAsyncMessageNestedLevel() ==
1373 IPC::Message::NESTED_INSIDE_CPOW) {
1374 // Generally only the parent dispatches urgent messages. And the only
1375 // sync messages it can send are NESTED_INSIDE_SYNC. Mainly we want to
1376 // ensure here that we don't return false for non-CPOW messages.
1377 MOZ_RELEASE_ASSERT(aMsg->nested_level() ==
1378 IPC::Message::NESTED_INSIDE_SYNC);
1379 IPC_LOG("Sending while dispatching urgent message");
1380 mLastSendError = SyncSendError::SendingCPOWWhileDispatchingUrgent;
1381 return false;
1382 }
1383
1384 if (aMsg->nested_level() < DispatchingSyncMessageNestedLevel() ||
1385 aMsg->nested_level() < AwaitingSyncReplyNestedLevel()) {
1386 MOZ_RELEASE_ASSERT(DispatchingSyncMessage() || DispatchingAsyncMessage());
1387 MOZ_RELEASE_ASSERT(!mIsPostponingSends);
1388 IPC_LOG("Cancel from Send");
1389 auto cancel =
1390 MakeUnique<CancelMessage>(CurrentNestedInsideSyncTransaction());
1391 CancelTransaction(CurrentNestedInsideSyncTransaction());
1392 mLink->SendMessage(std::move(cancel));
1393 }
1394
1395 IPC_ASSERT(aMsg->is_sync(), "can only Send() sync messages here");
1396
1397 IPC_ASSERT(aMsg->nested_level() >= DispatchingSyncMessageNestedLevel(),
1398 "can't send sync message of a lesser nested level than what's "
1399 "being dispatched");
1400 IPC_ASSERT(AwaitingSyncReplyNestedLevel() <= aMsg->nested_level(),
1401 "nested sync message sends must be of increasing nested level");
1402 IPC_ASSERT(
1403 DispatchingSyncMessageNestedLevel() != IPC::Message::NESTED_INSIDE_CPOW,
1404 "not allowed to send messages while dispatching urgent messages");
1405
1406 IPC_ASSERT(
1407 DispatchingAsyncMessageNestedLevel() != IPC::Message::NESTED_INSIDE_CPOW,
1408 "not allowed to send messages while dispatching urgent messages");
1409
1410 if (!Connected()) {
1411 ReportConnectionError("MessageChannel::SendAndWait", aMsg.get());
1412 mLastSendError = SyncSendError::NotConnectedBeforeSend;
1413 return false;
1414 }
1415
1416 aMsg->set_seqno(NextSeqno());
1417
1418 int32_t seqno = aMsg->seqno();
1419 int nestedLevel = aMsg->nested_level();
1420 msgid_t replyType = aMsg->type() + 1;
1421
1422 AutoEnterTransaction* stackTop = mTransactionStack;
1423
1424 // If the most recent message on the stack is NESTED_INSIDE_SYNC, then our
1425 // message should nest inside that and we use the same transaction
1426 // ID. Otherwise we need a new transaction ID (so we use the seqno of the
1427 // message we're sending).
1428 bool nest =
1429 stackTop && stackTop->NestedLevel() == IPC::Message::NESTED_INSIDE_SYNC;
1430 int32_t transaction = nest ? stackTop->TransactionID() : seqno;
1431 aMsg->set_transaction_id(transaction);
1432
1433 bool handleWindowsMessages = mListener->HandleWindowsMessages(*aMsg.get());
1434 AutoEnterTransaction transact(this, seqno, transaction, nestedLevel);
1435
1436 IPC_LOG("Send seqno=%d, xid=%d", seqno, transaction);
1437
1438 // aMsg will be destroyed soon, but name() is not owned by aMsg.
1439 const char* msgName = aMsg->name();
1440
1441 AddProfilerMarker(*aMsg, MessageDirection::eSending);
1442 SendMessageToLink(std::move(aMsg));
1443
1444 while (true) {
1445 MOZ_RELEASE_ASSERT(!transact.IsCanceled());
1446 ProcessPendingRequests(transact);
1447 if (transact.IsComplete()) {
1448 break;
1449 }
1450 if (!Connected()) {
1451 ReportConnectionError("MessageChannel::Send");
1452 mLastSendError = SyncSendError::DisconnectedDuringSend;
1453 return false;
1454 }
1455
1456 MOZ_RELEASE_ASSERT(!mTimedOutMessageSeqno);
1457 MOZ_RELEASE_ASSERT(!transact.IsComplete());
1458 MOZ_RELEASE_ASSERT(mTransactionStack == &transact);
1459
1460 bool maybeTimedOut = !WaitForSyncNotify(handleWindowsMessages);
1461
1462 if (mListener->NeedArtificialSleep()) {
1463 MonitorAutoUnlock unlock(*mMonitor);
1464 mListener->ArtificialSleep();
1465 }
1466
1467 if (!Connected()) {
1468 ReportConnectionError("MessageChannel::SendAndWait");
1469 mLastSendError = SyncSendError::DisconnectedDuringSend;
1470 return false;
1471 }
1472
1473 if (transact.IsCanceled()) {
1474 break;
1475 }
1476
1477 MOZ_RELEASE_ASSERT(mTransactionStack == &transact);
1478
1479 // We only time out a message if it initiated a new transaction (i.e.,
1480 // if neither side has any other message Sends on the stack).
1481 bool canTimeOut = transact.IsBottom();
1482 if (maybeTimedOut && canTimeOut && !ShouldContinueFromTimeout()) {
1483 // Since ShouldContinueFromTimeout drops the lock, we need to
1484 // re-check all our conditions here. We shouldn't time out if any of
1485 // these things happen because there won't be a reply to the timed
1486 // out message in these cases.
1487 if (transact.IsComplete()) {
1488 break;
1489 }
1490
1491 IPC_LOG("Timing out Send: xid=%d", transaction);
1492
1493 mTimedOutMessageSeqno = seqno;
1494 mTimedOutMessageNestedLevel = nestedLevel;
1495 mLastSendError = SyncSendError::TimedOut;
1496 return false;
1497 }
1498
1499 if (transact.IsCanceled()) {
1500 break;
1501 }
1502 }
1503
1504 if (transact.IsCanceled()) {
1505 IPC_LOG("Other side canceled seqno=%d, xid=%d", seqno, transaction);
1506 mLastSendError = SyncSendError::CancelledAfterSend;
1507 return false;
1508 }
1509
1510 if (transact.IsError()) {
1511 IPC_LOG("Error: seqno=%d, xid=%d", seqno, transaction);
1512 mLastSendError = SyncSendError::ReplyError;
1513 return false;
1514 }
1515
1516 uint32_t latencyMs = round((TimeStamp::Now() - start).ToMilliseconds());
1517 IPC_LOG("Got reply: seqno=%d, xid=%d, msgName=%s, latency=%ums", seqno,
1518 transaction, msgName, latencyMs);
1519
1520 UniquePtr<Message> reply = transact.GetReply();
1521
1522 MOZ_RELEASE_ASSERT(reply);
1523 MOZ_RELEASE_ASSERT(reply->is_reply(), "expected reply");
1524 MOZ_RELEASE_ASSERT(!reply->is_reply_error());
1525 MOZ_RELEASE_ASSERT(reply->seqno() == seqno);
1526 MOZ_RELEASE_ASSERT(reply->type() == replyType, "wrong reply type");
1527 MOZ_RELEASE_ASSERT(reply->is_sync());
1528
1529 AddProfilerMarker(*reply, MessageDirection::eReceiving);
1530
1531 *aReply = std::move(*reply);
1532 if (aReply->size() >= kMinTelemetryMessageSize) {
1533 Telemetry::Accumulate(Telemetry::IPC_REPLY_SIZE,
1534 nsDependentCString(msgName), aReply->size());
1535 }
1536
1537 // NOTE: Only collect IPC_SYNC_MAIN_LATENCY_MS on the main thread (bug
1538 // 1343729)
1539 if (NS_IsMainThread() && latencyMs >= kMinTelemetrySyncIPCLatencyMs) {
1540 Telemetry::Accumulate(Telemetry::IPC_SYNC_MAIN_LATENCY_MS,
1541 nsDependentCString(msgName), latencyMs);
1542 }
1543 return true;
1544 }
1545
Call(UniquePtr<Message> aMsg,Message * aReply)1546 bool MessageChannel::Call(UniquePtr<Message> aMsg, Message* aReply) {
1547 AssertWorkerThread();
1548 mMonitor->AssertNotCurrentThreadOwns();
1549 MOZ_RELEASE_ASSERT(!mIsSameThreadChannel,
1550 "intr call send over same-thread channel will deadlock!");
1551
1552 #ifdef OS_WIN
1553 SyncStackFrame frame(this, true);
1554 #endif
1555
1556 // This must come before MonitorAutoLock, as its destructor acquires the
1557 // monitor lock.
1558 CxxStackFrame cxxframe(*this, OUT_MESSAGE, aMsg.get());
1559
1560 MonitorAutoLock lock(*mMonitor);
1561 if (!Connected()) {
1562 ReportConnectionError("MessageChannel::Call", aMsg.get());
1563 return false;
1564 }
1565
1566 // Sanity checks.
1567 IPC_ASSERT(!AwaitingSyncReply(),
1568 "cannot issue Interrupt call while blocked on sync request");
1569 IPC_ASSERT(!DispatchingSyncMessage(), "violation of sync handler invariant");
1570 IPC_ASSERT(aMsg->is_interrupt(), "can only Call() Interrupt messages here");
1571 IPC_ASSERT(!mIsPostponingSends, "not postponing sends");
1572
1573 aMsg->set_seqno(NextSeqno());
1574 aMsg->set_interrupt_remote_stack_depth_guess(mRemoteStackDepthGuess);
1575 aMsg->set_interrupt_local_stack_depth(1 + InterruptStackDepth());
1576 mInterruptStack.push(MessageInfo(*aMsg));
1577
1578 AddProfilerMarker(*aMsg, MessageDirection::eSending);
1579
1580 mLink->SendMessage(std::move(aMsg));
1581
1582 while (true) {
1583 // if a handler invoked by *Dispatch*() spun a nested event
1584 // loop, and the connection was broken during that loop, we
1585 // might have already processed the OnError event. if so,
1586 // trying another loop iteration will be futile because
1587 // channel state will have been cleared
1588 if (!Connected()) {
1589 ReportConnectionError("MessageChannel::Call");
1590 return false;
1591 }
1592
1593 #ifdef OS_WIN
1594 // We need to limit the scoped of neuteredRgn to this spot in the code.
1595 // Window neutering can't be enabled during some plugin calls because
1596 // we then risk the neutered window procedure being subclassed by a
1597 // plugin.
1598 {
1599 NeuteredWindowRegion neuteredRgn(mFlags &
1600 REQUIRE_DEFERRED_MESSAGE_PROTECTION);
1601 /* We should pump messages at this point to ensure that the IPC
1602 peer does not become deadlocked on a pending inter-thread
1603 SendMessage() */
1604 neuteredRgn.PumpOnce();
1605 }
1606 #endif
1607
1608 // Now might be the time to process a message deferred because of race
1609 // resolution.
1610 MaybeUndeferIncall();
1611
1612 // Wait for an event to occur.
1613 while (!InterruptEventOccurred()) {
1614 bool maybeTimedOut = !WaitForInterruptNotify();
1615
1616 // We might have received a "subtly deferred" message in a nested
1617 // loop that it's now time to process.
1618 if (InterruptEventOccurred() ||
1619 (!maybeTimedOut &&
1620 (!mDeferred.empty() || !mOutOfTurnReplies.empty()))) {
1621 break;
1622 }
1623
1624 if (maybeTimedOut && !ShouldContinueFromTimeout()) return false;
1625 }
1626
1627 Message recvd;
1628 MessageMap::iterator it;
1629
1630 if ((it = mOutOfTurnReplies.find(mInterruptStack.top().seqno())) !=
1631 mOutOfTurnReplies.end()) {
1632 recvd = std::move(it->second);
1633 mOutOfTurnReplies.erase(it);
1634 } else if (!mPending.isEmpty()) {
1635 RefPtr<MessageTask> task = mPending.popFirst();
1636 recvd = std::move(task->Msg());
1637 if (!IsAlwaysDeferred(recvd)) {
1638 mMaybeDeferredPendingCount--;
1639 }
1640 } else {
1641 // because of subtleties with nested event loops, it's possible
1642 // that we got here and nothing happened. or, we might have a
1643 // deferred in-call that needs to be processed. either way, we
1644 // won't break the inner while loop again until something new
1645 // happens.
1646 continue;
1647 }
1648
1649 // If the message is not Interrupt, we can dispatch it as normal.
1650 if (!recvd.is_interrupt()) {
1651 DispatchMessage(std::move(recvd));
1652 if (!Connected()) {
1653 ReportConnectionError("MessageChannel::DispatchMessage");
1654 return false;
1655 }
1656 continue;
1657 }
1658
1659 // If the message is an Interrupt reply, either process it as a reply to our
1660 // call, or add it to the list of out-of-turn replies we've received.
1661 if (recvd.is_reply()) {
1662 IPC_ASSERT(!mInterruptStack.empty(), "invalid Interrupt stack");
1663
1664 // If this is not a reply the call we've initiated, add it to our
1665 // out-of-turn replies and keep polling for events.
1666 {
1667 const MessageInfo& outcall = mInterruptStack.top();
1668
1669 // Note, In the parent, sequence numbers increase from 0, and
1670 // in the child, they decrease from 0.
1671 if ((mSide == ChildSide && recvd.seqno() > outcall.seqno()) ||
1672 (mSide != ChildSide && recvd.seqno() < outcall.seqno())) {
1673 mOutOfTurnReplies[recvd.seqno()] = std::move(recvd);
1674 continue;
1675 }
1676
1677 IPC_ASSERT(
1678 recvd.is_reply_error() || (recvd.type() == (outcall.type() + 1) &&
1679 recvd.seqno() == outcall.seqno()),
1680 "somebody's misbehavin'", true);
1681 }
1682
1683 // We received a reply to our most recent outstanding call. Pop
1684 // this frame and return the reply.
1685 mInterruptStack.pop();
1686
1687 AddProfilerMarker(recvd, MessageDirection::eReceiving);
1688
1689 bool is_reply_error = recvd.is_reply_error();
1690 if (!is_reply_error) {
1691 *aReply = std::move(recvd);
1692 }
1693
1694 // If we have no more pending out calls waiting on replies, then
1695 // the reply queue should be empty.
1696 IPC_ASSERT(!mInterruptStack.empty() || mOutOfTurnReplies.empty(),
1697 "still have pending replies with no pending out-calls", true);
1698
1699 return !is_reply_error;
1700 }
1701
1702 // Dispatch an Interrupt in-call. Snapshot the current stack depth while we
1703 // own the monitor.
1704 size_t stackDepth = InterruptStackDepth();
1705 {
1706 MonitorAutoUnlock unlock(*mMonitor);
1707
1708 CxxStackFrame frame(*this, IN_MESSAGE, &recvd);
1709 RefPtr<ActorLifecycleProxy> listenerProxy =
1710 mListener->GetLifecycleProxy();
1711 DispatchInterruptMessage(listenerProxy, std::move(recvd), stackDepth);
1712 }
1713 if (!Connected()) {
1714 ReportConnectionError("MessageChannel::DispatchInterruptMessage");
1715 return false;
1716 }
1717 }
1718 }
1719
WaitForIncomingMessage()1720 bool MessageChannel::WaitForIncomingMessage() {
1721 #ifdef OS_WIN
1722 SyncStackFrame frame(this, true);
1723 NeuteredWindowRegion neuteredRgn(mFlags &
1724 REQUIRE_DEFERRED_MESSAGE_PROTECTION);
1725 #endif
1726
1727 MonitorAutoLock lock(*mMonitor);
1728 AutoEnterWaitForIncoming waitingForIncoming(*this);
1729 if (mChannelState != ChannelConnected) {
1730 return false;
1731 }
1732 if (!HasPendingEvents()) {
1733 return WaitForInterruptNotify();
1734 }
1735
1736 MOZ_RELEASE_ASSERT(!mPending.isEmpty());
1737 RefPtr<MessageTask> task = mPending.getFirst();
1738 RunMessage(*task);
1739 return true;
1740 }
1741
HasPendingEvents()1742 bool MessageChannel::HasPendingEvents() {
1743 AssertWorkerThread();
1744 mMonitor->AssertCurrentThreadOwns();
1745 return Connected() && !mPending.isEmpty();
1746 }
1747
InterruptEventOccurred()1748 bool MessageChannel::InterruptEventOccurred() {
1749 AssertWorkerThread();
1750 mMonitor->AssertCurrentThreadOwns();
1751 IPC_ASSERT(InterruptStackDepth() > 0, "not in wait loop");
1752
1753 return (!Connected() || !mPending.isEmpty() ||
1754 (!mOutOfTurnReplies.empty() &&
1755 mOutOfTurnReplies.find(mInterruptStack.top().seqno()) !=
1756 mOutOfTurnReplies.end()));
1757 }
1758
ProcessPendingRequest(Message && aUrgent)1759 bool MessageChannel::ProcessPendingRequest(Message&& aUrgent) {
1760 AssertWorkerThread();
1761 mMonitor->AssertCurrentThreadOwns();
1762
1763 IPC_LOG("Process pending: seqno=%d, xid=%d", aUrgent.seqno(),
1764 aUrgent.transaction_id());
1765
1766 DispatchMessage(std::move(aUrgent));
1767 if (!Connected()) {
1768 ReportConnectionError("MessageChannel::ProcessPendingRequest");
1769 return false;
1770 }
1771
1772 return true;
1773 }
1774
ShouldRunMessage(const Message & aMsg)1775 bool MessageChannel::ShouldRunMessage(const Message& aMsg) {
1776 if (!mTimedOutMessageSeqno) {
1777 return true;
1778 }
1779
1780 // If we've timed out a message and we're awaiting the reply to the timed
1781 // out message, we have to be careful what messages we process. Here's what
1782 // can go wrong:
1783 // 1. child sends a NOT_NESTED sync message S
1784 // 2. parent sends a NESTED_INSIDE_SYNC sync message H at the same time
1785 // 3. parent times out H
1786 // 4. child starts processing H and sends a NESTED_INSIDE_SYNC message H'
1787 // nested within the same transaction
1788 // 5. parent dispatches S and sends reply
1789 // 6. child asserts because it instead expected a reply to H'.
1790 //
1791 // To solve this, we refuse to process S in the parent until we get a reply
1792 // to H. More generally, let the timed out message be M. We don't process a
1793 // message unless the child would need the response to that message in order
1794 // to process M. Those messages are the ones that have a higher nested level
1795 // than M or that are part of the same transaction as M.
1796 if (aMsg.nested_level() < mTimedOutMessageNestedLevel ||
1797 (aMsg.nested_level() == mTimedOutMessageNestedLevel &&
1798 aMsg.transaction_id() != mTimedOutMessageSeqno)) {
1799 return false;
1800 }
1801
1802 return true;
1803 }
1804
RunMessage(MessageTask & aTask)1805 void MessageChannel::RunMessage(MessageTask& aTask) {
1806 AssertWorkerThread();
1807 mMonitor->AssertCurrentThreadOwns();
1808
1809 Message& msg = aTask.Msg();
1810
1811 if (!Connected()) {
1812 ReportConnectionError("RunMessage");
1813 return;
1814 }
1815
1816 // Check that we're going to run the first message that's valid to run.
1817 #if 0
1818 # ifdef DEBUG
1819 nsCOMPtr<nsIEventTarget> messageTarget =
1820 mListener->GetMessageEventTarget(msg);
1821
1822 for (MessageTask* task : mPending) {
1823 if (task == &aTask) {
1824 break;
1825 }
1826
1827 nsCOMPtr<nsIEventTarget> taskTarget =
1828 mListener->GetMessageEventTarget(task->Msg());
1829
1830 MOZ_ASSERT(!ShouldRunMessage(task->Msg()) ||
1831 taskTarget != messageTarget ||
1832 aTask.Msg().priority() != task->Msg().priority());
1833
1834 }
1835 # endif
1836 #endif
1837
1838 if (!mDeferred.empty()) {
1839 MaybeUndeferIncall();
1840 }
1841
1842 if (!ShouldRunMessage(msg)) {
1843 return;
1844 }
1845
1846 MOZ_RELEASE_ASSERT(aTask.isInList());
1847 aTask.remove();
1848
1849 if (!IsAlwaysDeferred(msg)) {
1850 mMaybeDeferredPendingCount--;
1851 }
1852
1853 if (IsOnCxxStack() && msg.is_interrupt() && msg.is_reply()) {
1854 // We probably just received a reply in a nested loop for an
1855 // Interrupt call sent before entering that loop.
1856 mOutOfTurnReplies[msg.seqno()] = std::move(msg);
1857 return;
1858 }
1859
1860 DispatchMessage(std::move(msg));
1861 }
1862
NS_IMPL_ISUPPORTS_INHERITED(MessageChannel::MessageTask,CancelableRunnable,nsIRunnablePriority,nsIRunnableIPCMessageType)1863 NS_IMPL_ISUPPORTS_INHERITED(MessageChannel::MessageTask, CancelableRunnable,
1864 nsIRunnablePriority, nsIRunnableIPCMessageType)
1865
1866 MessageChannel::MessageTask::MessageTask(MessageChannel* aChannel,
1867 Message&& aMessage)
1868 : CancelableRunnable(aMessage.name()),
1869 mMonitor(aChannel->mMonitor),
1870 mChannel(aChannel),
1871 mMessage(std::move(aMessage)),
1872 mScheduled(false) {}
1873
Run()1874 nsresult MessageChannel::MessageTask::Run() {
1875 mMonitor->AssertNotCurrentThreadOwns();
1876
1877 MonitorAutoLock lock(*mMonitor);
1878
1879 // In case we choose not to run this message, we may need to be able to Post
1880 // it again.
1881 mScheduled = false;
1882
1883 if (!isInList()) {
1884 return NS_OK;
1885 }
1886
1887 Channel()->AssertWorkerThread();
1888 Channel()->RunMessage(*this);
1889 return NS_OK;
1890 }
1891
1892 // Warning: This method removes the receiver from whatever list it might be in.
Cancel()1893 nsresult MessageChannel::MessageTask::Cancel() {
1894 mMonitor->AssertNotCurrentThreadOwns();
1895
1896 MonitorAutoLock lock(*mMonitor);
1897
1898 if (!isInList()) {
1899 return NS_OK;
1900 }
1901
1902 Channel()->AssertWorkerThread();
1903 if (!IsAlwaysDeferred(Msg())) {
1904 Channel()->mMaybeDeferredPendingCount--;
1905 }
1906
1907 remove();
1908
1909 return NS_OK;
1910 }
1911
Post()1912 void MessageChannel::MessageTask::Post() {
1913 mMonitor->AssertCurrentThreadOwns();
1914 MOZ_RELEASE_ASSERT(!mScheduled);
1915 MOZ_RELEASE_ASSERT(isInList());
1916
1917 mScheduled = true;
1918
1919 RefPtr<MessageTask> self = this;
1920 nsCOMPtr<nsISerialEventTarget> eventTarget =
1921 Channel()->mListener->GetMessageEventTarget(mMessage);
1922
1923 if (eventTarget) {
1924 eventTarget->Dispatch(self.forget(), NS_DISPATCH_NORMAL);
1925 } else {
1926 Channel()->mWorkerThread->Dispatch(self.forget());
1927 }
1928 }
1929
1930 NS_IMETHODIMP
GetPriority(uint32_t * aPriority)1931 MessageChannel::MessageTask::GetPriority(uint32_t* aPriority) {
1932 switch (mMessage.priority()) {
1933 case Message::NORMAL_PRIORITY:
1934 *aPriority = PRIORITY_NORMAL;
1935 break;
1936 case Message::INPUT_PRIORITY:
1937 *aPriority = PRIORITY_INPUT_HIGH;
1938 break;
1939 case Message::VSYNC_PRIORITY:
1940 *aPriority = PRIORITY_VSYNC;
1941 break;
1942 case Message::MEDIUMHIGH_PRIORITY:
1943 *aPriority = PRIORITY_MEDIUMHIGH;
1944 break;
1945 case Message::CONTROL_PRIORITY:
1946 *aPriority = PRIORITY_CONTROL;
1947 break;
1948 default:
1949 MOZ_ASSERT(false);
1950 break;
1951 }
1952 return NS_OK;
1953 }
1954
1955 NS_IMETHODIMP
GetType(uint32_t * aType)1956 MessageChannel::MessageTask::GetType(uint32_t* aType) {
1957 if (!Msg().is_valid()) {
1958 // If mMessage has been moved already elsewhere, we can't know what the type
1959 // has been.
1960 return NS_ERROR_FAILURE;
1961 }
1962
1963 *aType = Msg().type();
1964 return NS_OK;
1965 }
1966
DispatchMessage(Message && aMsg)1967 void MessageChannel::DispatchMessage(Message&& aMsg) {
1968 AssertWorkerThread();
1969 mMonitor->AssertCurrentThreadOwns();
1970
1971 RefPtr<ActorLifecycleProxy> listenerProxy = mListener->GetLifecycleProxy();
1972
1973 Maybe<AutoNoJSAPI> nojsapi;
1974 if (NS_IsMainThread() && CycleCollectedJSContext::Get()) {
1975 nojsapi.emplace();
1976 }
1977
1978 UniquePtr<Message> reply;
1979
1980 IPC_LOG("DispatchMessage: seqno=%d, xid=%d", aMsg.seqno(),
1981 aMsg.transaction_id());
1982 AddProfilerMarker(aMsg, MessageDirection::eReceiving);
1983
1984 {
1985 AutoEnterTransaction transaction(this, aMsg);
1986
1987 int id = aMsg.transaction_id();
1988 MOZ_RELEASE_ASSERT(!aMsg.is_sync() || id == transaction.TransactionID());
1989
1990 {
1991 MonitorAutoUnlock unlock(*mMonitor);
1992 CxxStackFrame frame(*this, IN_MESSAGE, &aMsg);
1993
1994 mListener->ArtificialSleep();
1995
1996 if (aMsg.is_sync()) {
1997 DispatchSyncMessage(listenerProxy, aMsg, *getter_Transfers(reply));
1998 } else if (aMsg.is_interrupt()) {
1999 DispatchInterruptMessage(listenerProxy, std::move(aMsg), 0);
2000 } else {
2001 DispatchAsyncMessage(listenerProxy, aMsg);
2002 }
2003
2004 mListener->ArtificialSleep();
2005 }
2006
2007 if (reply && transaction.IsCanceled()) {
2008 // The transaction has been canceled. Don't send a reply.
2009 IPC_LOG("Nulling out reply due to cancellation, seqno=%d, xid=%d",
2010 aMsg.seqno(), id);
2011 reply = nullptr;
2012 }
2013 }
2014
2015 if (reply && ChannelConnected == mChannelState) {
2016 IPC_LOG("Sending reply seqno=%d, xid=%d", aMsg.seqno(),
2017 aMsg.transaction_id());
2018 AddProfilerMarker(*reply, MessageDirection::eSending);
2019
2020 mLink->SendMessage(std::move(reply));
2021 }
2022 }
2023
DispatchSyncMessage(ActorLifecycleProxy * aProxy,const Message & aMsg,Message * & aReply)2024 void MessageChannel::DispatchSyncMessage(ActorLifecycleProxy* aProxy,
2025 const Message& aMsg,
2026 Message*& aReply) {
2027 AssertWorkerThread();
2028
2029 mozilla::TimeStamp start = TimeStamp::Now();
2030
2031 int nestedLevel = aMsg.nested_level();
2032
2033 MOZ_RELEASE_ASSERT(nestedLevel == IPC::Message::NOT_NESTED ||
2034 NS_IsMainThread());
2035
2036 MessageChannel* dummy;
2037 MessageChannel*& blockingVar =
2038 mSide == ChildSide && NS_IsMainThread() ? gParentProcessBlocker : dummy;
2039
2040 Result rv;
2041 {
2042 AutoSetValue<MessageChannel*> blocked(blockingVar, this);
2043 rv = aProxy->Get()->OnMessageReceived(aMsg, aReply);
2044 }
2045
2046 uint32_t latencyMs = round((TimeStamp::Now() - start).ToMilliseconds());
2047 if (latencyMs >= kMinTelemetrySyncIPCLatencyMs) {
2048 Telemetry::Accumulate(Telemetry::IPC_SYNC_RECEIVE_MS,
2049 nsDependentCString(aMsg.name()), latencyMs);
2050 }
2051
2052 if (!MaybeHandleError(rv, aMsg, "DispatchSyncMessage")) {
2053 aReply = Message::ForSyncDispatchError(aMsg.nested_level());
2054 }
2055 aReply->set_seqno(aMsg.seqno());
2056 aReply->set_transaction_id(aMsg.transaction_id());
2057 }
2058
DispatchAsyncMessage(ActorLifecycleProxy * aProxy,const Message & aMsg)2059 void MessageChannel::DispatchAsyncMessage(ActorLifecycleProxy* aProxy,
2060 const Message& aMsg) {
2061 AssertWorkerThread();
2062 MOZ_RELEASE_ASSERT(!aMsg.is_interrupt() && !aMsg.is_sync());
2063
2064 if (aMsg.routing_id() == MSG_ROUTING_NONE) {
2065 NS_WARNING("unhandled special message!");
2066 MaybeHandleError(MsgNotKnown, aMsg, "DispatchAsyncMessage");
2067 return;
2068 }
2069
2070 Result rv;
2071 {
2072 int nestedLevel = aMsg.nested_level();
2073 AutoSetValue<bool> async(mDispatchingAsyncMessage, true);
2074 AutoSetValue<int> nestedLevelSet(mDispatchingAsyncMessageNestedLevel,
2075 nestedLevel);
2076 rv = aProxy->Get()->OnMessageReceived(aMsg);
2077 }
2078 MaybeHandleError(rv, aMsg, "DispatchAsyncMessage");
2079 }
2080
DispatchInterruptMessage(ActorLifecycleProxy * aProxy,Message && aMsg,size_t stackDepth)2081 void MessageChannel::DispatchInterruptMessage(ActorLifecycleProxy* aProxy,
2082 Message&& aMsg,
2083 size_t stackDepth) {
2084 AssertWorkerThread();
2085 mMonitor->AssertNotCurrentThreadOwns();
2086
2087 IPC_ASSERT(aMsg.is_interrupt() && !aMsg.is_reply(), "wrong message type");
2088
2089 if (ShouldDeferInterruptMessage(aMsg, stackDepth)) {
2090 // We now know the other side's stack has one more frame
2091 // than we thought.
2092 ++mRemoteStackDepthGuess; // decremented in MaybeProcessDeferred()
2093 mDeferred.push(std::move(aMsg));
2094 return;
2095 }
2096
2097 // If we "lost" a race and need to process the other side's in-call, we
2098 // don't need to fix up the mRemoteStackDepthGuess here, because we're just
2099 // about to increment it, which will make it correct again.
2100
2101 #ifdef OS_WIN
2102 SyncStackFrame frame(this, true);
2103 #endif
2104
2105 UniquePtr<Message> reply;
2106
2107 ++mRemoteStackDepthGuess;
2108 Result rv = aProxy->Get()->OnCallReceived(aMsg, *getter_Transfers(reply));
2109 --mRemoteStackDepthGuess;
2110
2111 if (!MaybeHandleError(rv, aMsg, "DispatchInterruptMessage")) {
2112 reply = WrapUnique(Message::ForInterruptDispatchError());
2113 }
2114 reply->set_seqno(aMsg.seqno());
2115
2116 MonitorAutoLock lock(*mMonitor);
2117 if (ChannelConnected == mChannelState) {
2118 AddProfilerMarker(*reply, MessageDirection::eSending);
2119 mLink->SendMessage(std::move(reply));
2120 }
2121 }
2122
ShouldDeferInterruptMessage(const Message & aMsg,size_t aStackDepth)2123 bool MessageChannel::ShouldDeferInterruptMessage(const Message& aMsg,
2124 size_t aStackDepth) {
2125 AssertWorkerThread();
2126
2127 // We may or may not own the lock in this function, so don't access any
2128 // channel state.
2129
2130 IPC_ASSERT(aMsg.is_interrupt() && !aMsg.is_reply(), "wrong message type");
2131
2132 // Race detection: see the long comment near mRemoteStackDepthGuess in
2133 // MessageChannel.h. "Remote" stack depth means our side, and "local" means
2134 // the other side.
2135 if (aMsg.interrupt_remote_stack_depth_guess() ==
2136 RemoteViewOfStackDepth(aStackDepth) ||
2137 mInterruptStack.empty()) {
2138 return false;
2139 }
2140
2141 // Interrupt in-calls have raced. The winner, if there is one, gets to defer
2142 // processing of the other side's in-call.
2143 bool defer;
2144 const char* winner;
2145 const MessageInfo parentMsgInfo =
2146 (mSide == ChildSide) ? MessageInfo(aMsg) : mInterruptStack.top();
2147 const MessageInfo childMsgInfo =
2148 (mSide == ChildSide) ? mInterruptStack.top() : MessageInfo(aMsg);
2149 switch (mListener->MediateInterruptRace(parentMsgInfo, childMsgInfo)) {
2150 case RIPChildWins:
2151 winner = "child";
2152 defer = (mSide == ChildSide);
2153 break;
2154 case RIPParentWins:
2155 winner = "parent";
2156 defer = (mSide != ChildSide);
2157 break;
2158 case RIPError:
2159 MOZ_CRASH("NYI: 'Error' Interrupt race policy");
2160 default:
2161 MOZ_CRASH("not reached");
2162 }
2163
2164 IPC_LOG("race in %s: %s won", (mSide == ChildSide) ? "child" : "parent",
2165 winner);
2166
2167 return defer;
2168 }
2169
MaybeUndeferIncall()2170 void MessageChannel::MaybeUndeferIncall() {
2171 AssertWorkerThread();
2172 mMonitor->AssertCurrentThreadOwns();
2173
2174 if (mDeferred.empty()) return;
2175
2176 size_t stackDepth = InterruptStackDepth();
2177
2178 Message& deferred = mDeferred.top();
2179
2180 // the other side can only *under*-estimate our actual stack depth
2181 IPC_ASSERT(deferred.interrupt_remote_stack_depth_guess() <= stackDepth,
2182 "fatal logic error");
2183
2184 if (ShouldDeferInterruptMessage(deferred, stackDepth)) {
2185 return;
2186 }
2187
2188 // maybe time to process this message
2189 Message call(std::move(deferred));
2190 mDeferred.pop();
2191
2192 // fix up fudge factor we added to account for race
2193 IPC_ASSERT(0 < mRemoteStackDepthGuess, "fatal logic error");
2194 --mRemoteStackDepthGuess;
2195
2196 MOZ_RELEASE_ASSERT(call.nested_level() == IPC::Message::NOT_NESTED);
2197 RefPtr<MessageTask> task = new MessageTask(this, std::move(call));
2198 mPending.insertBack(task);
2199 MOZ_ASSERT(IsAlwaysDeferred(task->Msg()));
2200 task->Post();
2201 }
2202
EnteredCxxStack()2203 void MessageChannel::EnteredCxxStack() { mListener->EnteredCxxStack(); }
2204
ExitedCxxStack()2205 void MessageChannel::ExitedCxxStack() {
2206 mListener->ExitedCxxStack();
2207 if (mSawInterruptOutMsg) {
2208 MonitorAutoLock lock(*mMonitor);
2209 // see long comment in OnMaybeDequeueOne()
2210 EnqueuePendingMessages();
2211 mSawInterruptOutMsg = false;
2212 }
2213 }
2214
EnteredCall()2215 void MessageChannel::EnteredCall() { mListener->EnteredCall(); }
2216
ExitedCall()2217 void MessageChannel::ExitedCall() { mListener->ExitedCall(); }
2218
EnteredSyncSend()2219 void MessageChannel::EnteredSyncSend() { mListener->OnEnteredSyncSend(); }
2220
ExitedSyncSend()2221 void MessageChannel::ExitedSyncSend() { mListener->OnExitedSyncSend(); }
2222
EnqueuePendingMessages()2223 void MessageChannel::EnqueuePendingMessages() {
2224 AssertWorkerThread();
2225 mMonitor->AssertCurrentThreadOwns();
2226
2227 MaybeUndeferIncall();
2228
2229 // XXX performance tuning knob: could process all or k pending
2230 // messages here, rather than enqueuing for later processing
2231
2232 RepostAllMessages();
2233 }
2234
WaitResponse(bool aWaitTimedOut)2235 bool MessageChannel::WaitResponse(bool aWaitTimedOut) {
2236 if (aWaitTimedOut) {
2237 if (mInTimeoutSecondHalf) {
2238 // We've really timed out this time.
2239 return false;
2240 }
2241 // Try a second time.
2242 mInTimeoutSecondHalf = true;
2243 } else {
2244 mInTimeoutSecondHalf = false;
2245 }
2246 return true;
2247 }
2248
2249 #ifndef OS_WIN
WaitForSyncNotify(bool)2250 bool MessageChannel::WaitForSyncNotify(bool /* aHandleWindowsMessages */) {
2251 # ifdef DEBUG
2252 // WARNING: We don't release the lock here. We can't because the link
2253 // could signal at this time and we would miss it. Instead we require
2254 // ArtificialTimeout() to be extremely simple.
2255 if (mListener->ArtificialTimeout()) {
2256 return false;
2257 }
2258 # endif
2259
2260 MOZ_RELEASE_ASSERT(!mIsSameThreadChannel,
2261 "Wait on same-thread channel will deadlock!");
2262
2263 TimeDuration timeout = (kNoTimeout == mTimeoutMs)
2264 ? TimeDuration::Forever()
2265 : TimeDuration::FromMilliseconds(mTimeoutMs);
2266 CVStatus status = mMonitor->Wait(timeout);
2267
2268 // If the timeout didn't expire, we know we received an event. The
2269 // converse is not true.
2270 return WaitResponse(status == CVStatus::Timeout);
2271 }
2272
WaitForInterruptNotify()2273 bool MessageChannel::WaitForInterruptNotify() {
2274 return WaitForSyncNotify(true);
2275 }
2276
NotifyWorkerThread()2277 void MessageChannel::NotifyWorkerThread() { mMonitor->Notify(); }
2278 #endif
2279
ShouldContinueFromTimeout()2280 bool MessageChannel::ShouldContinueFromTimeout() {
2281 AssertWorkerThread();
2282 mMonitor->AssertCurrentThreadOwns();
2283
2284 bool cont;
2285 {
2286 MonitorAutoUnlock unlock(*mMonitor);
2287 cont = mListener->ShouldContinueFromReplyTimeout();
2288 mListener->ArtificialSleep();
2289 }
2290
2291 static enum {
2292 UNKNOWN,
2293 NOT_DEBUGGING,
2294 DEBUGGING
2295 } sDebuggingChildren = UNKNOWN;
2296
2297 if (sDebuggingChildren == UNKNOWN) {
2298 sDebuggingChildren =
2299 getenv("MOZ_DEBUG_CHILD_PROCESS") || getenv("MOZ_DEBUG_CHILD_PAUSE")
2300 ? DEBUGGING
2301 : NOT_DEBUGGING;
2302 }
2303 if (sDebuggingChildren == DEBUGGING) {
2304 return true;
2305 }
2306
2307 return cont;
2308 }
2309
SetReplyTimeoutMs(int32_t aTimeoutMs)2310 void MessageChannel::SetReplyTimeoutMs(int32_t aTimeoutMs) {
2311 // Set channel timeout value. Since this is broken up into
2312 // two period, the minimum timeout value is 2ms.
2313 AssertWorkerThread();
2314 mTimeoutMs =
2315 (aTimeoutMs <= 0) ? kNoTimeout : (int32_t)ceil((double)aTimeoutMs / 2.0);
2316 }
2317
ReportMessageRouteError(const char * channelName) const2318 void MessageChannel::ReportMessageRouteError(const char* channelName) const {
2319 PrintErrorMessage(mSide, channelName, "Need a route");
2320 mListener->ProcessingError(MsgRouteError, "MsgRouteError");
2321 }
2322
ReportConnectionError(const char * aChannelName,Message * aMsg) const2323 void MessageChannel::ReportConnectionError(const char* aChannelName,
2324 Message* aMsg) const {
2325 AssertWorkerThread();
2326 mMonitor->AssertCurrentThreadOwns();
2327
2328 const char* errorMsg = nullptr;
2329 switch (mChannelState) {
2330 case ChannelClosed:
2331 errorMsg = "Closed channel: cannot send/recv";
2332 break;
2333 case ChannelOpening:
2334 errorMsg = "Opening channel: not yet ready for send/recv";
2335 break;
2336 case ChannelTimeout:
2337 errorMsg = "Channel timeout: cannot send/recv";
2338 break;
2339 case ChannelClosing:
2340 errorMsg =
2341 "Channel closing: too late to send/recv, messages will be lost";
2342 break;
2343 case ChannelError:
2344 errorMsg = "Channel error: cannot send/recv";
2345 break;
2346
2347 default:
2348 MOZ_CRASH("unreached");
2349 }
2350
2351 if (aMsg) {
2352 char reason[512];
2353 SprintfLiteral(reason, "(msgtype=0x%X,name=%s) %s", aMsg->type(),
2354 aMsg->name(), errorMsg);
2355
2356 PrintErrorMessage(mSide, aChannelName, reason);
2357 } else {
2358 PrintErrorMessage(mSide, aChannelName, errorMsg);
2359 }
2360
2361 MonitorAutoUnlock unlock(*mMonitor);
2362 mListener->ProcessingError(MsgDropped, errorMsg);
2363 }
2364
MaybeHandleError(Result code,const Message & aMsg,const char * channelName)2365 bool MessageChannel::MaybeHandleError(Result code, const Message& aMsg,
2366 const char* channelName) {
2367 if (MsgProcessed == code) return true;
2368
2369 const char* errorMsg = nullptr;
2370 switch (code) {
2371 case MsgNotKnown:
2372 errorMsg = "Unknown message: not processed";
2373 break;
2374 case MsgNotAllowed:
2375 errorMsg = "Message not allowed: cannot be sent/recvd in this state";
2376 break;
2377 case MsgPayloadError:
2378 errorMsg = "Payload error: message could not be deserialized";
2379 break;
2380 case MsgProcessingError:
2381 errorMsg =
2382 "Processing error: message was deserialized, but the handler "
2383 "returned false (indicating failure)";
2384 break;
2385 case MsgRouteError:
2386 errorMsg = "Route error: message sent to unknown actor ID";
2387 break;
2388 case MsgValueError:
2389 errorMsg =
2390 "Value error: message was deserialized, but contained an illegal "
2391 "value";
2392 break;
2393
2394 default:
2395 MOZ_CRASH("unknown Result code");
2396 return false;
2397 }
2398
2399 char reason[512];
2400 const char* msgname = aMsg.name();
2401 if (msgname[0] == '?') {
2402 SprintfLiteral(reason, "(msgtype=0x%X) %s", aMsg.type(), errorMsg);
2403 } else {
2404 SprintfLiteral(reason, "%s %s", msgname, errorMsg);
2405 }
2406
2407 PrintErrorMessage(mSide, channelName, reason);
2408
2409 // Error handled in mozilla::ipc::IPCResult.
2410 if (code == MsgProcessingError) {
2411 return false;
2412 }
2413
2414 mListener->ProcessingError(code, reason);
2415
2416 return false;
2417 }
2418
OnChannelErrorFromLink()2419 void MessageChannel::OnChannelErrorFromLink() {
2420 mMonitor->AssertCurrentThreadOwns();
2421
2422 IPC_LOG("OnChannelErrorFromLink");
2423
2424 if (InterruptStackDepth() > 0) NotifyWorkerThread();
2425
2426 if (AwaitingSyncReply() || AwaitingIncomingMessage()) NotifyWorkerThread();
2427
2428 if (ChannelClosing != mChannelState) {
2429 if (mAbortOnError) {
2430 // mAbortOnError is set by main actors (e.g., ContentChild) to ensure
2431 // that the process terminates even if normal shutdown is prevented.
2432 // A MOZ_CRASH() here is not helpful because crash reporting relies
2433 // on the parent process which we know is dead or otherwise unusable.
2434 //
2435 // Additionally, the parent process can (and often is) killed on Android
2436 // when apps are backgrounded. We don't need to report a crash for
2437 // normal behavior in that case.
2438 printf_stderr("Exiting due to channel error.\n");
2439 ProcessChild::QuickExit();
2440 }
2441 mChannelState = ChannelError;
2442 mMonitor->Notify();
2443 }
2444
2445 PostErrorNotifyTask();
2446 }
2447
NotifyMaybeChannelError()2448 void MessageChannel::NotifyMaybeChannelError() {
2449 mMonitor->AssertNotCurrentThreadOwns();
2450
2451 // TODO sort out Close() on this side racing with Close() on the other side
2452 if (ChannelClosing == mChannelState) {
2453 // the channel closed, but we received a "Goodbye" message warning us
2454 // about it. no worries
2455 mChannelState = ChannelClosed;
2456 NotifyChannelClosed();
2457 return;
2458 }
2459
2460 Clear();
2461
2462 // Oops, error! Let the listener know about it.
2463 mChannelState = ChannelError;
2464
2465 // IPDL assumes these notifications do not fire twice, so we do not let
2466 // that happen.
2467 if (mNotifiedChannelDone) {
2468 return;
2469 }
2470 mNotifiedChannelDone = true;
2471
2472 // After this, the channel may be deleted. Based on the premise that
2473 // mListener owns this channel, any calls back to this class that may
2474 // work with mListener should still work on living objects.
2475 mListener->OnChannelError();
2476 }
2477
OnNotifyMaybeChannelError()2478 void MessageChannel::OnNotifyMaybeChannelError() {
2479 AssertWorkerThread();
2480 mMonitor->AssertNotCurrentThreadOwns();
2481
2482 mChannelErrorTask = nullptr;
2483
2484 // OnChannelError holds mMonitor when it posts this task and this
2485 // task cannot be allowed to run until OnChannelError has
2486 // exited. We enforce that order by grabbing the mutex here which
2487 // should only continue once OnChannelError has completed.
2488 {
2489 MonitorAutoLock lock(*mMonitor);
2490 // nothing to do here
2491 }
2492
2493 if (IsOnCxxStack()) {
2494 mChannelErrorTask = NewNonOwningCancelableRunnableMethod(
2495 "ipc::MessageChannel::OnNotifyMaybeChannelError", this,
2496 &MessageChannel::OnNotifyMaybeChannelError);
2497 RefPtr<Runnable> task = mChannelErrorTask;
2498 // This used to post a 10ms delayed patch; however not all
2499 // nsISerialEventTarget implementations support delayed dispatch.
2500 // The delay being completely arbitrary, we may not as well have any.
2501 mWorkerThread->Dispatch(task.forget());
2502 return;
2503 }
2504
2505 NotifyMaybeChannelError();
2506 }
2507
PostErrorNotifyTask()2508 void MessageChannel::PostErrorNotifyTask() {
2509 mMonitor->AssertCurrentThreadOwns();
2510
2511 if (mChannelErrorTask) return;
2512
2513 // This must be the last code that runs on this thread!
2514 mChannelErrorTask = NewNonOwningCancelableRunnableMethod(
2515 "ipc::MessageChannel::OnNotifyMaybeChannelError", this,
2516 &MessageChannel::OnNotifyMaybeChannelError);
2517 RefPtr<Runnable> task = mChannelErrorTask;
2518 mWorkerThread->Dispatch(task.forget());
2519 }
2520
2521 // Special async message.
2522 class GoodbyeMessage : public IPC::Message {
2523 public:
GoodbyeMessage()2524 GoodbyeMessage() : IPC::Message(MSG_ROUTING_NONE, GOODBYE_MESSAGE_TYPE) {}
Read(const Message * msg)2525 static bool Read(const Message* msg) { return true; }
Log(const std::string & aPrefix,FILE * aOutf) const2526 void Log(const std::string& aPrefix, FILE* aOutf) const {
2527 fputs("(special `Goodbye' message)", aOutf);
2528 }
2529 };
2530
SynchronouslyClose()2531 void MessageChannel::SynchronouslyClose() {
2532 AssertWorkerThread();
2533 mMonitor->AssertCurrentThreadOwns();
2534 mLink->SendClose();
2535
2536 MOZ_RELEASE_ASSERT(!mIsSameThreadChannel || ChannelClosed == mChannelState,
2537 "same-thread channel failed to synchronously close?");
2538
2539 while (ChannelClosed != mChannelState) mMonitor->Wait();
2540 }
2541
CloseWithError()2542 void MessageChannel::CloseWithError() {
2543 AssertWorkerThread();
2544
2545 MonitorAutoLock lock(*mMonitor);
2546 if (ChannelConnected != mChannelState) {
2547 return;
2548 }
2549 SynchronouslyClose();
2550 mChannelState = ChannelError;
2551 PostErrorNotifyTask();
2552 }
2553
CloseWithTimeout()2554 void MessageChannel::CloseWithTimeout() {
2555 AssertWorkerThread();
2556
2557 MonitorAutoLock lock(*mMonitor);
2558 if (ChannelConnected != mChannelState) {
2559 return;
2560 }
2561 SynchronouslyClose();
2562 mChannelState = ChannelTimeout;
2563 }
2564
NotifyImpendingShutdown()2565 void MessageChannel::NotifyImpendingShutdown() {
2566 UniquePtr<Message> msg =
2567 MakeUnique<Message>(MSG_ROUTING_NONE, IMPENDING_SHUTDOWN_MESSAGE_TYPE);
2568 MonitorAutoLock lock(*mMonitor);
2569 if (Connected()) {
2570 mLink->SendMessage(std::move(msg));
2571 }
2572 }
2573
Close()2574 void MessageChannel::Close() {
2575 AssertWorkerThread();
2576
2577 {
2578 // We don't use MonitorAutoLock here as that causes some sort of
2579 // deadlock in the error/timeout-with-a-listener state below when
2580 // compiling an optimized msvc build.
2581 mMonitor->Lock();
2582
2583 // Instead just use a ScopeExit to manage the unlock.
2584 RefPtr<RefCountedMonitor> monitor(mMonitor);
2585 auto exit = MakeScopeExit([m = std::move(monitor)]() { m->Unlock(); });
2586
2587 if (ChannelError == mChannelState || ChannelTimeout == mChannelState) {
2588 // See bug 538586: if the listener gets deleted while the
2589 // IO thread's NotifyChannelError event is still enqueued
2590 // and subsequently deletes us, then the error event will
2591 // also be deleted and the listener will never be notified
2592 // of the channel error.
2593 if (mListener) {
2594 exit.release(); // Explicitly unlocking, clear scope exit.
2595 mMonitor->Unlock();
2596 NotifyMaybeChannelError();
2597 }
2598 return;
2599 }
2600
2601 if (ChannelOpening == mChannelState) {
2602 // SynchronouslyClose() waits for an ack from the other side, so
2603 // the opening sequence should complete before this returns.
2604 SynchronouslyClose();
2605 mChannelState = ChannelError;
2606 NotifyMaybeChannelError();
2607 return;
2608 }
2609
2610 if (ChannelClosed == mChannelState) {
2611 // Slightly unexpected but harmless; ignore. See bug 1554244.
2612 return;
2613 }
2614
2615 // Notify the other side that we're about to close our socket. If we've
2616 // already received a Goodbye from the other side (and our state is
2617 // ChannelClosing), there's no reason to send one.
2618 if (ChannelConnected == mChannelState) {
2619 mLink->SendMessage(MakeUnique<GoodbyeMessage>());
2620 }
2621 SynchronouslyClose();
2622 }
2623
2624 NotifyChannelClosed();
2625 }
2626
NotifyChannelClosed()2627 void MessageChannel::NotifyChannelClosed() {
2628 mMonitor->AssertNotCurrentThreadOwns();
2629
2630 if (ChannelClosed != mChannelState)
2631 MOZ_CRASH("channel should have been closed!");
2632
2633 Clear();
2634
2635 // IPDL assumes these notifications do not fire twice, so we do not let
2636 // that happen.
2637 if (mNotifiedChannelDone) {
2638 return;
2639 }
2640 mNotifiedChannelDone = true;
2641
2642 // OK, the IO thread just closed the channel normally. Let the
2643 // listener know about it. After this point the channel may be
2644 // deleted.
2645 mListener->OnChannelClose();
2646 }
2647
DebugAbort(const char * file,int line,const char * cond,const char * why,bool reply)2648 void MessageChannel::DebugAbort(const char* file, int line, const char* cond,
2649 const char* why, bool reply) {
2650 printf_stderr(
2651 "###!!! [MessageChannel][%s][%s:%d] "
2652 "Assertion (%s) failed. %s %s\n",
2653 mSide == ChildSide ? "Child" : "Parent", file, line, cond, why,
2654 reply ? "(reply)" : "");
2655 // technically we need the mutex for this, but we're dying anyway
2656 DumpInterruptStack(" ");
2657 printf_stderr(" remote Interrupt stack guess: %zu\n",
2658 mRemoteStackDepthGuess);
2659 printf_stderr(" deferred stack size: %zu\n", mDeferred.size());
2660 printf_stderr(" out-of-turn Interrupt replies stack size: %zu\n",
2661 mOutOfTurnReplies.size());
2662
2663 MessageQueue pending = std::move(mPending);
2664 while (!pending.isEmpty()) {
2665 printf_stderr(
2666 " [ %s%s ]\n",
2667 pending.getFirst()->Msg().is_interrupt()
2668 ? "intr"
2669 : (pending.getFirst()->Msg().is_sync() ? "sync" : "async"),
2670 pending.getFirst()->Msg().is_reply() ? "reply" : "");
2671 pending.popFirst();
2672 }
2673
2674 MOZ_CRASH_UNSAFE(why);
2675 }
2676
DumpInterruptStack(const char * const pfx) const2677 void MessageChannel::DumpInterruptStack(const char* const pfx) const {
2678 NS_WARNING_ASSERTION(!mWorkerThread->IsOnCurrentThread(),
2679 "The worker thread had better be paused in a debugger!");
2680
2681 printf_stderr("%sMessageChannel 'backtrace':\n", pfx);
2682
2683 // print a python-style backtrace, first frame to last
2684 for (uint32_t i = 0; i < mCxxStackFrames.length(); ++i) {
2685 int32_t id;
2686 const char* dir;
2687 const char* sems;
2688 const char* name;
2689 mCxxStackFrames[i].Describe(&id, &dir, &sems, &name);
2690
2691 printf_stderr("%s[(%u) %s %s %s(actor=%d) ]\n", pfx, i, dir, sems, name,
2692 id);
2693 }
2694 }
2695
AddProfilerMarker(const IPC::Message & aMessage,MessageDirection aDirection)2696 void MessageChannel::AddProfilerMarker(const IPC::Message& aMessage,
2697 MessageDirection aDirection) {
2698 mMonitor->AssertCurrentThreadOwns();
2699
2700 if (profiler_feature_active(ProfilerFeature::IPCMessages)) {
2701 int32_t pid = mListener->OtherPidMaybeInvalid();
2702 // Only record markers for IPCs with a valid pid.
2703 // And if one of the profiler mutexes is locked on this thread, don't record
2704 // markers, because we don't want to expose profiler IPCs due to the
2705 // profiler itself, and also to avoid possible re-entrancy issues.
2706 if (pid != kInvalidProcessId && !profiler_is_locked_on_current_thread()) {
2707 // The current timestamp must be given to the `IPCMarker` payload.
2708 [[maybe_unused]] const TimeStamp now = TimeStamp::NowUnfuzzed();
2709 PROFILER_MARKER("IPC", IPC, MarkerTiming::InstantAt(now), IPCMarker, now,
2710 now, pid, aMessage.seqno(), aMessage.type(), mSide,
2711 aDirection, MessagePhase::Endpoint, aMessage.is_sync());
2712 }
2713 }
2714 }
2715
GetTopmostMessageRoutingId() const2716 int32_t MessageChannel::GetTopmostMessageRoutingId() const {
2717 AssertWorkerThread();
2718
2719 if (mCxxStackFrames.empty()) {
2720 return MSG_ROUTING_NONE;
2721 }
2722 const InterruptFrame& frame = mCxxStackFrames.back();
2723 return frame.GetRoutingId();
2724 }
2725
EndTimeout()2726 void MessageChannel::EndTimeout() {
2727 mMonitor->AssertCurrentThreadOwns();
2728
2729 IPC_LOG("Ending timeout of seqno=%d", mTimedOutMessageSeqno);
2730 mTimedOutMessageSeqno = 0;
2731 mTimedOutMessageNestedLevel = 0;
2732
2733 RepostAllMessages();
2734 }
2735
RepostAllMessages()2736 void MessageChannel::RepostAllMessages() {
2737 bool needRepost = false;
2738 for (MessageTask* task : mPending) {
2739 if (!task->IsScheduled()) {
2740 needRepost = true;
2741 break;
2742 }
2743 }
2744 if (!needRepost) {
2745 // If everything is already scheduled to run, do nothing.
2746 return;
2747 }
2748
2749 // In some cases we may have deferred dispatch of some messages in the
2750 // queue. Now we want to run them again. However, we can't just re-post
2751 // those messages since the messages after them in mPending would then be
2752 // before them in the event queue. So instead we cancel everything and
2753 // re-post all messages in the correct order.
2754 MessageQueue queue = std::move(mPending);
2755 while (RefPtr<MessageTask> task = queue.popFirst()) {
2756 RefPtr<MessageTask> newTask = new MessageTask(this, std::move(task->Msg()));
2757 mPending.insertBack(newTask);
2758 newTask->Post();
2759 }
2760
2761 AssertMaybeDeferredCountCorrect();
2762 }
2763
CancelTransaction(int transaction)2764 void MessageChannel::CancelTransaction(int transaction) {
2765 mMonitor->AssertCurrentThreadOwns();
2766
2767 // When we cancel a transaction, we need to behave as if there's no longer
2768 // any IPC on the stack. Anything we were dispatching or sending will get
2769 // canceled. Consequently, we have to update the state variables below.
2770 //
2771 // We also need to ensure that when any IPC functions on the stack return,
2772 // they don't reset these values using an RAII class like AutoSetValue. To
2773 // avoid that, these RAII classes check if the variable they set has been
2774 // tampered with (by us). If so, they don't reset the variable to the old
2775 // value.
2776
2777 IPC_LOG("CancelTransaction: xid=%d", transaction);
2778
2779 // An unusual case: We timed out a transaction which the other side then
2780 // cancelled. In this case we just leave the timedout state and try to
2781 // forget this ever happened.
2782 if (transaction == mTimedOutMessageSeqno) {
2783 IPC_LOG("Cancelled timed out message %d", mTimedOutMessageSeqno);
2784 EndTimeout();
2785
2786 // Normally mCurrentTransaction == 0 here. But it can be non-zero if:
2787 // 1. Parent sends NESTED_INSIDE_SYNC message H.
2788 // 2. Parent times out H.
2789 // 3. Child dispatches H and sends nested message H' (same transaction).
2790 // 4. Parent dispatches H' and cancels.
2791 MOZ_RELEASE_ASSERT(!mTransactionStack ||
2792 mTransactionStack->TransactionID() == transaction);
2793 if (mTransactionStack) {
2794 mTransactionStack->Cancel();
2795 }
2796 } else {
2797 MOZ_RELEASE_ASSERT(mTransactionStack->TransactionID() == transaction);
2798 mTransactionStack->Cancel();
2799 }
2800
2801 bool foundSync = false;
2802 for (MessageTask* p = mPending.getFirst(); p;) {
2803 Message& msg = p->Msg();
2804
2805 // If there was a race between the parent and the child, then we may
2806 // have a queued sync message. We want to drop this message from the
2807 // queue since if will get cancelled along with the transaction being
2808 // cancelled. This happens if the message in the queue is
2809 // NESTED_INSIDE_SYNC.
2810 if (msg.is_sync() && msg.nested_level() != IPC::Message::NOT_NESTED) {
2811 MOZ_RELEASE_ASSERT(!foundSync);
2812 MOZ_RELEASE_ASSERT(msg.transaction_id() != transaction);
2813 IPC_LOG("Removing msg from queue seqno=%d xid=%d", msg.seqno(),
2814 msg.transaction_id());
2815 foundSync = true;
2816 if (!IsAlwaysDeferred(msg)) {
2817 mMaybeDeferredPendingCount--;
2818 }
2819 p = p->removeAndGetNext();
2820 continue;
2821 }
2822
2823 p = p->getNext();
2824 }
2825
2826 AssertMaybeDeferredCountCorrect();
2827 }
2828
CancelCurrentTransaction()2829 void MessageChannel::CancelCurrentTransaction() {
2830 MonitorAutoLock lock(*mMonitor);
2831 if (DispatchingSyncMessageNestedLevel() >= IPC::Message::NESTED_INSIDE_SYNC) {
2832 if (DispatchingSyncMessageNestedLevel() ==
2833 IPC::Message::NESTED_INSIDE_CPOW ||
2834 DispatchingAsyncMessageNestedLevel() ==
2835 IPC::Message::NESTED_INSIDE_CPOW) {
2836 mListener->IntentionalCrash();
2837 }
2838
2839 IPC_LOG("Cancel requested: current xid=%d",
2840 CurrentNestedInsideSyncTransaction());
2841 MOZ_RELEASE_ASSERT(DispatchingSyncMessage());
2842 auto cancel =
2843 MakeUnique<CancelMessage>(CurrentNestedInsideSyncTransaction());
2844 CancelTransaction(CurrentNestedInsideSyncTransaction());
2845 mLink->SendMessage(std::move(cancel));
2846 }
2847 }
2848
CancelCPOWs()2849 void CancelCPOWs() {
2850 if (gParentProcessBlocker) {
2851 mozilla::Telemetry::Accumulate(mozilla::Telemetry::IPC_TRANSACTION_CANCEL,
2852 true);
2853 gParentProcessBlocker->CancelCurrentTransaction();
2854 }
2855 }
2856
IsCrossProcess() const2857 bool MessageChannel::IsCrossProcess() const {
2858 mMonitor->AssertCurrentThreadOwns();
2859 return mIsCrossProcess;
2860 }
2861
SetIsCrossProcess(bool aIsCrossProcess)2862 void MessageChannel::SetIsCrossProcess(bool aIsCrossProcess) {
2863 mMonitor->AssertCurrentThreadOwns();
2864 if (aIsCrossProcess == mIsCrossProcess) {
2865 return;
2866 }
2867 mIsCrossProcess = aIsCrossProcess;
2868 if (mIsCrossProcess) {
2869 ChannelCountReporter::Increment(mName);
2870 } else {
2871 ChannelCountReporter::Decrement(mName);
2872 }
2873 }
2874
2875 } // namespace ipc
2876 } // namespace mozilla
2877