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/GeckoChildProcessHost.h"
13 #include "mozilla/ipc/ProtocolMessageUtils.h"
14 #include "mozilla/ipc/ProtocolUtils.h"
15 #include "nsThreadUtils.h"
16 #include "nsXULAppAPI.h"
18 #ifdef FUZZING_SNAPSHOT
19 # include "mozilla/fuzzing/IPCFuzzController.h"
23 struct IPC::ParamTraits
<mozilla::ipc::NodeChannel::Introduction
> {
24 using paramType
= mozilla::ipc::NodeChannel::Introduction
;
25 static void Write(MessageWriter
* aWriter
, paramType
&& aParam
) {
26 WriteParam(aWriter
, aParam
.mName
);
27 WriteParam(aWriter
, std::move(aParam
.mHandle
));
28 WriteParam(aWriter
, aParam
.mMode
);
29 WriteParam(aWriter
, aParam
.mMyPid
);
30 WriteParam(aWriter
, aParam
.mOtherPid
);
32 static bool Read(MessageReader
* aReader
, paramType
* aResult
) {
33 return ReadParam(aReader
, &aResult
->mName
) &&
34 ReadParam(aReader
, &aResult
->mHandle
) &&
35 ReadParam(aReader
, &aResult
->mMode
) &&
36 ReadParam(aReader
, &aResult
->mMyPid
) &&
37 ReadParam(aReader
, &aResult
->mOtherPid
);
41 namespace mozilla::ipc
{
43 NodeChannel::NodeChannel(const NodeName
& aName
,
44 UniquePtr
<IPC::Channel
> aChannel
, Listener
* aListener
,
46 GeckoChildProcessHost
* aChildProcessHost
)
47 : mListener(aListener
),
50 mChannel(std::move(aChannel
)),
51 mChildProcessHost(aChildProcessHost
) {}
53 NodeChannel::~NodeChannel() { Close(); }
55 // Called when the NodeChannel's refcount drops to `0`.
56 void NodeChannel::Destroy() {
57 // Dispatch the `delete` operation to the IO thread. We need to do this even
58 // if we're already on the IO thread, as we could be in an `IPC::Channel`
59 // callback which unfortunately will not hold a strong reference to keep
61 MessageLoop
* ioThread
= XRE_GetIOMessageLoop();
62 if (ioThread
->IsAcceptingTasks()) {
63 ioThread
->PostTask(NewNonOwningRunnableMethod("NodeChannel::Destroy", this,
64 &NodeChannel::FinalDestroy
));
68 // If the IOThread has already been destroyed, we must be shutting it down and
69 // need to synchronously invoke `FinalDestroy` to ensure we're cleaned up
70 // before the thread dies. This is safe as we can't be in a non-owning
71 // IPC::Channel callback at this point.
72 if (MessageLoop::current() == ioThread
) {
77 MOZ_ASSERT_UNREACHABLE("Leaking NodeChannel after IOThread destroyed!");
80 void NodeChannel::FinalDestroy() {
85 void NodeChannel::Start() {
88 if (!mChannel
->Connect(this)) {
93 void NodeChannel::Close() {
96 if (mState
.exchange(State::Closed
) != State::Closed
) {
101 void NodeChannel::SetOtherPid(base::ProcessId aNewPid
) {
103 MOZ_ASSERT(aNewPid
!= base::kInvalidProcessId
);
105 base::ProcessId previousPid
= base::kInvalidProcessId
;
106 if (!mOtherPid
.compare_exchange_strong(previousPid
, aNewPid
)) {
107 // The PID was already set before this call, double-check that it's correct.
108 MOZ_RELEASE_ASSERT(previousPid
== aNewPid
,
109 "Different sources disagree on the correct pid?");
112 mChannel
->SetOtherPid(aNewPid
);
116 void NodeChannel::SetMachTaskPort(task_t aTask
) {
119 if (mState
!= State::Closed
) {
120 mChannel
->SetOtherMachTask(aTask
);
125 void NodeChannel::SendEventMessage(UniquePtr
<IPC::Message
> aMessage
) {
126 // Make sure we're not sending a message with one of our special internal
127 // types ,as those should only be sent using the corresponding methods on
129 MOZ_DIAGNOSTIC_ASSERT(aMessage
->type() != BROADCAST_MESSAGE_TYPE
&&
130 aMessage
->type() != INTRODUCE_MESSAGE_TYPE
&&
131 aMessage
->type() != REQUEST_INTRODUCTION_MESSAGE_TYPE
&&
132 aMessage
->type() != ACCEPT_INVITE_MESSAGE_TYPE
);
133 SendMessage(std::move(aMessage
));
136 void NodeChannel::RequestIntroduction(const NodeName
& aPeerName
) {
137 MOZ_ASSERT(aPeerName
!= mojo::core::ports::kInvalidNodeName
);
138 auto message
= MakeUnique
<IPC::Message
>(MSG_ROUTING_CONTROL
,
139 REQUEST_INTRODUCTION_MESSAGE_TYPE
);
140 IPC::MessageWriter
writer(*message
);
141 WriteParam(&writer
, aPeerName
);
142 SendMessage(std::move(message
));
145 void NodeChannel::Introduce(Introduction aIntroduction
) {
147 MakeUnique
<IPC::Message
>(MSG_ROUTING_CONTROL
, INTRODUCE_MESSAGE_TYPE
);
148 IPC::MessageWriter
writer(*message
);
149 WriteParam(&writer
, std::move(aIntroduction
));
150 SendMessage(std::move(message
));
153 void NodeChannel::Broadcast(UniquePtr
<IPC::Message
> aMessage
) {
154 MOZ_DIAGNOSTIC_ASSERT(aMessage
->type() == BROADCAST_MESSAGE_TYPE
,
155 "Can only broadcast messages with the correct type");
156 SendMessage(std::move(aMessage
));
159 void NodeChannel::AcceptInvite(const NodeName
& aRealName
,
160 const PortName
& aInitialPort
) {
161 MOZ_ASSERT(aRealName
!= mojo::core::ports::kInvalidNodeName
);
162 MOZ_ASSERT(aInitialPort
!= mojo::core::ports::kInvalidPortName
);
164 MakeUnique
<IPC::Message
>(MSG_ROUTING_CONTROL
, ACCEPT_INVITE_MESSAGE_TYPE
);
165 IPC::MessageWriter
writer(*message
);
166 WriteParam(&writer
, aRealName
);
167 WriteParam(&writer
, aInitialPort
);
168 SendMessage(std::move(message
));
171 void NodeChannel::SendMessage(UniquePtr
<IPC::Message
> aMessage
) {
172 if (aMessage
->size() > IPC::Channel::kMaximumMessageSize
) {
173 CrashReporter::AnnotateCrashReport(
174 CrashReporter::Annotation::IPCMessageName
,
175 nsDependentCString(aMessage
->name()));
176 CrashReporter::AnnotateCrashReport(
177 CrashReporter::Annotation::IPCMessageSize
,
178 static_cast<unsigned int>(aMessage
->size()));
179 MOZ_CRASH("IPC message size is too large");
181 aMessage
->AssertAsLargeAsHeader();
183 #ifdef FUZZING_SNAPSHOT
184 if (mBlockSendRecv
) {
189 if (mState
!= State::Active
) {
190 NS_WARNING("Dropping message as channel has been closed");
194 // NOTE: As this is not guaranteed to be running on the I/O thread, the
195 // channel may have become closed since we checked above. IPC::Channel will
196 // handle that and return `false` here, so we can re-check `mState`.
197 if (!mChannel
->Send(std::move(aMessage
))) {
198 NS_WARNING("Call to Send() failed");
200 // If we're still active, update `mState` to `State::Closing`, and dispatch
201 // a runnable to actually close our channel.
202 State expected
= State::Active
;
203 if (mState
.compare_exchange_strong(expected
, State::Closing
)) {
204 XRE_GetIOMessageLoop()->PostTask(
205 NewRunnableMethod("NodeChannel::CloseForSendError", this,
206 &NodeChannel::OnChannelError
));
211 void NodeChannel::OnMessageReceived(UniquePtr
<IPC::Message
> aMessage
) {
214 #ifdef FUZZING_SNAPSHOT
215 if (mBlockSendRecv
&& !aMessage
->IsFuzzMsg()) {
220 IPC::MessageReader
reader(*aMessage
);
221 switch (aMessage
->type()) {
222 case REQUEST_INTRODUCTION_MESSAGE_TYPE
: {
224 if (IPC::ReadParam(&reader
, &name
)) {
225 mListener
->OnRequestIntroduction(mName
, name
);
230 case INTRODUCE_MESSAGE_TYPE
: {
231 Introduction introduction
;
232 if (IPC::ReadParam(&reader
, &introduction
)) {
233 mListener
->OnIntroduce(mName
, std::move(introduction
));
238 case BROADCAST_MESSAGE_TYPE
: {
239 mListener
->OnBroadcast(mName
, std::move(aMessage
));
242 case ACCEPT_INVITE_MESSAGE_TYPE
: {
244 PortName initialPort
;
245 if (IPC::ReadParam(&reader
, &realName
) &&
246 IPC::ReadParam(&reader
, &initialPort
)) {
247 mListener
->OnAcceptInvite(mName
, realName
, initialPort
);
252 // Assume all unrecognized types are intended as user event messages, and
253 // deliver them to our listener as such. This allows us to use the same type
254 // field for both internal messages and protocol messages.
256 // FIXME: Consider doing something cleaner in the future?
257 case EVENT_MESSAGE_TYPE
:
259 #ifdef FUZZING_SNAPSHOT
260 if (!fuzzing::IPCFuzzController::instance().ObserveIPCMessage(
266 mListener
->OnEventMessage(mName
, std::move(aMessage
));
271 // If we got to this point without early returning the message was malformed
272 // in some way. Report an error.
274 NS_WARNING("NodeChannel received a malformed message");
278 void NodeChannel::OnChannelConnected(base::ProcessId aPeerPid
) {
281 SetOtherPid(aPeerPid
);
283 // We may need to tell the GeckoChildProcessHost which we were created by that
284 // the channel has been connected to unblock completing the process launch.
285 if (mChildProcessHost
) {
286 mChildProcessHost
->OnChannelConnected(aPeerPid
);
290 void NodeChannel::OnChannelError() {
293 State prev
= mState
.exchange(State::Closed
);
294 if (prev
== State::Closed
) {
298 // Clean up the channel.
301 // Tell our listener about the error.
302 mListener
->OnChannelError(mName
);
305 } // namespace mozilla::ipc