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 file,
5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "mozilla/ipc/NodeChannel.h"
8 #include "chrome/common/ipc_message.h"
9 #include "chrome/common/ipc_message_utils.h"
10 #include "mojo/core/ports/name.h"
11 #include "mozilla/ipc/BrowserProcessSubThread.h"
12 #include "mozilla/ipc/ProtocolMessageUtils.h"
13 #include "mozilla/ipc/ProtocolUtils.h"
14 #include "nsThreadUtils.h"
15 #include "nsXULAppAPI.h"
17 #ifdef FUZZING_SNAPSHOT
18 # include "mozilla/fuzzing/IPCFuzzController.h"
22 struct IPC::ParamTraits
<mozilla::ipc::NodeChannel::Introduction
> {
23 using paramType
= mozilla::ipc::NodeChannel::Introduction
;
24 static void Write(MessageWriter
* aWriter
, paramType
&& aParam
) {
25 WriteParam(aWriter
, aParam
.mName
);
26 WriteParam(aWriter
, std::move(aParam
.mHandle
));
27 WriteParam(aWriter
, aParam
.mMode
);
28 WriteParam(aWriter
, aParam
.mMyPid
);
29 WriteParam(aWriter
, aParam
.mOtherPid
);
31 static bool Read(MessageReader
* aReader
, paramType
* aResult
) {
32 return ReadParam(aReader
, &aResult
->mName
) &&
33 ReadParam(aReader
, &aResult
->mHandle
) &&
34 ReadParam(aReader
, &aResult
->mMode
) &&
35 ReadParam(aReader
, &aResult
->mMyPid
) &&
36 ReadParam(aReader
, &aResult
->mOtherPid
);
40 namespace mozilla::ipc
{
42 NodeChannel::NodeChannel(const NodeName
& aName
,
43 UniquePtr
<IPC::Channel
> aChannel
, Listener
* aListener
,
45 : mListener(aListener
),
48 mChannel(std::move(aChannel
)) {}
50 NodeChannel::~NodeChannel() {
57 // Called when the NodeChannel's refcount drops to `0`.
58 void NodeChannel::Destroy() {
59 // Dispatch the `delete` operation to the IO thread. We need to do this even
60 // if we're already on the IO thread, as we could be in an `IPC::Channel`
61 // callback which unfortunately will not hold a strong reference to keep
63 MessageLoop
* ioThread
= XRE_GetIOMessageLoop();
64 if (ioThread
->IsAcceptingTasks()) {
65 ioThread
->PostTask(NewNonOwningRunnableMethod("NodeChannel::Destroy", this,
66 &NodeChannel::FinalDestroy
));
70 // If the IOThread has already been destroyed, we must be shutting it down and
71 // need to synchronously invoke `FinalDestroy` to ensure we're cleaned up
72 // before the thread dies. This is safe as we can't be in a non-owning
73 // IPC::Channel callback at this point.
74 if (MessageLoop::current() == ioThread
) {
79 MOZ_ASSERT_UNREACHABLE("Leaking NodeChannel after IOThread destroyed!");
82 void NodeChannel::FinalDestroy() {
87 void NodeChannel::Start(bool aCallConnect
) {
90 mExistingListener
= mChannel
->set_listener(this);
92 std::queue
<UniquePtr
<IPC::Message
>> pending
;
93 if (mExistingListener
) {
94 mExistingListener
->GetQueuedMessages(pending
);
98 MOZ_ASSERT(pending
.empty(), "unopened channel with pending messages?");
99 if (!mChannel
->Connect()) {
103 // Check if our channel has already been connected, and knows the other PID.
104 base::ProcessId otherPid
= mChannel
->OtherPid();
105 if (otherPid
!= base::kInvalidProcessId
) {
106 SetOtherPid(otherPid
);
109 // Handle any events the previous listener had queued up. Make sure to stop
110 // if an error causes our channel to become closed.
111 while (!pending
.empty() && !mClosed
) {
112 OnMessageReceived(std::move(pending
.front()));
118 void NodeChannel::Close() {
123 mChannel
->set_listener(mExistingListener
);
128 void NodeChannel::SetOtherPid(base::ProcessId aNewPid
) {
130 MOZ_ASSERT(aNewPid
!= base::kInvalidProcessId
);
132 base::ProcessId previousPid
= base::kInvalidProcessId
;
133 if (!mOtherPid
.compare_exchange_strong(previousPid
, aNewPid
)) {
134 // The PID was already set before this call, double-check that it's correct.
135 MOZ_RELEASE_ASSERT(previousPid
== aNewPid
,
136 "Different sources disagree on the correct pid?");
141 void NodeChannel::SetMachTaskPort(task_t aTask
) {
145 mChannel
->SetOtherMachTask(aTask
);
150 void NodeChannel::SendEventMessage(UniquePtr
<IPC::Message
> aMessage
) {
151 // Make sure we're not sending a message with one of our special internal
152 // types ,as those should only be sent using the corresponding methods on
154 MOZ_DIAGNOSTIC_ASSERT(aMessage
->type() != BROADCAST_MESSAGE_TYPE
&&
155 aMessage
->type() != INTRODUCE_MESSAGE_TYPE
&&
156 aMessage
->type() != REQUEST_INTRODUCTION_MESSAGE_TYPE
&&
157 aMessage
->type() != ACCEPT_INVITE_MESSAGE_TYPE
);
158 SendMessage(std::move(aMessage
));
161 void NodeChannel::RequestIntroduction(const NodeName
& aPeerName
) {
162 MOZ_ASSERT(aPeerName
!= mojo::core::ports::kInvalidNodeName
);
163 auto message
= MakeUnique
<IPC::Message
>(MSG_ROUTING_CONTROL
,
164 REQUEST_INTRODUCTION_MESSAGE_TYPE
);
165 IPC::MessageWriter
writer(*message
);
166 WriteParam(&writer
, aPeerName
);
167 SendMessage(std::move(message
));
170 void NodeChannel::Introduce(Introduction aIntroduction
) {
172 MakeUnique
<IPC::Message
>(MSG_ROUTING_CONTROL
, INTRODUCE_MESSAGE_TYPE
);
173 IPC::MessageWriter
writer(*message
);
174 WriteParam(&writer
, std::move(aIntroduction
));
175 SendMessage(std::move(message
));
178 void NodeChannel::Broadcast(UniquePtr
<IPC::Message
> aMessage
) {
179 MOZ_DIAGNOSTIC_ASSERT(aMessage
->type() == BROADCAST_MESSAGE_TYPE
,
180 "Can only broadcast messages with the correct type");
181 SendMessage(std::move(aMessage
));
184 void NodeChannel::AcceptInvite(const NodeName
& aRealName
,
185 const PortName
& aInitialPort
) {
186 MOZ_ASSERT(aRealName
!= mojo::core::ports::kInvalidNodeName
);
187 MOZ_ASSERT(aInitialPort
!= mojo::core::ports::kInvalidPortName
);
189 MakeUnique
<IPC::Message
>(MSG_ROUTING_CONTROL
, ACCEPT_INVITE_MESSAGE_TYPE
);
190 IPC::MessageWriter
writer(*message
);
191 WriteParam(&writer
, aRealName
);
192 WriteParam(&writer
, aInitialPort
);
193 SendMessage(std::move(message
));
196 void NodeChannel::SendMessage(UniquePtr
<IPC::Message
> aMessage
) {
197 if (aMessage
->size() > IPC::Channel::kMaximumMessageSize
) {
198 CrashReporter::AnnotateCrashReport(
199 CrashReporter::Annotation::IPCMessageName
,
200 nsDependentCString(aMessage
->name()));
201 CrashReporter::AnnotateCrashReport(
202 CrashReporter::Annotation::IPCMessageSize
,
203 static_cast<unsigned int>(aMessage
->size()));
204 MOZ_CRASH("IPC message size is too large");
206 aMessage
->AssertAsLargeAsHeader();
208 XRE_GetIOMessageLoop()->PostTask(
209 NewRunnableMethod
<StoreCopyPassByRRef
<UniquePtr
<IPC::Message
>>>(
210 "NodeChannel::DoSendMessage", this, &NodeChannel::DoSendMessage
,
211 std::move(aMessage
)));
214 void NodeChannel::DoSendMessage(UniquePtr
<IPC::Message
> aMessage
) {
215 #ifdef FUZZING_SNAPSHOT
216 if (mBlockSendRecv
) {
223 NS_WARNING("Dropping message as channel has been closed");
227 if (!mChannel
->Send(std::move(aMessage
))) {
228 NS_WARNING("Call to Send() failed");
233 void NodeChannel::OnMessageReceived(UniquePtr
<IPC::Message
> aMessage
) {
236 #ifdef FUZZING_SNAPSHOT
237 if (mBlockSendRecv
&& !aMessage
->IsFuzzMsg()) {
242 IPC::MessageReader
reader(*aMessage
);
243 switch (aMessage
->type()) {
244 case REQUEST_INTRODUCTION_MESSAGE_TYPE
: {
246 if (IPC::ReadParam(&reader
, &name
)) {
247 mListener
->OnRequestIntroduction(mName
, name
);
252 case INTRODUCE_MESSAGE_TYPE
: {
253 Introduction introduction
;
254 if (IPC::ReadParam(&reader
, &introduction
)) {
255 mListener
->OnIntroduce(mName
, std::move(introduction
));
260 case BROADCAST_MESSAGE_TYPE
: {
261 mListener
->OnBroadcast(mName
, std::move(aMessage
));
264 case ACCEPT_INVITE_MESSAGE_TYPE
: {
266 PortName initialPort
;
267 if (IPC::ReadParam(&reader
, &realName
) &&
268 IPC::ReadParam(&reader
, &initialPort
)) {
269 mListener
->OnAcceptInvite(mName
, realName
, initialPort
);
274 // Assume all unrecognized types are intended as user event messages, and
275 // deliver them to our listener as such. This allows us to use the same type
276 // field for both internal messages and protocol messages.
278 // FIXME: Consider doing something cleaner in the future?
279 case EVENT_MESSAGE_TYPE
:
281 #ifdef FUZZING_SNAPSHOT
282 if (!fuzzing::IPCFuzzController::instance().ObserveIPCMessage(
288 mListener
->OnEventMessage(mName
, std::move(aMessage
));
293 // If we got to this point without early returning the message was malformed
294 // in some way. Report an error.
296 NS_WARNING("NodeChannel received a malformed message");
300 void NodeChannel::OnChannelConnected(base::ProcessId aPeerPid
) {
303 SetOtherPid(aPeerPid
);
305 // We may need to tell our original listener (which will be the process launch
306 // code) that the the channel has been connected to unblock completing the
308 // FIXME: This is super sketchy, but it's also what we were already doing. We
309 // should swap this out for something less sketchy.
310 if (mExistingListener
) {
311 mExistingListener
->OnChannelConnected(aPeerPid
);
315 void NodeChannel::OnChannelError() {
318 // Clean up the channel and make sure we're no longer the active listener.
320 MOZ_ALWAYS_TRUE(this == mChannel
->set_listener(mExistingListener
));
323 // Tell our listener about the error.
324 mListener
->OnChannelError(mName
);
327 } // namespace mozilla::ipc