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