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/. */
6
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"
16
17 template <>
18 struct IPC::ParamTraits<mozilla::ipc::NodeChannel::Introduction> {
19 using paramType = mozilla::ipc::NodeChannel::Introduction;
WriteIPC::ParamTraits20 static void Write(MessageWriter* aWriter, paramType&& aParam) {
21 WriteParam(aWriter, aParam.mName);
22 WriteParam(aWriter, std::move(aParam.mHandle));
23 WriteParam(aWriter, aParam.mMode);
24 WriteParam(aWriter, aParam.mMyPid);
25 WriteParam(aWriter, aParam.mOtherPid);
26 }
ReadIPC::ParamTraits27 static bool Read(MessageReader* aReader, paramType* aResult) {
28 return ReadParam(aReader, &aResult->mName) &&
29 ReadParam(aReader, &aResult->mHandle) &&
30 ReadParam(aReader, &aResult->mMode) &&
31 ReadParam(aReader, &aResult->mMyPid) &&
32 ReadParam(aReader, &aResult->mOtherPid);
33 }
34 };
35
36 namespace mozilla::ipc {
37
NodeChannel(const NodeName & aName,UniquePtr<IPC::Channel> aChannel,Listener * aListener,int32_t aPid)38 NodeChannel::NodeChannel(const NodeName& aName,
39 UniquePtr<IPC::Channel> aChannel, Listener* aListener,
40 int32_t aPid)
41 : mListener(aListener),
42 mName(aName),
43 mOtherPid(aPid),
44 mChannel(std::move(aChannel)) {}
45
~NodeChannel()46 NodeChannel::~NodeChannel() {
47 AssertIOThread();
48 if (!mClosed) {
49 mChannel->Close();
50 }
51 }
52
53 // Called when the NodeChannel's refcount drops to `0`.
Destroy()54 void NodeChannel::Destroy() {
55 // Dispatch the `delete` operation to the IO thread. We need to do this even
56 // if we're already on the IO thread, as we could be in an `IPC::Channel`
57 // callback which unfortunately will not hold a strong reference to keep
58 // `this` alive.
59 MessageLoop* ioThread = XRE_GetIOMessageLoop();
60 if (ioThread->IsAcceptingTasks()) {
61 ioThread->PostTask(NewNonOwningRunnableMethod("NodeChannel::Destroy", this,
62 &NodeChannel::FinalDestroy));
63 return;
64 }
65
66 // If the IOThread has already been destroyed, we must be shutting it down and
67 // need to synchronously invoke `FinalDestroy` to ensure we're cleaned up
68 // before the thread dies. This is safe as we can't be in a non-owning
69 // IPC::Channel callback at this point.
70 if (MessageLoop::current() == ioThread) {
71 FinalDestroy();
72 return;
73 }
74
75 MOZ_ASSERT_UNREACHABLE("Leaking NodeChannel after IOThread destroyed!");
76 }
77
FinalDestroy()78 void NodeChannel::FinalDestroy() {
79 AssertIOThread();
80 delete this;
81 }
82
Start(bool aCallConnect)83 void NodeChannel::Start(bool aCallConnect) {
84 AssertIOThread();
85
86 mExistingListener = mChannel->set_listener(this);
87
88 std::queue<IPC::Message> pending;
89 if (mExistingListener) {
90 mExistingListener->GetQueuedMessages(pending);
91 }
92
93 if (aCallConnect) {
94 MOZ_ASSERT(pending.empty(), "unopened channel with pending messages?");
95 if (!mChannel->Connect()) {
96 OnChannelError();
97 }
98 } else {
99 // Check if our channel has already been connected, and knows the other PID.
100 int32_t otherPid = mChannel->OtherPid();
101 if (otherPid != -1) {
102 SetOtherPid(otherPid);
103 }
104
105 // Handle any events the previous listener had queued up. Make sure to stop
106 // if an error causes our channel to become closed.
107 while (!pending.empty() && !mClosed) {
108 OnMessageReceived(std::move(pending.front()));
109 pending.pop();
110 }
111 }
112 }
113
Close()114 void NodeChannel::Close() {
115 AssertIOThread();
116
117 if (!mClosed) {
118 mChannel->Close();
119 mChannel->set_listener(mExistingListener);
120 }
121 mClosed = true;
122 }
123
SetOtherPid(int32_t aNewPid)124 void NodeChannel::SetOtherPid(int32_t aNewPid) {
125 AssertIOThread();
126 MOZ_ASSERT(aNewPid != -1);
127
128 int32_t previousPid = -1;
129 if (!mOtherPid.compare_exchange_strong(previousPid, aNewPid)) {
130 // The PID was already set before this call, double-check that it's correct.
131 MOZ_RELEASE_ASSERT(previousPid == aNewPid,
132 "Different sources disagree on the correct pid?");
133 }
134 }
135
136 #ifdef XP_MACOSX
SetMachTaskPort(task_t aTask)137 void NodeChannel::SetMachTaskPort(task_t aTask) {
138 AssertIOThread();
139
140 if (!mClosed) {
141 mChannel->SetOtherMachTask(aTask);
142 }
143 }
144 #endif
145
SendEventMessage(UniquePtr<IPC::Message> aMessage)146 void NodeChannel::SendEventMessage(UniquePtr<IPC::Message> aMessage) {
147 // Make sure we're not sending a message with one of our special internal
148 // types ,as those should only be sent using the corresponding methods on
149 // NodeChannel.
150 MOZ_DIAGNOSTIC_ASSERT(aMessage->type() != BROADCAST_MESSAGE_TYPE &&
151 aMessage->type() != INTRODUCE_MESSAGE_TYPE &&
152 aMessage->type() != REQUEST_INTRODUCTION_MESSAGE_TYPE &&
153 aMessage->type() != ACCEPT_INVITE_MESSAGE_TYPE);
154 SendMessage(std::move(aMessage));
155 }
156
RequestIntroduction(const NodeName & aPeerName)157 void NodeChannel::RequestIntroduction(const NodeName& aPeerName) {
158 MOZ_ASSERT(aPeerName != mojo::core::ports::kInvalidNodeName);
159 auto message = MakeUnique<IPC::Message>(MSG_ROUTING_CONTROL,
160 REQUEST_INTRODUCTION_MESSAGE_TYPE);
161 IPC::MessageWriter writer(*message);
162 WriteParam(&writer, aPeerName);
163 SendMessage(std::move(message));
164 }
165
Introduce(Introduction aIntroduction)166 void NodeChannel::Introduce(Introduction aIntroduction) {
167 auto message =
168 MakeUnique<IPC::Message>(MSG_ROUTING_CONTROL, INTRODUCE_MESSAGE_TYPE);
169 IPC::MessageWriter writer(*message);
170 WriteParam(&writer, std::move(aIntroduction));
171 SendMessage(std::move(message));
172 }
173
Broadcast(UniquePtr<IPC::Message> aMessage)174 void NodeChannel::Broadcast(UniquePtr<IPC::Message> aMessage) {
175 MOZ_DIAGNOSTIC_ASSERT(aMessage->type() == BROADCAST_MESSAGE_TYPE,
176 "Can only broadcast messages with the correct type");
177 SendMessage(std::move(aMessage));
178 }
179
AcceptInvite(const NodeName & aRealName,const PortName & aInitialPort)180 void NodeChannel::AcceptInvite(const NodeName& aRealName,
181 const PortName& aInitialPort) {
182 MOZ_ASSERT(aRealName != mojo::core::ports::kInvalidNodeName);
183 MOZ_ASSERT(aInitialPort != mojo::core::ports::kInvalidPortName);
184 auto message =
185 MakeUnique<IPC::Message>(MSG_ROUTING_CONTROL, ACCEPT_INVITE_MESSAGE_TYPE);
186 IPC::MessageWriter writer(*message);
187 WriteParam(&writer, aRealName);
188 WriteParam(&writer, aInitialPort);
189 SendMessage(std::move(message));
190 }
191
SendMessage(UniquePtr<IPC::Message> aMessage)192 void NodeChannel::SendMessage(UniquePtr<IPC::Message> aMessage) {
193 if (aMessage->size() > IPC::Channel::kMaximumMessageSize) {
194 CrashReporter::AnnotateCrashReport(
195 CrashReporter::Annotation::IPCMessageName,
196 nsDependentCString(aMessage->name()));
197 CrashReporter::AnnotateCrashReport(
198 CrashReporter::Annotation::IPCMessageSize,
199 static_cast<unsigned int>(aMessage->size()));
200 MOZ_CRASH("IPC message size is too large");
201 }
202 aMessage->AssertAsLargeAsHeader();
203
204 XRE_GetIOMessageLoop()->PostTask(
205 NewRunnableMethod<StoreCopyPassByRRef<UniquePtr<IPC::Message>>>(
206 "NodeChannel::DoSendMessage", this, &NodeChannel::DoSendMessage,
207 std::move(aMessage)));
208 }
209
DoSendMessage(UniquePtr<IPC::Message> aMessage)210 void NodeChannel::DoSendMessage(UniquePtr<IPC::Message> aMessage) {
211 AssertIOThread();
212 if (mClosed) {
213 NS_WARNING("Dropping message as channel has been closed");
214 return;
215 }
216
217 if (!mChannel->Send(std::move(aMessage))) {
218 NS_WARNING("Call to Send() failed");
219 OnChannelError();
220 }
221 }
222
OnMessageReceived(IPC::Message && aMessage)223 void NodeChannel::OnMessageReceived(IPC::Message&& aMessage) {
224 AssertIOThread();
225
226 if (!aMessage.is_valid()) {
227 NS_WARNING("Received an invalid message");
228 OnChannelError();
229 return;
230 }
231
232 IPC::MessageReader reader(aMessage);
233 switch (aMessage.type()) {
234 case REQUEST_INTRODUCTION_MESSAGE_TYPE: {
235 NodeName name;
236 if (IPC::ReadParam(&reader, &name)) {
237 mListener->OnRequestIntroduction(mName, name);
238 return;
239 }
240 break;
241 }
242 case INTRODUCE_MESSAGE_TYPE: {
243 Introduction introduction;
244 if (IPC::ReadParam(&reader, &introduction)) {
245 mListener->OnIntroduce(mName, std::move(introduction));
246 return;
247 }
248 break;
249 }
250 case BROADCAST_MESSAGE_TYPE: {
251 mListener->OnBroadcast(mName,
252 MakeUnique<IPC::Message>(std::move(aMessage)));
253 return;
254 }
255 case ACCEPT_INVITE_MESSAGE_TYPE: {
256 NodeName realName;
257 PortName initialPort;
258 if (IPC::ReadParam(&reader, &realName) &&
259 IPC::ReadParam(&reader, &initialPort)) {
260 mListener->OnAcceptInvite(mName, realName, initialPort);
261 return;
262 }
263 break;
264 }
265 // Assume all unrecognized types are intended as user event messages, and
266 // deliver them to our listener as such. This allows us to use the same type
267 // field for both internal messages and protocol messages.
268 //
269 // FIXME: Consider doing something cleaner in the future?
270 case EVENT_MESSAGE_TYPE:
271 default: {
272 mListener->OnEventMessage(mName,
273 MakeUnique<IPC::Message>(std::move(aMessage)));
274 return;
275 }
276 }
277
278 // If we got to this point without early returning the message was malformed
279 // in some way. Report an error.
280
281 NS_WARNING("NodeChannel received a malformed message");
282 OnChannelError();
283 }
284
OnChannelConnected(int32_t aPeerPid)285 void NodeChannel::OnChannelConnected(int32_t aPeerPid) {
286 AssertIOThread();
287
288 SetOtherPid(aPeerPid);
289
290 // We may need to tell our original listener (which will be the process launch
291 // code) that the the channel has been connected to unblock completing the
292 // process launch.
293 // FIXME: This is super sketchy, but it's also what we were already doing. We
294 // should swap this out for something less sketchy.
295 if (mExistingListener) {
296 mExistingListener->OnChannelConnected(aPeerPid);
297 }
298 }
299
OnChannelError()300 void NodeChannel::OnChannelError() {
301 AssertIOThread();
302
303 // Clean up the channel and make sure we're no longer the active listener.
304 mChannel->Close();
305 MOZ_ALWAYS_TRUE(this == mChannel->set_listener(mExistingListener));
306 mClosed = true;
307
308 // Tell our listener about the error.
309 mListener->OnChannelError(mName);
310 }
311
312 } // namespace mozilla::ipc
313