Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : * vim: sw=4 ts=4 et :
3 : */
4 : /* This Source Code Form is subject to the terms of the Mozilla Public
5 : * License, v. 2.0. If a copy of the MPL was not distributed with this
6 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 :
8 : #include "mozilla/ipc/MessageLink.h"
9 : #include "mozilla/ipc/MessageChannel.h"
10 : #include "mozilla/ipc/BrowserProcessSubThread.h"
11 : #include "mozilla/ipc/ProtocolUtils.h"
12 : #include "chrome/common/ipc_channel.h"
13 :
14 : #include "mozilla/Assertions.h"
15 : #include "mozilla/DebugOnly.h"
16 : #include "nsDebug.h"
17 : #ifdef MOZ_CRASHREPORTER
18 : #include "nsExceptionHandler.h"
19 : #endif
20 : #include "nsISupportsImpl.h"
21 : #include "nsPrintfCString.h"
22 : #include "nsXULAppAPI.h"
23 :
24 : using namespace mozilla;
25 : using namespace std;
26 :
27 : // We rely on invariants about the lifetime of the transport:
28 : //
29 : // - outlives this MessageChannel
30 : // - deleted on the IO thread
31 : //
32 : // These invariants allow us to send messages directly through the
33 : // transport without having to worry about orphaned Send() tasks on
34 : // the IO thread touching MessageChannel memory after it's been deleted
35 : // on the worker thread. We also don't need to refcount the
36 : // Transport, because whatever task triggers its deletion only runs on
37 : // the IO thread, and only runs after this MessageChannel is done with
38 : // the Transport.
39 :
40 : namespace mozilla {
41 : namespace ipc {
42 :
43 38 : MessageLink::MessageLink(MessageChannel *aChan)
44 38 : : mChan(aChan)
45 : {
46 38 : }
47 :
48 0 : MessageLink::~MessageLink()
49 : {
50 : #ifdef DEBUG
51 0 : mChan = nullptr;
52 : #endif
53 0 : }
54 :
55 28 : ProcessLink::ProcessLink(MessageChannel *aChan)
56 : : MessageLink(aChan)
57 : , mTransport(nullptr)
58 : , mIOLoop(nullptr)
59 28 : , mExistingListener(nullptr)
60 : {
61 28 : }
62 :
63 0 : ProcessLink::~ProcessLink()
64 : {
65 : #ifdef DEBUG
66 0 : mTransport = nullptr;
67 0 : mIOLoop = nullptr;
68 0 : mExistingListener = nullptr;
69 : #endif
70 0 : }
71 :
72 : void
73 28 : ProcessLink::Open(mozilla::ipc::Transport* aTransport, MessageLoop *aIOLoop, Side aSide)
74 : {
75 28 : NS_PRECONDITION(aTransport, "need transport layer");
76 :
77 : // FIXME need to check for valid channel
78 :
79 28 : mTransport = aTransport;
80 :
81 : // FIXME figure out whether we're in parent or child, grab IO loop
82 : // appropriately
83 28 : bool needOpen = true;
84 28 : if(aIOLoop) {
85 : // We're a child or using the new arguments. Either way, we
86 : // need an open.
87 26 : needOpen = true;
88 26 : mChan->mSide = (aSide == UnknownSide) ? ChildSide : aSide;
89 : } else {
90 2 : NS_PRECONDITION(aSide == UnknownSide, "expected default side arg");
91 :
92 : // parent
93 2 : mChan->mSide = ParentSide;
94 2 : needOpen = false;
95 2 : aIOLoop = XRE_GetIOMessageLoop();
96 : }
97 :
98 28 : mIOLoop = aIOLoop;
99 :
100 28 : NS_ASSERTION(mIOLoop, "need an IO loop");
101 28 : NS_ASSERTION(mChan->mWorkerLoop, "need a worker loop");
102 :
103 : // If we were never able to open the transport, immediately post an error message.
104 28 : if (mTransport->Unsound_IsClosed()) {
105 0 : mIOLoop->PostTask(
106 0 : NewNonOwningRunnableMethod("ipc::ProcessLink::OnChannelConnectError",
107 : this,
108 0 : &ProcessLink::OnChannelConnectError));
109 0 : return;
110 : }
111 :
112 : {
113 56 : MonitorAutoLock lock(*mChan->mMonitor);
114 :
115 28 : if (needOpen) {
116 : // Transport::Connect() has not been called. Call it so
117 : // we start polling our pipe and processing outgoing
118 : // messages.
119 52 : mIOLoop->PostTask(
120 52 : NewNonOwningRunnableMethod("ipc::ProcessLink::OnChannelOpened",
121 : this,
122 26 : &ProcessLink::OnChannelOpened));
123 : } else {
124 : // Transport::Connect() has already been called. Take
125 : // over the channel from the previous listener and process
126 : // any queued messages.
127 4 : mIOLoop->PostTask(NewNonOwningRunnableMethod(
128 : "ipc::ProcessLink::OnTakeConnectedChannel",
129 : this,
130 2 : &ProcessLink::OnTakeConnectedChannel));
131 : }
132 :
133 : // Should not wait here if something goes wrong with the channel.
134 84 : while (!mChan->Connected() && mChan->mChannelState != ChannelError) {
135 28 : mChan->mMonitor->Wait();
136 : }
137 : }
138 : }
139 :
140 : void
141 0 : ProcessLink::EchoMessage(Message *msg)
142 : {
143 0 : mChan->AssertWorkerThread();
144 0 : mChan->mMonitor->AssertCurrentThreadOwns();
145 :
146 0 : mIOLoop->PostTask(
147 0 : NewNonOwningRunnableMethod<Message*>("ipc::ProcessLink::OnEchoMessage",
148 : this,
149 : &ProcessLink::OnEchoMessage,
150 0 : msg));
151 : // OnEchoMessage takes ownership of |msg|
152 0 : }
153 :
154 : void
155 360 : ProcessLink::SendMessage(Message *msg)
156 : {
157 360 : if (msg->size() > IPC::Channel::kMaximumMessageSize) {
158 : #ifdef MOZ_CRASHREPORTER
159 0 : CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("IPCMessageName"), nsDependentCString(msg->name()));
160 0 : CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("IPCMessageSize"), nsPrintfCString("%d", msg->size()));
161 : #endif
162 0 : MOZ_CRASH("IPC message size is too large");
163 : }
164 :
165 360 : if (!mChan->mIsPostponingSends) {
166 360 : mChan->AssertWorkerThread();
167 : }
168 360 : mChan->mMonitor->AssertCurrentThreadOwns();
169 :
170 720 : mIOLoop->PostTask(NewNonOwningRunnableMethod<Message*>(
171 360 : "IPC::Channel::Send", mTransport, &Transport::Send, msg));
172 360 : }
173 :
174 : void
175 0 : ProcessLink::SendClose()
176 : {
177 0 : mChan->AssertWorkerThread();
178 0 : mChan->mMonitor->AssertCurrentThreadOwns();
179 :
180 0 : mIOLoop->PostTask(NewNonOwningRunnableMethod(
181 0 : "ipc::ProcessLink::OnCloseChannel", this, &ProcessLink::OnCloseChannel));
182 0 : }
183 :
184 10 : ThreadLink::ThreadLink(MessageChannel *aChan, MessageChannel *aTargetChan)
185 : : MessageLink(aChan),
186 10 : mTargetChan(aTargetChan)
187 : {
188 10 : }
189 :
190 0 : ThreadLink::~ThreadLink()
191 : {
192 0 : MOZ_ASSERT(mChan);
193 0 : MOZ_ASSERT(mChan->mMonitor);
194 0 : MonitorAutoLock lock(*mChan->mMonitor);
195 :
196 : // Bug 848949: We need to prevent the other side
197 : // from sending us any more messages to avoid Use-After-Free.
198 : // The setup here is as shown:
199 : //
200 : // (Us) (Them)
201 : // MessageChannel MessageChannel
202 : // | ^ \ / ^ |
203 : // | | X | |
204 : // v | / \ | v
205 : // ThreadLink ThreadLink
206 : //
207 : // We want to null out the diagonal link from their ThreadLink
208 : // to our MessageChannel. Note that we must hold the monitor so
209 : // that we do this atomically with respect to them trying to send
210 : // us a message. Since the channels share the same monitor this
211 : // also protects against the two ~ThreadLink() calls racing.
212 0 : if (mTargetChan) {
213 0 : MOZ_ASSERT(mTargetChan->mLink);
214 0 : static_cast<ThreadLink*>(mTargetChan->mLink)->mTargetChan = nullptr;
215 : }
216 0 : mTargetChan = nullptr;
217 0 : }
218 :
219 : void
220 0 : ThreadLink::EchoMessage(Message *msg)
221 : {
222 0 : mChan->AssertWorkerThread();
223 0 : mChan->mMonitor->AssertCurrentThreadOwns();
224 :
225 0 : mChan->OnMessageReceivedFromLink(Move(*msg));
226 0 : delete msg;
227 0 : }
228 :
229 : void
230 166 : ThreadLink::SendMessage(Message *msg)
231 : {
232 166 : if (!mChan->mIsPostponingSends) {
233 166 : mChan->AssertWorkerThread();
234 : }
235 166 : mChan->mMonitor->AssertCurrentThreadOwns();
236 :
237 166 : if (mTargetChan)
238 166 : mTargetChan->OnMessageReceivedFromLink(Move(*msg));
239 166 : delete msg;
240 166 : }
241 :
242 : void
243 0 : ThreadLink::SendClose()
244 : {
245 0 : mChan->AssertWorkerThread();
246 0 : mChan->mMonitor->AssertCurrentThreadOwns();
247 :
248 0 : mChan->mChannelState = ChannelClosed;
249 :
250 : // In a ProcessLink, we would close our half the channel. This
251 : // would show up on the other side as an error on the I/O thread.
252 : // The I/O thread would then invoke OnChannelErrorFromLink().
253 : // As usual, we skip that process and just invoke the
254 : // OnChannelErrorFromLink() method directly.
255 0 : if (mTargetChan)
256 0 : mTargetChan->OnChannelErrorFromLink();
257 0 : }
258 :
259 : bool
260 0 : ThreadLink::Unsound_IsClosed() const
261 : {
262 0 : MonitorAutoLock lock(*mChan->mMonitor);
263 0 : return mChan->mChannelState == ChannelClosed;
264 : }
265 :
266 : uint32_t
267 0 : ThreadLink::Unsound_NumQueuedMessages() const
268 : {
269 : // ThreadLinks don't have a message queue.
270 0 : return 0;
271 : }
272 :
273 : //
274 : // The methods below run in the context of the IO thread
275 : //
276 :
277 : void
278 365 : ProcessLink::OnMessageReceived(Message&& msg)
279 : {
280 365 : AssertIOThread();
281 365 : NS_ASSERTION(mChan->mChannelState != ChannelError, "Shouldn't get here!");
282 730 : MonitorAutoLock lock(*mChan->mMonitor);
283 365 : mChan->OnMessageReceivedFromLink(Move(msg));
284 365 : }
285 :
286 : void
287 0 : ProcessLink::OnEchoMessage(Message* msg)
288 : {
289 0 : AssertIOThread();
290 0 : OnMessageReceived(Move(*msg));
291 0 : delete msg;
292 0 : }
293 :
294 : void
295 26 : ProcessLink::OnChannelOpened()
296 : {
297 26 : AssertIOThread();
298 :
299 : {
300 52 : MonitorAutoLock lock(*mChan->mMonitor);
301 :
302 26 : mExistingListener = mTransport->set_listener(this);
303 : #ifdef DEBUG
304 26 : if (mExistingListener) {
305 4 : std::queue<Message> pending;
306 2 : mExistingListener->GetQueuedMessages(pending);
307 2 : MOZ_ASSERT(pending.empty());
308 : }
309 : #endif // DEBUG
310 :
311 26 : mChan->mChannelState = ChannelOpening;
312 26 : lock.Notify();
313 : }
314 26 : /*assert*/mTransport->Connect();
315 26 : }
316 :
317 : void
318 2 : ProcessLink::OnTakeConnectedChannel()
319 : {
320 2 : AssertIOThread();
321 :
322 4 : std::queue<Message> pending;
323 : {
324 4 : MonitorAutoLock lock(*mChan->mMonitor);
325 :
326 2 : mChan->mChannelState = ChannelConnected;
327 :
328 2 : mExistingListener = mTransport->set_listener(this);
329 2 : if (mExistingListener) {
330 2 : mExistingListener->GetQueuedMessages(pending);
331 : }
332 2 : lock.Notify();
333 : }
334 :
335 : // Dispatch whatever messages the previous listener had queued up.
336 2 : while (!pending.empty()) {
337 0 : OnMessageReceived(Move(pending.front()));
338 0 : pending.pop();
339 : }
340 2 : }
341 :
342 : void
343 23 : ProcessLink::OnChannelConnected(int32_t peer_pid)
344 : {
345 23 : AssertIOThread();
346 :
347 23 : bool notifyChannel = false;
348 :
349 : {
350 46 : MonitorAutoLock lock(*mChan->mMonitor);
351 : // Only update channel state if its still thinks its opening. Do not
352 : // force it into connected if it has errored out, started closing, etc.
353 23 : if (mChan->mChannelState == ChannelOpening) {
354 21 : mChan->mChannelState = ChannelConnected;
355 21 : mChan->mMonitor->Notify();
356 21 : notifyChannel = true;
357 : }
358 : }
359 :
360 23 : if (mExistingListener)
361 4 : mExistingListener->OnChannelConnected(peer_pid);
362 :
363 23 : if (notifyChannel) {
364 21 : mChan->OnChannelConnected(peer_pid);
365 : }
366 23 : }
367 :
368 : void
369 0 : ProcessLink::OnChannelConnectError()
370 : {
371 0 : AssertIOThread();
372 :
373 0 : MonitorAutoLock lock(*mChan->mMonitor);
374 :
375 0 : mChan->OnChannelErrorFromLink();
376 0 : }
377 :
378 : void
379 0 : ProcessLink::OnChannelError()
380 : {
381 0 : AssertIOThread();
382 :
383 0 : MonitorAutoLock lock(*mChan->mMonitor);
384 :
385 0 : MOZ_ALWAYS_TRUE(this == mTransport->set_listener(mExistingListener));
386 :
387 0 : mChan->OnChannelErrorFromLink();
388 0 : }
389 :
390 : void
391 0 : ProcessLink::OnCloseChannel()
392 : {
393 0 : AssertIOThread();
394 :
395 0 : mTransport->Close();
396 :
397 0 : MonitorAutoLock lock(*mChan->mMonitor);
398 :
399 : DebugOnly<IPC::Channel::Listener*> previousListener =
400 0 : mTransport->set_listener(mExistingListener);
401 :
402 : // OnChannelError may have reset the listener already.
403 0 : MOZ_ASSERT(previousListener == this ||
404 : previousListener == mExistingListener);
405 :
406 0 : mChan->mChannelState = ChannelClosed;
407 0 : mChan->mMonitor->Notify();
408 0 : }
409 :
410 : bool
411 0 : ProcessLink::Unsound_IsClosed() const
412 : {
413 0 : return mTransport->Unsound_IsClosed();
414 : }
415 :
416 : uint32_t
417 0 : ProcessLink::Unsound_NumQueuedMessages() const
418 : {
419 0 : return mTransport->Unsound_NumQueuedMessages();
420 : }
421 :
422 : } // namespace ipc
423 : } // namespace mozilla
|