Source code

Revision control

Other Tools

1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
3
/* This Source Code Form is subject to the terms of the Mozilla Public
4
* License, v. 2.0. If a copy of the MPL was not distributed with this
5
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
#include "base/process_util.h"
8
#include "base/task.h"
9
10
#ifdef OS_POSIX
11
# include <errno.h>
12
#endif
13
14
#include "mozilla/IntegerPrintfMacros.h"
15
16
#include "mozilla/ipc/ProtocolUtils.h"
17
18
#include "mozilla/ipc/MessageChannel.h"
19
#include "mozilla/ipc/Transport.h"
20
#include "mozilla/recordreplay/ChildIPC.h"
21
#include "mozilla/recordreplay/ParentIPC.h"
22
#include "mozilla/StaticMutex.h"
23
#include "mozilla/SystemGroup.h"
24
#include "mozilla/Unused.h"
25
#include "nsPrintfCString.h"
26
27
#if defined(MOZ_SANDBOX) && defined(XP_WIN)
28
# include "mozilla/sandboxTarget.h"
29
#endif
30
31
#if defined(XP_WIN)
32
# include "aclapi.h"
33
# include "sddl.h"
34
35
# include "mozilla/TypeTraits.h"
36
#endif
37
38
#include "nsAutoPtr.h"
39
40
using namespace IPC;
41
42
using base::GetCurrentProcId;
43
using base::ProcessHandle;
44
using base::ProcessId;
45
46
namespace mozilla {
47
48
#if defined(XP_WIN)
49
// Generate RAII classes for LPTSTR and PSECURITY_DESCRIPTOR.
50
MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedLPTStr,
51
RemovePointer<LPTSTR>::Type,
52
::LocalFree)
53
MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(
54
ScopedPSecurityDescriptor, RemovePointer<PSECURITY_DESCRIPTOR>::Type,
55
::LocalFree)
56
#endif
57
58
namespace ipc {
59
60
IPCResult IPCResult::Fail(NotNull<IProtocol*> actor, const char* where,
61
const char* why) {
62
// Calls top-level protocol to handle the error.
63
nsPrintfCString errorMsg("%s %s\n", where, why);
64
actor->GetIPCChannel()->Listener()->ProcessingError(
65
HasResultCodes::MsgProcessingError, errorMsg.get());
66
return IPCResult(false);
67
}
68
69
#if defined(XP_WIN)
70
bool DuplicateHandle(HANDLE aSourceHandle, DWORD aTargetProcessId,
71
HANDLE* aTargetHandle, DWORD aDesiredAccess,
72
DWORD aOptions) {
73
// If our process is the target just duplicate the handle.
74
if (aTargetProcessId == base::GetCurrentProcId()) {
75
return !!::DuplicateHandle(::GetCurrentProcess(), aSourceHandle,
76
::GetCurrentProcess(), aTargetHandle,
77
aDesiredAccess, false, aOptions);
78
}
79
80
# if defined(MOZ_SANDBOX)
81
// Try the broker next (will fail if not sandboxed).
82
if (SandboxTarget::Instance()->BrokerDuplicateHandle(
83
aSourceHandle, aTargetProcessId, aTargetHandle, aDesiredAccess,
84
aOptions)) {
85
return true;
86
}
87
# endif
88
89
// Finally, see if we already have access to the process.
90
ScopedProcessHandle targetProcess(
91
OpenProcess(PROCESS_DUP_HANDLE, FALSE, aTargetProcessId));
92
if (!targetProcess) {
93
CrashReporter::AnnotateCrashReport(
94
CrashReporter::Annotation::IPCTransportFailureReason,
95
NS_LITERAL_CSTRING("Failed to open target process."));
96
return false;
97
}
98
99
return !!::DuplicateHandle(::GetCurrentProcess(), aSourceHandle,
100
targetProcess, aTargetHandle, aDesiredAccess,
101
FALSE, aOptions);
102
}
103
#endif
104
105
void AnnotateSystemError() {
106
int64_t error = 0;
107
#if defined(XP_WIN)
108
error = ::GetLastError();
109
#elif defined(OS_POSIX)
110
error = errno;
111
#endif
112
if (error) {
113
CrashReporter::AnnotateCrashReport(
114
CrashReporter::Annotation::IPCSystemError,
115
nsPrintfCString("%" PRId64, error));
116
}
117
}
118
119
#if defined(XP_MACOSX)
120
void AnnotateCrashReportWithErrno(CrashReporter::Annotation tag, int error) {
121
CrashReporter::AnnotateCrashReport(tag, error);
122
}
123
#endif // defined(XP_MACOSX)
124
125
void LogMessageForProtocol(const char* aTopLevelProtocol,
126
base::ProcessId aOtherPid,
127
const char* aContextDescription, uint32_t aMessageId,
128
MessageDirection aDirection) {
129
nsPrintfCString logMessage(
130
"[time: %" PRId64 "][%d%s%d] [%s] %s %s\n", PR_Now(),
131
base::GetCurrentProcId(),
132
aDirection == MessageDirection::eReceiving ? "<-" : "->", aOtherPid,
133
aTopLevelProtocol, aContextDescription,
134
StringFromIPCMessageType(aMessageId));
135
#ifdef ANDROID
136
__android_log_write(ANDROID_LOG_INFO, "GeckoIPC", logMessage.get());
137
#endif
138
fputs(logMessage.get(), stderr);
139
}
140
141
void ProtocolErrorBreakpoint(const char* aMsg) {
142
// Bugs that generate these error messages can be tough to
143
// reproduce. Log always in the hope that someone finds the error
144
// message.
145
printf_stderr("IPDL protocol error: %s\n", aMsg);
146
}
147
148
void FatalError(const char* aMsg, bool aIsParent) {
149
#ifndef FUZZING
150
ProtocolErrorBreakpoint(aMsg);
151
#endif
152
153
nsAutoCString formattedMessage("IPDL error: \"");
154
formattedMessage.AppendASCII(aMsg);
155
if (aIsParent) {
156
// We're going to crash the parent process because at this time
157
// there's no other really nice way of getting a minidump out of
158
// this process if we're off the main thread.
159
formattedMessage.AppendLiteral("\". Intentionally crashing.");
160
NS_ERROR(formattedMessage.get());
161
CrashReporter::AnnotateCrashReport(
162
CrashReporter::Annotation::IPCFatalErrorMsg, nsDependentCString(aMsg));
163
AnnotateSystemError();
164
#ifndef FUZZING
165
MOZ_CRASH("IPC FatalError in the parent process!");
166
#endif
167
} else {
168
formattedMessage.AppendLiteral("\". abort()ing as a result.");
169
#ifndef FUZZING
170
MOZ_CRASH_UNSAFE(formattedMessage.get());
171
#endif
172
}
173
}
174
175
void LogicError(const char* aMsg) { MOZ_CRASH_UNSAFE(aMsg); }
176
177
void ActorIdReadError(const char* aActorDescription) {
178
#ifndef FUZZING
179
MOZ_CRASH_UNSAFE_PRINTF("Error deserializing id for %s", aActorDescription);
180
#endif
181
}
182
183
void BadActorIdError(const char* aActorDescription) {
184
nsPrintfCString message("bad id for %s", aActorDescription);
185
ProtocolErrorBreakpoint(message.get());
186
}
187
188
void ActorLookupError(const char* aActorDescription) {
189
nsPrintfCString message("could not lookup id for %s", aActorDescription);
190
ProtocolErrorBreakpoint(message.get());
191
}
192
193
void MismatchedActorTypeError(const char* aActorDescription) {
194
nsPrintfCString message("actor that should be of type %s has different type",
195
aActorDescription);
196
ProtocolErrorBreakpoint(message.get());
197
}
198
199
void UnionTypeReadError(const char* aUnionName) {
200
MOZ_CRASH_UNSAFE_PRINTF("error deserializing type of union %s", aUnionName);
201
}
202
203
void ArrayLengthReadError(const char* aElementName) {
204
MOZ_CRASH_UNSAFE_PRINTF("error deserializing length of %s[]", aElementName);
205
}
206
207
void SentinelReadError(const char* aClassName) {
208
MOZ_CRASH_UNSAFE_PRINTF("incorrect sentinel when reading %s", aClassName);
209
}
210
211
void TableToArray(const nsTHashtable<nsPtrHashKey<void>>& aTable,
212
nsTArray<void*>& aArray) {
213
uint32_t i = 0;
214
void** elements = aArray.AppendElements(aTable.Count());
215
for (auto iter = aTable.ConstIter(); !iter.Done(); iter.Next()) {
216
elements[i] = iter.Get()->GetKey();
217
++i;
218
}
219
}
220
221
ActorLifecycleProxy::ActorLifecycleProxy(IProtocol* aActor) : mActor(aActor) {
222
MOZ_ASSERT(mActor);
223
MOZ_ASSERT(mActor->CanSend(),
224
"Cannot create LifecycleProxy for non-connected actor!");
225
226
// Take a reference to our manager's lifecycle proxy to try to hold it &
227
// ensure it doesn't die before us.
228
if (mActor->mManager) {
229
mManager = mActor->mManager->mLifecycleProxy;
230
}
231
232
// Record that we've taken our first reference to our actor.
233
mActor->ActorAlloc();
234
}
235
236
ActorLifecycleProxy::~ActorLifecycleProxy() {
237
// When the LifecycleProxy's lifetime has come to an end, it means that the
238
// actor should have its `Dealloc` method called on it. In a well-behaved
239
// actor, this will release the IPC-held reference to the actor.
240
//
241
// If the actor has already died before the `LifecycleProxy`, the `IProtocol`
242
// destructor below will clear our reference to it, preventing us from
243
// performing a use-after-free here.
244
if (!mActor) {
245
return;
246
}
247
248
// Clear our actor's state back to inactive, and then invoke ActorDealloc.
249
MOZ_ASSERT(mActor->mLinkStatus == LinkStatus::Destroyed,
250
"Deallocating non-destroyed actor!");
251
mActor->mLifecycleProxy = nullptr;
252
mActor->mLinkStatus = LinkStatus::Inactive;
253
mActor->ActorDealloc();
254
mActor = nullptr;
255
}
256
257
IProtocol::~IProtocol() {
258
// If the actor still has a lifecycle proxy when it is being torn down, it
259
// means that IPC was not given control over the lifecycle of the actor
260
// correctly. Usually this means that the actor was destroyed while IPC is
261
// calling a message handler for it, and the actor incorrectly frees itself
262
// during that operation.
263
//
264
// As this happens unfortunately frequently, due to many odd protocols in
265
// Gecko, simply emit a warning and clear the weak backreference from our
266
// LifecycleProxy back to us.
267
if (mLifecycleProxy) {
268
// FIXME: It would be nice to have this print out the name of the
269
// misbehaving actor, to help people notice it's their fault!
270
NS_WARNING(
271
"Actor destructor called before IPC lifecycle complete!\n"
272
"References to this actor may unexpectedly dangle!");
273
274
mLifecycleProxy->mActor = nullptr;
275
276
// If we are somehow being destroyed while active, make sure that the
277
// existing IPC reference has been freed. If the status of the actor is
278
// `Destroyed`, the reference has already been freed, and we shouldn't free
279
// it a second time.
280
MOZ_ASSERT(mLinkStatus != LinkStatus::Inactive);
281
if (mLinkStatus != LinkStatus::Destroyed) {
282
NS_IF_RELEASE(mLifecycleProxy);
283
}
284
mLifecycleProxy = nullptr;
285
}
286
}
287
288
// The following methods either directly forward to the toplevel protocol, or
289
// almost directly do.
290
int32_t IProtocol::Register(IProtocol* aRouted) {
291
return mToplevel->Register(aRouted);
292
}
293
int32_t IProtocol::RegisterID(IProtocol* aRouted, int32_t aId) {
294
return mToplevel->RegisterID(aRouted, aId);
295
}
296
IProtocol* IProtocol::Lookup(int32_t aId) { return mToplevel->Lookup(aId); }
297
void IProtocol::Unregister(int32_t aId) {
298
if (aId == mId) {
299
mId = kFreedActorId;
300
}
301
return mToplevel->Unregister(aId);
302
}
303
304
Shmem::SharedMemory* IProtocol::CreateSharedMemory(
305
size_t aSize, SharedMemory::SharedMemoryType aType, bool aUnsafe,
306
int32_t* aId) {
307
return mToplevel->CreateSharedMemory(aSize, aType, aUnsafe, aId);
308
}
309
Shmem::SharedMemory* IProtocol::LookupSharedMemory(int32_t aId) {
310
return mToplevel->LookupSharedMemory(aId);
311
}
312
bool IProtocol::IsTrackingSharedMemory(Shmem::SharedMemory* aSegment) {
313
return mToplevel->IsTrackingSharedMemory(aSegment);
314
}
315
bool IProtocol::DestroySharedMemory(Shmem& aShmem) {
316
return mToplevel->DestroySharedMemory(aShmem);
317
}
318
319
MessageChannel* IProtocol::GetIPCChannel() {
320
return mToplevel->GetIPCChannel();
321
}
322
const MessageChannel* IProtocol::GetIPCChannel() const {
323
return mToplevel->GetIPCChannel();
324
}
325
326
void IProtocol::SetEventTargetForActor(IProtocol* aActor,
327
nsIEventTarget* aEventTarget) {
328
// Make sure we have a manager for the internal method to access.
329
aActor->SetManager(this);
330
mToplevel->SetEventTargetForActorInternal(aActor, aEventTarget);
331
}
332
333
void IProtocol::ReplaceEventTargetForActor(IProtocol* aActor,
334
nsIEventTarget* aEventTarget) {
335
MOZ_ASSERT(aActor->Manager());
336
mToplevel->ReplaceEventTargetForActor(aActor, aEventTarget);
337
}
338
339
void IProtocol::SetEventTargetForRoute(int32_t aRoute,
340
nsIEventTarget* aEventTarget) {
341
mToplevel->SetEventTargetForRoute(aRoute, aEventTarget);
342
}
343
344
nsIEventTarget* IProtocol::GetActorEventTarget() {
345
// FIXME: It's a touch sketchy that we don't return a strong reference here.
346
RefPtr<nsIEventTarget> target = GetActorEventTarget(this);
347
return target;
348
}
349
already_AddRefed<nsIEventTarget> IProtocol::GetActorEventTarget(
350
IProtocol* aActor) {
351
return mToplevel->GetActorEventTarget(aActor);
352
}
353
354
ProcessId IProtocol::OtherPid() const { return mToplevel->OtherPid(); }
355
356
void IProtocol::SetId(int32_t aId) {
357
MOZ_ASSERT(mId == aId || mLinkStatus == LinkStatus::Inactive);
358
mId = aId;
359
}
360
361
Maybe<IProtocol*> IProtocol::ReadActor(const IPC::Message* aMessage,
362
PickleIterator* aIter, bool aNullable,
363
const char* aActorDescription,
364
int32_t aProtocolTypeId) {
365
int32_t id;
366
if (!IPC::ReadParam(aMessage, aIter, &id)) {
367
ActorIdReadError(aActorDescription);
368
return Nothing();
369
}
370
371
if (id == 1 || (id == 0 && !aNullable)) {
372
BadActorIdError(aActorDescription);
373
return Nothing();
374
}
375
376
if (id == 0) {
377
return Some(static_cast<IProtocol*>(nullptr));
378
}
379
380
IProtocol* listener = this->Lookup(id);
381
if (!listener) {
382
ActorLookupError(aActorDescription);
383
return Nothing();
384
}
385
386
if (listener->GetProtocolId() != aProtocolTypeId) {
387
MismatchedActorTypeError(aActorDescription);
388
return Nothing();
389
}
390
391
return Some(listener);
392
}
393
394
void IProtocol::FatalError(const char* const aErrorMsg) const {
395
HandleFatalError(aErrorMsg);
396
}
397
398
void IProtocol::HandleFatalError(const char* aErrorMsg) const {
399
if (IProtocol* manager = Manager()) {
400
manager->HandleFatalError(aErrorMsg);
401
return;
402
}
403
404
mozilla::ipc::FatalError(aErrorMsg, mSide == ParentSide);
405
}
406
407
bool IProtocol::AllocShmem(size_t aSize,
408
Shmem::SharedMemory::SharedMemoryType aType,
409
Shmem* aOutMem) {
410
Shmem::id_t id;
411
Shmem::SharedMemory* rawmem(CreateSharedMemory(aSize, aType, false, &id));
412
if (!rawmem) {
413
return false;
414
}
415
416
*aOutMem = Shmem(Shmem::PrivateIPDLCaller(), rawmem, id);
417
return true;
418
}
419
420
bool IProtocol::AllocUnsafeShmem(size_t aSize,
421
Shmem::SharedMemory::SharedMemoryType aType,
422
Shmem* aOutMem) {
423
Shmem::id_t id;
424
Shmem::SharedMemory* rawmem(CreateSharedMemory(aSize, aType, true, &id));
425
if (!rawmem) {
426
return false;
427
}
428
429
*aOutMem = Shmem(Shmem::PrivateIPDLCaller(), rawmem, id);
430
return true;
431
}
432
433
bool IProtocol::DeallocShmem(Shmem& aMem) {
434
bool ok = DestroySharedMemory(aMem);
435
#ifdef DEBUG
436
if (!ok) {
437
if (mSide == ChildSide) {
438
FatalError("bad Shmem");
439
} else {
440
NS_WARNING("bad Shmem");
441
}
442
return false;
443
}
444
#endif // DEBUG
445
aMem.forget(Shmem::PrivateIPDLCaller());
446
return ok;
447
}
448
449
void IProtocol::SetManager(IProtocol* aManager) {
450
MOZ_RELEASE_ASSERT(!mManager || mManager == aManager);
451
mManager = aManager;
452
mToplevel = aManager->mToplevel;
453
}
454
455
void IProtocol::SetManagerAndRegister(IProtocol* aManager) {
456
// Set the manager prior to registering so registering properly inherits
457
// the manager's event target.
458
SetManager(aManager);
459
460
aManager->Register(this);
461
}
462
463
void IProtocol::SetManagerAndRegister(IProtocol* aManager, int32_t aId) {
464
// Set the manager prior to registering so registering properly inherits
465
// the manager's event target.
466
SetManager(aManager);
467
468
aManager->RegisterID(this, aId);
469
}
470
471
bool IProtocol::ChannelSend(IPC::Message* aMsg) {
472
UniquePtr<IPC::Message> msg(aMsg);
473
if (CanSend()) {
474
// NOTE: This send call failing can only occur during toplevel channel
475
// teardown. As this is an async call, this isn't reasonable to predict or
476
// respond to, so just drop the message on the floor silently.
477
GetIPCChannel()->Send(msg.release());
478
return true;
479
}
480
481
NS_WARNING("IPC message discarded: actor cannot send");
482
return false;
483
}
484
485
bool IProtocol::ChannelSend(IPC::Message* aMsg, IPC::Message* aReply) {
486
UniquePtr<IPC::Message> msg(aMsg);
487
if (CanSend()) {
488
return GetIPCChannel()->Send(msg.release(), aReply);
489
}
490
491
NS_WARNING("IPC message discarded: actor cannot send");
492
return false;
493
}
494
495
bool IProtocol::ChannelCall(IPC::Message* aMsg, IPC::Message* aReply) {
496
UniquePtr<IPC::Message> msg(aMsg);
497
if (CanSend()) {
498
return GetIPCChannel()->Call(msg.release(), aReply);
499
}
500
501
NS_WARNING("IPC message discarded: actor cannot send");
502
return false;
503
}
504
505
void IProtocol::ActorConnected() {
506
if (mLinkStatus != LinkStatus::Inactive) {
507
return;
508
}
509
510
mLinkStatus = LinkStatus::Connected;
511
512
MOZ_ASSERT(!mLifecycleProxy, "double-connecting live actor");
513
mLifecycleProxy = new ActorLifecycleProxy(this);
514
NS_ADDREF(mLifecycleProxy); // Reference freed in DestroySubtree();
515
}
516
517
void IProtocol::DoomSubtree() {
518
MOZ_ASSERT(CanSend(), "dooming non-connected actor");
519
MOZ_ASSERT(mLifecycleProxy, "dooming zombie actor");
520
521
nsTArray<RefPtr<ActorLifecycleProxy>> managed;
522
AllManagedActors(managed);
523
for (ActorLifecycleProxy* proxy : managed) {
524
// Guard against actor being disconnected or destroyed during previous Doom
525
IProtocol* actor = proxy->Get();
526
if (actor && actor->CanSend()) {
527
actor->DoomSubtree();
528
}
529
}
530
531
// ActorDoom is called immediately before changing state, this allows messages
532
// to be sent during ActorDoom immediately before the channel is closed and
533
// sending messages is disabled.
534
ActorDoom();
535
mLinkStatus = LinkStatus::Doomed;
536
}
537
538
void IProtocol::DestroySubtree(ActorDestroyReason aWhy) {
539
MOZ_ASSERT(CanRecv(), "destroying non-connected actor");
540
MOZ_ASSERT(mLifecycleProxy, "destroying zombie actor");
541
542
// If we're a managed actor, unregister from our manager
543
if (Manager()) {
544
Unregister(Id());
545
}
546
547
// Destroy subtree
548
ActorDestroyReason subtreeWhy = aWhy;
549
if (aWhy == Deletion || aWhy == FailedConstructor) {
550
subtreeWhy = AncestorDeletion;
551
}
552
553
nsTArray<RefPtr<ActorLifecycleProxy>> managed;
554
AllManagedActors(managed);
555
for (ActorLifecycleProxy* proxy : managed) {
556
// Guard against actor being disconnected or destroyed during previous
557
// Destroy
558
IProtocol* actor = proxy->Get();
559
if (actor && actor->CanRecv()) {
560
actor->DestroySubtree(subtreeWhy);
561
}
562
}
563
564
// Ensure that we don't send any messages while we're calling `ActorDestroy`
565
// by setting our state to `Doomed`.
566
mLinkStatus = LinkStatus::Doomed;
567
568
// The actor is being destroyed, reject any pending responses, invoke
569
// `ActorDestroy` to destroy it, and then clear our status to
570
// `LinkStatus::Destroyed`.
571
GetIPCChannel()->RejectPendingResponsesForActor(this);
572
ActorDestroy(aWhy);
573
mLinkStatus = LinkStatus::Destroyed;
574
}
575
576
IToplevelProtocol::IToplevelProtocol(const char* aName, ProtocolId aProtoId,
577
Side aSide)
578
: IProtocol(aProtoId, aSide),
579
mOtherPid(mozilla::ipc::kInvalidProcessId),
580
mLastLocalId(0),
581
mEventTargetMutex("ProtocolEventTargetMutex"),
582
mMiddlemanChannelOverride(nullptr),
583
mChannel(aName, this) {
584
mToplevel = this;
585
}
586
587
IToplevelProtocol::~IToplevelProtocol() {
588
if (mTrans) {
589
RefPtr<DeleteTask<Transport>> task =
590
new DeleteTask<Transport>(mTrans.release());
591
XRE_GetIOMessageLoop()->PostTask(task.forget());
592
}
593
}
594
595
base::ProcessId IToplevelProtocol::OtherPid() const {
596
base::ProcessId pid = OtherPidMaybeInvalid();
597
MOZ_RELEASE_ASSERT(pid != kInvalidProcessId);
598
return pid;
599
}
600
601
void IToplevelProtocol::SetOtherProcessId(base::ProcessId aOtherPid) {
602
// When recording an execution, all communication we do is forwarded from
603
// the middleman to the parent process, so use its pid instead of the
604
// middleman's pid.
605
if (recordreplay::IsRecordingOrReplaying() &&
606
aOtherPid == recordreplay::child::MiddlemanProcessId()) {
607
mOtherPid = recordreplay::child::ParentProcessId();
608
} else {
609
mOtherPid = aOtherPid;
610
}
611
}
612
613
bool IToplevelProtocol::Open(mozilla::ipc::Transport* aTransport,
614
base::ProcessId aOtherPid, MessageLoop* aThread,
615
mozilla::ipc::Side aSide) {
616
SetOtherProcessId(aOtherPid);
617
return GetIPCChannel()->Open(aTransport, aThread, aSide);
618
}
619
620
bool IToplevelProtocol::Open(MessageChannel* aChannel,
621
MessageLoop* aMessageLoop,
622
mozilla::ipc::Side aSide) {
623
SetOtherProcessId(base::GetCurrentProcId());
624
return GetIPCChannel()->Open(aChannel, aMessageLoop->SerialEventTarget(),
625
aSide);
626
}
627
628
bool IToplevelProtocol::Open(MessageChannel* aChannel,
629
nsIEventTarget* aEventTarget,
630
mozilla::ipc::Side aSide) {
631
SetOtherProcessId(base::GetCurrentProcId());
632
return GetIPCChannel()->Open(aChannel, aEventTarget, aSide);
633
}
634
635
bool IToplevelProtocol::OpenWithAsyncPid(mozilla::ipc::Transport* aTransport,
636
MessageLoop* aThread,
637
mozilla::ipc::Side aSide) {
638
return GetIPCChannel()->Open(aTransport, aThread, aSide);
639
}
640
641
bool IToplevelProtocol::OpenOnSameThread(MessageChannel* aChannel, Side aSide) {
642
SetOtherProcessId(base::GetCurrentProcId());
643
return GetIPCChannel()->OpenOnSameThread(aChannel, aSide);
644
}
645
646
void IToplevelProtocol::Close() { GetIPCChannel()->Close(); }
647
648
void IToplevelProtocol::SetReplyTimeoutMs(int32_t aTimeoutMs) {
649
GetIPCChannel()->SetReplyTimeoutMs(aTimeoutMs);
650
}
651
652
bool IToplevelProtocol::IsOnCxxStack() const {
653
return GetIPCChannel()->IsOnCxxStack();
654
}
655
656
int32_t IToplevelProtocol::NextId() {
657
// Genreate the next ID to use for a shared memory or protocol. Parent and
658
// Child sides of the protocol use different pools, and actors created in the
659
// middleman need to use a distinct pool as well.
660
int32_t tag = 0;
661
if (recordreplay::IsMiddleman()) {
662
tag |= 1 << 0;
663
}
664
if (GetSide() == ParentSide) {
665
tag |= 1 << 1;
666
}
667
668
// Check any overflow
669
MOZ_RELEASE_ASSERT(mLastLocalId < (1 << 29));
670
671
// Compute the ID to use with the low two bits as our tag, and the remaining
672
// bits as a monotonic.
673
return (++mLastLocalId << 2) | tag;
674
}
675
676
int32_t IToplevelProtocol::Register(IProtocol* aRouted) {
677
if (aRouted->Id() != kNullActorId && aRouted->Id() != kFreedActorId) {
678
// If there's already an ID, just return that.
679
return aRouted->Id();
680
}
681
int32_t id = RegisterID(aRouted, NextId());
682
683
// Inherit our event target from our manager.
684
if (IProtocol* manager = aRouted->Manager()) {
685
MutexAutoLock lock(mEventTargetMutex);
686
if (nsCOMPtr<nsIEventTarget> target =
687
mEventTargetMap.Lookup(manager->Id())) {
688
mEventTargetMap.AddWithID(target, id);
689
}
690
}
691
692
return id;
693
}
694
695
int32_t IToplevelProtocol::RegisterID(IProtocol* aRouted, int32_t aId) {
696
aRouted->SetId(aId);
697
aRouted->ActorConnected();
698
mActorMap.AddWithID(aRouted, aId);
699
return aId;
700
}
701
702
IProtocol* IToplevelProtocol::Lookup(int32_t aId) {
703
return mActorMap.Lookup(aId);
704
}
705
706
void IToplevelProtocol::Unregister(int32_t aId) {
707
mActorMap.Remove(aId);
708
709
MutexAutoLock lock(mEventTargetMutex);
710
mEventTargetMap.RemoveIfPresent(aId);
711
}
712
713
Shmem::SharedMemory* IToplevelProtocol::CreateSharedMemory(
714
size_t aSize, Shmem::SharedMemory::SharedMemoryType aType, bool aUnsafe,
715
Shmem::id_t* aId) {
716
RefPtr<Shmem::SharedMemory> segment(
717
Shmem::Alloc(Shmem::PrivateIPDLCaller(), aSize, aType, aUnsafe));
718
if (!segment) {
719
return nullptr;
720
}
721
int32_t id = NextId();
722
Shmem shmem(Shmem::PrivateIPDLCaller(), segment.get(), id);
723
724
base::ProcessId pid =
725
#ifdef ANDROID
726
// We use OtherPidMaybeInvalid() because on Android this method is
727
// actually called on an unconnected protocol, but Android's shared memory
728
// implementation doesn't actually use the PID.
729
OtherPidMaybeInvalid();
730
#else
731
OtherPid();
732
#endif
733
734
Message* descriptor =
735
shmem.ShareTo(Shmem::PrivateIPDLCaller(), pid, MSG_ROUTING_CONTROL);
736
if (!descriptor) {
737
return nullptr;
738
}
739
Unused << GetIPCChannel()->Send(descriptor);
740
741
*aId = shmem.Id(Shmem::PrivateIPDLCaller());
742
Shmem::SharedMemory* rawSegment = segment.get();
743
mShmemMap.AddWithID(segment.forget().take(), *aId);
744
return rawSegment;
745
}
746
747
Shmem::SharedMemory* IToplevelProtocol::LookupSharedMemory(Shmem::id_t aId) {
748
return mShmemMap.Lookup(aId);
749
}
750
751
bool IToplevelProtocol::IsTrackingSharedMemory(Shmem::SharedMemory* segment) {
752
return mShmemMap.HasData(segment);
753
}
754
755
bool IToplevelProtocol::DestroySharedMemory(Shmem& shmem) {
756
Shmem::id_t aId = shmem.Id(Shmem::PrivateIPDLCaller());
757
Shmem::SharedMemory* segment = LookupSharedMemory(aId);
758
if (!segment) {
759
return false;
760
}
761
762
Message* descriptor =
763
shmem.UnshareFrom(Shmem::PrivateIPDLCaller(), MSG_ROUTING_CONTROL);
764
765
mShmemMap.Remove(aId);
766
Shmem::Dealloc(Shmem::PrivateIPDLCaller(), segment);
767
768
MessageChannel* channel = GetIPCChannel();
769
if (!channel->CanSend()) {
770
delete descriptor;
771
return true;
772
}
773
774
return descriptor && channel->Send(descriptor);
775
}
776
777
void IToplevelProtocol::DeallocShmems() {
778
for (IDMap<SharedMemory*>::const_iterator cit = mShmemMap.begin();
779
cit != mShmemMap.end(); ++cit) {
780
Shmem::Dealloc(Shmem::PrivateIPDLCaller(), cit->second);
781
}
782
mShmemMap.Clear();
783
}
784
785
bool IToplevelProtocol::ShmemCreated(const Message& aMsg) {
786
Shmem::id_t id;
787
RefPtr<Shmem::SharedMemory> rawmem(
788
Shmem::OpenExisting(Shmem::PrivateIPDLCaller(), aMsg, &id, true));
789
if (!rawmem) {
790
return false;
791
}
792
mShmemMap.AddWithID(rawmem.forget().take(), id);
793
return true;
794
}
795
796
bool IToplevelProtocol::ShmemDestroyed(const Message& aMsg) {
797
Shmem::id_t id;
798
PickleIterator iter = PickleIterator(aMsg);
799
if (!IPC::ReadParam(&aMsg, &iter, &id)) {
800
return false;
801
}
802
aMsg.EndRead(iter);
803
804
Shmem::SharedMemory* rawmem = LookupSharedMemory(id);
805
if (rawmem) {
806
mShmemMap.Remove(id);
807
Shmem::Dealloc(Shmem::PrivateIPDLCaller(), rawmem);
808
}
809
return true;
810
}
811
812
already_AddRefed<nsIEventTarget> IToplevelProtocol::GetMessageEventTarget(
813
const Message& aMsg) {
814
int32_t route = aMsg.routing_id();
815
816
Maybe<MutexAutoLock> lock;
817
lock.emplace(mEventTargetMutex);
818
819
nsCOMPtr<nsIEventTarget> target = mEventTargetMap.Lookup(route);
820
821
if (aMsg.is_constructor()) {
822
ActorHandle handle;
823
PickleIterator iter = PickleIterator(aMsg);
824
if (!IPC::ReadParam(&aMsg, &iter, &handle)) {
825
return nullptr;
826
}
827
828
// Normally a new actor inherits its event target from its manager. If the
829
// manager has no event target, we give the subclass a chance to make a new
830
// one.
831
if (!target) {
832
MutexAutoUnlock unlock(mEventTargetMutex);
833
target = GetConstructedEventTarget(aMsg);
834
}
835
836
mEventTargetMap.AddWithID(target, handle.mId);
837
} else if (!target) {
838
// We don't need the lock after this point.
839
lock.reset();
840
841
target = GetSpecificMessageEventTarget(aMsg);
842
}
843
844
return target.forget();
845
}
846
847
already_AddRefed<nsIEventTarget> IToplevelProtocol::GetActorEventTarget(
848
IProtocol* aActor) {
849
MOZ_RELEASE_ASSERT(aActor->Id() != kNullActorId &&
850
aActor->Id() != kFreedActorId);
851
852
MutexAutoLock lock(mEventTargetMutex);
853
nsCOMPtr<nsIEventTarget> target = mEventTargetMap.Lookup(aActor->Id());
854
return target.forget();
855
}
856
857
nsIEventTarget* IToplevelProtocol::GetActorEventTarget() {
858
// The EventTarget of a ToplevelProtocol shall never be set.
859
return nullptr;
860
}
861
862
void IToplevelProtocol::SetEventTargetForActorInternal(
863
IProtocol* aActor, nsIEventTarget* aEventTarget) {
864
// The EventTarget of a ToplevelProtocol shall never be set.
865
MOZ_RELEASE_ASSERT(aActor != this);
866
867
// We should only call this function on actors that haven't been used for IPC
868
// code yet. Otherwise we'll be posting stuff to the wrong event target before
869
// we're called.
870
MOZ_RELEASE_ASSERT(aActor->Id() == kNullActorId ||
871
aActor->Id() == kFreedActorId);
872
873
MOZ_ASSERT(aActor->Manager() && aActor->ToplevelProtocol() == this);
874
875
// Register the actor early. When it's registered again, it will keep the same
876
// ID.
877
int32_t id = Register(aActor);
878
aActor->SetId(id);
879
880
MutexAutoLock lock(mEventTargetMutex);
881
// FIXME bug 1445121 - sometimes the id is already mapped.
882
// (IDMap debug-asserts that the existing state is as expected.)
883
bool replace = false;
884
#ifdef DEBUG
885
replace = mEventTargetMap.Lookup(id) != nullptr;
886
#endif
887
if (replace) {
888
mEventTargetMap.ReplaceWithID(aEventTarget, id);
889
} else {
890
mEventTargetMap.AddWithID(aEventTarget, id);
891
}
892
}
893
894
void IToplevelProtocol::ReplaceEventTargetForActor(
895
IProtocol* aActor, nsIEventTarget* aEventTarget) {
896
// The EventTarget of a ToplevelProtocol shall never be set.
897
MOZ_RELEASE_ASSERT(aActor != this);
898
899
int32_t id = aActor->Id();
900
// The ID of the actor should have existed.
901
MOZ_RELEASE_ASSERT(id != kNullActorId && id != kFreedActorId);
902
903
MutexAutoLock lock(mEventTargetMutex);
904
mEventTargetMap.ReplaceWithID(aEventTarget, id);
905
}
906
907
void IToplevelProtocol::SetEventTargetForRoute(int32_t aRoute,
908
nsIEventTarget* aEventTarget) {
909
MOZ_RELEASE_ASSERT(aRoute != Id());
910
MOZ_RELEASE_ASSERT(aRoute != kNullActorId && aRoute != kFreedActorId);
911
912
MutexAutoLock lock(mEventTargetMutex);
913
MOZ_ASSERT(!mEventTargetMap.Lookup(aRoute));
914
mEventTargetMap.AddWithID(aEventTarget, aRoute);
915
}
916
917
} // namespace ipc
918
} // namespace mozilla