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::RecordAnnotationCString(
174 CrashReporter::Annotation::IPCMessageName
, aMessage
->name());
175 CrashReporter::RecordAnnotationU32(
176 CrashReporter::Annotation::IPCMessageSize
, aMessage
->size());
177 MOZ_CRASH("IPC message size is too large");
179 aMessage
->AssertAsLargeAsHeader();
181 #ifdef FUZZING_SNAPSHOT
182 if (mBlockSendRecv
) {
187 if (mState
!= State::Active
) {
188 NS_WARNING("Dropping message as channel has been closed");
192 // NOTE: As this is not guaranteed to be running on the I/O thread, the
193 // channel may have become closed since we checked above. IPC::Channel will
194 // handle that and return `false` here, so we can re-check `mState`.
195 if (!mChannel
->Send(std::move(aMessage
))) {
196 NS_WARNING("Call to Send() failed");
198 // If we're still active, update `mState` to `State::Closing`, and dispatch
199 // a runnable to actually close our channel.
200 State expected
= State::Active
;
201 if (mState
.compare_exchange_strong(expected
, State::Closing
)) {
202 XRE_GetIOMessageLoop()->PostTask(
203 NewRunnableMethod("NodeChannel::CloseForSendError", this,
204 &NodeChannel::OnChannelError
));
209 void NodeChannel::OnMessageReceived(UniquePtr
<IPC::Message
> aMessage
) {
212 #ifdef FUZZING_SNAPSHOT
213 if (mBlockSendRecv
&& !aMessage
->IsFuzzMsg()) {
218 IPC::MessageReader
reader(*aMessage
);
219 switch (aMessage
->type()) {
220 case REQUEST_INTRODUCTION_MESSAGE_TYPE
: {
222 if (IPC::ReadParam(&reader
, &name
)) {
223 mListener
->OnRequestIntroduction(mName
, name
);
228 case INTRODUCE_MESSAGE_TYPE
: {
229 Introduction introduction
;
230 if (IPC::ReadParam(&reader
, &introduction
)) {
231 mListener
->OnIntroduce(mName
, std::move(introduction
));
236 case BROADCAST_MESSAGE_TYPE
: {
237 mListener
->OnBroadcast(mName
, std::move(aMessage
));
240 case ACCEPT_INVITE_MESSAGE_TYPE
: {
242 PortName initialPort
;
243 if (IPC::ReadParam(&reader
, &realName
) &&
244 IPC::ReadParam(&reader
, &initialPort
)) {
245 mListener
->OnAcceptInvite(mName
, realName
, initialPort
);
250 // Assume all unrecognized types are intended as user event messages, and
251 // deliver them to our listener as such. This allows us to use the same type
252 // field for both internal messages and protocol messages.
254 // FIXME: Consider doing something cleaner in the future?
255 case EVENT_MESSAGE_TYPE
:
257 #ifdef FUZZING_SNAPSHOT
258 if (!fuzzing::IPCFuzzController::instance().ObserveIPCMessage(
264 mListener
->OnEventMessage(mName
, std::move(aMessage
));
269 // If we got to this point without early returning the message was malformed
270 // in some way. Report an error.
272 NS_WARNING("NodeChannel received a malformed message");
276 void NodeChannel::OnChannelConnected(base::ProcessId aPeerPid
) {
279 SetOtherPid(aPeerPid
);
281 // We may need to tell the GeckoChildProcessHost which we were created by that
282 // the channel has been connected to unblock completing the process launch.
283 if (mChildProcessHost
) {
284 mChildProcessHost
->OnChannelConnected(aPeerPid
);
288 void NodeChannel::OnChannelError() {
291 State prev
= mState
.exchange(State::Closed
);
292 if (prev
== State::Closed
) {
296 // Clean up the channel.
299 // Tell our listener about the error.
300 mListener
->OnChannelError(mName
);
303 } // namespace mozilla::ipc