Backed out changeset 1ecf6afb4dc7 for causing failures.
[gecko.git] / ipc / glue / NodeChannel.cpp
blobdf81a9dc0e658d15da010a0dc020b2f0ea415a2c
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"
20 #endif
22 template <>
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,
45 base::ProcessId aPid,
46 GeckoChildProcessHost* aChildProcessHost)
47 : mListener(aListener),
48 mName(aName),
49 mOtherPid(aPid),
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
60 // `this` alive.
61 MessageLoop* ioThread = XRE_GetIOMessageLoop();
62 if (ioThread->IsAcceptingTasks()) {
63 ioThread->PostTask(NewNonOwningRunnableMethod("NodeChannel::Destroy", this,
64 &NodeChannel::FinalDestroy));
65 return;
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) {
73 FinalDestroy();
74 return;
77 MOZ_ASSERT_UNREACHABLE("Leaking NodeChannel after IOThread destroyed!");
80 void NodeChannel::FinalDestroy() {
81 AssertIOThread();
82 delete this;
85 void NodeChannel::Start() {
86 AssertIOThread();
88 if (!mChannel->Connect(this)) {
89 OnChannelError();
93 void NodeChannel::Close() {
94 AssertIOThread();
96 if (mState.exchange(State::Closed) != State::Closed) {
97 mChannel->Close();
101 void NodeChannel::SetOtherPid(base::ProcessId aNewPid) {
102 AssertIOThread();
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);
115 #ifdef XP_DARWIN
116 void NodeChannel::SetMachTaskPort(task_t aTask) {
117 AssertIOThread();
119 if (mState != State::Closed) {
120 mChannel->SetOtherMachTask(aTask);
123 #endif
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
128 // NodeChannel.
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) {
146 auto message =
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);
163 auto message =
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 CrashReporter::RecordAnnotationU32(
178 CrashReporter::Annotation::IPCMessageLargeBufferShmemFailureSize,
179 aMessage->LargeBufferShmemFailureSize());
180 MOZ_CRASH("IPC message size is too large");
182 aMessage->AssertAsLargeAsHeader();
184 #ifdef FUZZING_SNAPSHOT
185 if (mBlockSendRecv) {
186 return;
188 #endif
190 if (mState != State::Active) {
191 NS_WARNING("Dropping message as channel has been closed");
192 return;
195 // NOTE: As this is not guaranteed to be running on the I/O thread, the
196 // channel may have become closed since we checked above. IPC::Channel will
197 // handle that and return `false` here, so we can re-check `mState`.
198 if (!mChannel->Send(std::move(aMessage))) {
199 NS_WARNING("Call to Send() failed");
201 // If we're still active, update `mState` to `State::Closing`, and dispatch
202 // a runnable to actually close our channel.
203 State expected = State::Active;
204 if (mState.compare_exchange_strong(expected, State::Closing)) {
205 XRE_GetIOMessageLoop()->PostTask(
206 NewRunnableMethod("NodeChannel::CloseForSendError", this,
207 &NodeChannel::OnChannelError));
212 void NodeChannel::OnMessageReceived(UniquePtr<IPC::Message> aMessage) {
213 AssertIOThread();
215 #ifdef FUZZING_SNAPSHOT
216 if (mBlockSendRecv && !aMessage->IsFuzzMsg()) {
217 return;
219 #endif
221 IPC::MessageReader reader(*aMessage);
222 switch (aMessage->type()) {
223 case REQUEST_INTRODUCTION_MESSAGE_TYPE: {
224 NodeName name;
225 if (IPC::ReadParam(&reader, &name)) {
226 mListener->OnRequestIntroduction(mName, name);
227 return;
229 break;
231 case INTRODUCE_MESSAGE_TYPE: {
232 Introduction introduction;
233 if (IPC::ReadParam(&reader, &introduction)) {
234 mListener->OnIntroduce(mName, std::move(introduction));
235 return;
237 break;
239 case BROADCAST_MESSAGE_TYPE: {
240 mListener->OnBroadcast(mName, std::move(aMessage));
241 return;
243 case ACCEPT_INVITE_MESSAGE_TYPE: {
244 NodeName realName;
245 PortName initialPort;
246 if (IPC::ReadParam(&reader, &realName) &&
247 IPC::ReadParam(&reader, &initialPort)) {
248 mListener->OnAcceptInvite(mName, realName, initialPort);
249 return;
251 break;
253 // Assume all unrecognized types are intended as user event messages, and
254 // deliver them to our listener as such. This allows us to use the same type
255 // field for both internal messages and protocol messages.
257 // FIXME: Consider doing something cleaner in the future?
258 case EVENT_MESSAGE_TYPE:
259 default: {
260 #ifdef FUZZING_SNAPSHOT
261 if (!fuzzing::IPCFuzzController::instance().ObserveIPCMessage(
262 this, *aMessage)) {
263 return;
265 #endif
267 mListener->OnEventMessage(mName, std::move(aMessage));
268 return;
272 // If we got to this point without early returning the message was malformed
273 // in some way. Report an error.
275 NS_WARNING("NodeChannel received a malformed message");
276 OnChannelError();
279 void NodeChannel::OnChannelConnected(base::ProcessId aPeerPid) {
280 AssertIOThread();
282 SetOtherPid(aPeerPid);
284 // We may need to tell the GeckoChildProcessHost which we were created by that
285 // the channel has been connected to unblock completing the process launch.
286 if (mChildProcessHost) {
287 mChildProcessHost->OnChannelConnected(aPeerPid);
291 void NodeChannel::OnChannelError() {
292 AssertIOThread();
294 State prev = mState.exchange(State::Closed);
295 if (prev == State::Closed) {
296 return;
299 // Clean up the channel.
300 mChannel->Close();
302 // Tell our listener about the error.
303 mListener->OnChannelError(mName);
306 } // namespace mozilla::ipc