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 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
4 // Use of this source code is governed by a BSD-style license that can be
5 // found in the LICENSE file.
6
7 #include "chrome/common/ipc_channel_win.h"
8
9 #include <windows.h>
10 #include <sstream>
11
12 #include "base/command_line.h"
13 #include "base/compiler_specific.h"
14 #include "base/logging.h"
15 #include "base/process_util.h"
16 #include "base/rand_util.h"
17 #include "base/string_util.h"
18 #include "base/win_util.h"
19 #include "chrome/common/chrome_switches.h"
20 #include "chrome/common/ipc_channel_utils.h"
21 #include "chrome/common/ipc_message_utils.h"
22 #include "mozilla/ipc/ProtocolUtils.h"
23 #include "mozilla/Atomics.h"
24 #include "mozilla/LateWriteChecks.h"
25 #include "nsThreadUtils.h"
26
27 #ifdef FUZZING
28 # include "mozilla/ipc/Faulty.h"
29 #endif
30
31 using namespace mozilla::ipc;
32
33 // ChannelImpl is used on the IPC thread, but constructed on a different thread,
34 // so it has to hold the nsAutoOwningThread as a pointer, and we need a slightly
35 // different macro.
36 #ifdef DEBUG
37 # define ASSERT_OWNINGTHREAD(_class) \
38 if (nsAutoOwningThread* owningThread = _mOwningThread.get()) { \
39 owningThread->AssertOwnership(#_class " not thread-safe"); \
40 }
41 #else
42 # define ASSERT_OWNINGTHREAD(_class) ((void)0)
43 #endif
44
45 namespace IPC {
46 //------------------------------------------------------------------------------
47
State(ChannelImpl * channel)48 Channel::ChannelImpl::State::State(ChannelImpl* channel) : is_pending(false) {
49 memset(&context.overlapped, 0, sizeof(context.overlapped));
50 context.handler = channel;
51 }
52
~State()53 Channel::ChannelImpl::State::~State() {
54 COMPILE_ASSERT(!offsetof(Channel::ChannelImpl::State, context),
55 starts_with_io_context);
56 }
57
58 //------------------------------------------------------------------------------
59
ChannelImpl(const ChannelId & channel_id,Mode mode,Listener * listener)60 Channel::ChannelImpl::ChannelImpl(const ChannelId& channel_id, Mode mode,
61 Listener* listener)
62 : ALLOW_THIS_IN_INITIALIZER_LIST(input_state_(this)),
63 ALLOW_THIS_IN_INITIALIZER_LIST(output_state_(this)),
64 ALLOW_THIS_IN_INITIALIZER_LIST(factory_(this)),
65 shared_secret_(0),
66 waiting_for_shared_secret_(false) {
67 Init(mode, listener);
68
69 if (!CreatePipe(channel_id, mode)) {
70 // The pipe may have been closed already.
71 CHROMIUM_LOG(WARNING) << "Unable to create pipe named \"" << channel_id
72 << "\" in " << (mode == 0 ? "server" : "client")
73 << " mode.";
74 }
75 }
76
ChannelImpl(const ChannelId & channel_id,HANDLE server_pipe,Mode mode,Listener * listener)77 Channel::ChannelImpl::ChannelImpl(const ChannelId& channel_id,
78 HANDLE server_pipe, Mode mode,
79 Listener* listener)
80 : ALLOW_THIS_IN_INITIALIZER_LIST(input_state_(this)),
81 ALLOW_THIS_IN_INITIALIZER_LIST(output_state_(this)),
82 ALLOW_THIS_IN_INITIALIZER_LIST(factory_(this)),
83 shared_secret_(0),
84 waiting_for_shared_secret_(false) {
85 Init(mode, listener);
86
87 if (mode == MODE_SERVER) {
88 // We don't need the pipe name because we've been passed a handle, but we do
89 // need to get the shared secret from the channel_id.
90 PipeName(channel_id, &shared_secret_);
91 waiting_for_shared_secret_ = !!shared_secret_;
92
93 // Use the existing handle that was dup'd to us
94 pipe_ = server_pipe;
95 EnqueueHelloMessage();
96 } else {
97 // Take the normal init path to connect to the server pipe
98 CreatePipe(channel_id, mode);
99 }
100 }
101
Init(Mode mode,Listener * listener)102 void Channel::ChannelImpl::Init(Mode mode, Listener* listener) {
103 // Verify that we fit in a "quantum-spaced" jemalloc bucket.
104 static_assert(sizeof(*this) <= 512, "Exceeded expected size class");
105
106 pipe_ = INVALID_HANDLE_VALUE;
107 listener_ = listener;
108 waiting_connect_ = (mode == MODE_SERVER);
109 processing_incoming_ = false;
110 closed_ = false;
111 output_queue_length_ = 0;
112 input_buf_offset_ = 0;
113 input_buf_ = mozilla::MakeUnique<char[]>(Channel::kReadBufferSize);
114 }
115
OutputQueuePush(mozilla::UniquePtr<Message> msg)116 void Channel::ChannelImpl::OutputQueuePush(mozilla::UniquePtr<Message> msg) {
117 mozilla::LogIPCMessage::LogDispatchWithPid(msg.get(), other_pid_);
118
119 output_queue_.Push(std::move(msg));
120 output_queue_length_++;
121 }
122
OutputQueuePop()123 void Channel::ChannelImpl::OutputQueuePop() {
124 mozilla::UniquePtr<Message> message = output_queue_.Pop();
125 output_queue_length_--;
126 }
127
GetServerPipeHandle() const128 HANDLE Channel::ChannelImpl::GetServerPipeHandle() const { return pipe_; }
129
Close()130 void Channel::ChannelImpl::Close() {
131 ASSERT_OWNINGTHREAD(ChannelImpl);
132
133 bool waited = false;
134 if (input_state_.is_pending || output_state_.is_pending) {
135 CancelIo(pipe_);
136 waited = true;
137 }
138
139 // Closing the handle at this point prevents us from issuing more requests
140 // form OnIOCompleted().
141 if (pipe_ != INVALID_HANDLE_VALUE) {
142 CloseHandle(pipe_);
143 pipe_ = INVALID_HANDLE_VALUE;
144 }
145
146 while (input_state_.is_pending || output_state_.is_pending) {
147 MessageLoopForIO::current()->WaitForIOCompletion(INFINITE, this);
148 }
149
150 while (!output_queue_.IsEmpty()) {
151 OutputQueuePop();
152 }
153
154 #ifdef DEBUG
155 _mOwningThread = nullptr;
156 #endif
157 closed_ = true;
158 }
159
Send(mozilla::UniquePtr<Message> message)160 bool Channel::ChannelImpl::Send(mozilla::UniquePtr<Message> message) {
161 ASSERT_OWNINGTHREAD(ChannelImpl);
162
163 #ifdef IPC_MESSAGE_DEBUG_EXTRA
164 DLOG(INFO) << "sending message @" << message.get() << " on channel @" << this
165 << " with type " << message->type() << " ("
166 << output_queue_.Count() << " in queue)";
167 #endif
168
169 #ifdef FUZZING
170 message = mozilla::ipc::Faulty::instance().MutateIPCMessage(
171 "Channel::ChannelImpl::Send", std::move(message));
172 #endif
173
174 if (closed_) {
175 if (mozilla::ipc::LoggingEnabled()) {
176 fprintf(stderr,
177 "Can't send message %s, because this channel is closed.\n",
178 message->name());
179 }
180 return false;
181 }
182
183 OutputQueuePush(std::move(message));
184 // ensure waiting to write
185 if (!waiting_connect_) {
186 if (!output_state_.is_pending) {
187 if (!ProcessOutgoingMessages(NULL, 0)) return false;
188 }
189 }
190
191 return true;
192 }
193
PipeName(const ChannelId & channel_id,int32_t * secret) const194 const Channel::ChannelId Channel::ChannelImpl::PipeName(
195 const ChannelId& channel_id, int32_t* secret) const {
196 MOZ_ASSERT(secret);
197
198 std::wostringstream ss;
199 ss << L"\\\\.\\pipe\\chrome.";
200
201 // Prevent the shared secret from ending up in the pipe name.
202 size_t index = channel_id.find_first_of(L'\\');
203 if (index != std::string::npos) {
204 StringToInt(channel_id.substr(index + 1), secret);
205 ss << channel_id.substr(0, index - 1);
206 } else {
207 // This case is here to support predictable named pipes in tests.
208 *secret = 0;
209 ss << channel_id;
210 }
211 return ss.str();
212 }
213
CreatePipe(const ChannelId & channel_id,Mode mode)214 bool Channel::ChannelImpl::CreatePipe(const ChannelId& channel_id, Mode mode) {
215 DCHECK(pipe_ == INVALID_HANDLE_VALUE);
216 const ChannelId pipe_name = PipeName(channel_id, &shared_secret_);
217 if (mode == MODE_SERVER) {
218 waiting_for_shared_secret_ = !!shared_secret_;
219 pipe_ = CreateNamedPipeW(pipe_name.c_str(),
220 PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED |
221 FILE_FLAG_FIRST_PIPE_INSTANCE,
222 PIPE_TYPE_BYTE | PIPE_READMODE_BYTE,
223 1, // number of pipe instances
224 // output buffer size (XXX tune)
225 Channel::kReadBufferSize,
226 // input buffer size (XXX tune)
227 Channel::kReadBufferSize,
228 5000, // timeout in milliseconds (XXX tune)
229 NULL);
230 } else {
231 pipe_ = CreateFileW(
232 pipe_name.c_str(), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING,
233 SECURITY_SQOS_PRESENT | SECURITY_IDENTIFICATION | FILE_FLAG_OVERLAPPED,
234 NULL);
235 }
236 if (pipe_ == INVALID_HANDLE_VALUE) {
237 // If this process is being closed, the pipe may be gone already.
238 CHROMIUM_LOG(WARNING) << "failed to create pipe: " << GetLastError();
239 closed_ = true;
240 return false;
241 }
242
243 // Create the Hello message to be sent when Connect is called
244 return EnqueueHelloMessage();
245 }
246
EnqueueHelloMessage()247 bool Channel::ChannelImpl::EnqueueHelloMessage() {
248 auto m = mozilla::MakeUnique<Message>(MSG_ROUTING_NONE, HELLO_MESSAGE_TYPE);
249
250 // If we're waiting for our shared secret from the other end's hello message
251 // then don't give the game away by sending it in ours.
252 int32_t secret = waiting_for_shared_secret_ ? 0 : shared_secret_;
253
254 // Also, don't send if the value is zero (for IPC backwards compatability).
255 if (!m->WriteInt(GetCurrentProcessId()) ||
256 (secret && !m->WriteUInt32(secret))) {
257 CloseHandle(pipe_);
258 pipe_ = INVALID_HANDLE_VALUE;
259 return false;
260 }
261
262 OutputQueuePush(std::move(m));
263 return true;
264 }
265
Connect()266 bool Channel::ChannelImpl::Connect() {
267 #ifdef DEBUG
268 if (!_mOwningThread) {
269 _mOwningThread = mozilla::MakeUnique<nsAutoOwningThread>();
270 }
271 #endif
272
273 if (pipe_ == INVALID_HANDLE_VALUE) return false;
274
275 MessageLoopForIO::current()->RegisterIOHandler(pipe_, this);
276
277 // Check to see if there is a client connected to our pipe...
278 if (waiting_connect_) {
279 if (!ProcessConnection()) {
280 return false;
281 }
282 }
283
284 if (!input_state_.is_pending) {
285 // Complete setup asynchronously. By not setting input_state_.is_pending
286 // to true, we indicate to OnIOCompleted that this is the special
287 // initialization signal.
288 MessageLoopForIO::current()->PostTask(factory_.NewRunnableMethod(
289 &Channel::ChannelImpl::OnIOCompleted, &input_state_.context, 0, 0));
290 }
291
292 if (!waiting_connect_) ProcessOutgoingMessages(NULL, 0);
293 return true;
294 }
295
ProcessConnection()296 bool Channel::ChannelImpl::ProcessConnection() {
297 ASSERT_OWNINGTHREAD(ChannelImpl);
298 if (input_state_.is_pending) input_state_.is_pending = false;
299
300 // Do we have a client connected to our pipe?
301 if (INVALID_HANDLE_VALUE == pipe_) return false;
302
303 BOOL ok = ConnectNamedPipe(pipe_, &input_state_.context.overlapped);
304
305 DWORD err = GetLastError();
306 if (ok) {
307 // Uhm, the API documentation says that this function should never
308 // return success when used in overlapped mode.
309 NOTREACHED();
310 return false;
311 }
312
313 switch (err) {
314 case ERROR_IO_PENDING:
315 input_state_.is_pending = true;
316 break;
317 case ERROR_PIPE_CONNECTED:
318 waiting_connect_ = false;
319 break;
320 case ERROR_NO_DATA:
321 // The pipe is being closed.
322 return false;
323 default:
324 NOTREACHED();
325 return false;
326 }
327
328 return true;
329 }
330
ProcessIncomingMessages(MessageLoopForIO::IOContext * context,DWORD bytes_read)331 bool Channel::ChannelImpl::ProcessIncomingMessages(
332 MessageLoopForIO::IOContext* context, DWORD bytes_read) {
333 ASSERT_OWNINGTHREAD(ChannelImpl);
334 if (input_state_.is_pending) {
335 input_state_.is_pending = false;
336 DCHECK(context);
337
338 if (!context || !bytes_read) return false;
339 } else {
340 // This happens at channel initialization.
341 DCHECK(!bytes_read && context == &input_state_.context);
342 }
343
344 for (;;) {
345 if (bytes_read == 0) {
346 if (INVALID_HANDLE_VALUE == pipe_) return false;
347
348 // Read from pipe...
349 BOOL ok = ReadFile(pipe_, input_buf_.get() + input_buf_offset_,
350 Channel::kReadBufferSize - input_buf_offset_,
351 &bytes_read, &input_state_.context.overlapped);
352 if (!ok) {
353 DWORD err = GetLastError();
354 if (err == ERROR_IO_PENDING) {
355 input_state_.is_pending = true;
356 return true;
357 }
358 if (err != ERROR_BROKEN_PIPE) {
359 CHROMIUM_LOG(ERROR) << "pipe error: " << err;
360 }
361 return false;
362 }
363 input_state_.is_pending = true;
364 return true;
365 }
366 DCHECK(bytes_read);
367
368 // Process messages from input buffer.
369
370 const char* p = input_buf_.get();
371 const char* end = input_buf_.get() + input_buf_offset_ + bytes_read;
372
373 while (p < end) {
374 // Try to figure out how big the message is. Size is 0 if we haven't read
375 // enough of the header to know the size.
376 uint32_t message_length = 0;
377 if (incoming_message_.isSome()) {
378 message_length = incoming_message_.ref().size();
379 } else {
380 message_length = Message::MessageSize(p, end);
381 }
382
383 if (!message_length) {
384 // We haven't seen the full message header.
385 MOZ_ASSERT(incoming_message_.isNothing());
386
387 // Move everything we have to the start of the buffer. We'll finish
388 // reading this message when we get more data. For now we leave it in
389 // input_buf_.
390 memmove(input_buf_.get(), p, end - p);
391 input_buf_offset_ = end - p;
392
393 break;
394 }
395
396 input_buf_offset_ = 0;
397
398 bool partial;
399 if (incoming_message_.isSome()) {
400 // We already have some data for this message stored in
401 // incoming_message_. We want to append the new data there.
402 Message& m = incoming_message_.ref();
403
404 // How much data from this message remains to be added to
405 // incoming_message_?
406 MOZ_ASSERT(message_length > m.CurrentSize());
407 uint32_t remaining = message_length - m.CurrentSize();
408
409 // How much data from this message is stored in input_buf_?
410 uint32_t in_buf = std::min(remaining, uint32_t(end - p));
411
412 m.InputBytes(p, in_buf);
413 p += in_buf;
414
415 // Are we done reading this message?
416 partial = in_buf != remaining;
417 } else {
418 // How much data from this message is stored in input_buf_?
419 uint32_t in_buf = std::min(message_length, uint32_t(end - p));
420
421 incoming_message_.emplace(p, in_buf);
422 p += in_buf;
423
424 // Are we done reading this message?
425 partial = in_buf != message_length;
426 }
427
428 if (partial) {
429 break;
430 }
431
432 Message& m = incoming_message_.ref();
433
434 // Note: We set other_pid_ below when we receive a Hello message (which
435 // has no routing ID), but we only emit a profiler marker for messages
436 // with a routing ID, so there's no conflict here.
437 AddIPCProfilerMarker(m, other_pid_, MessageDirection::eReceiving,
438 MessagePhase::TransferEnd);
439
440 #ifdef IPC_MESSAGE_DEBUG_EXTRA
441 DLOG(INFO) << "received message on channel @" << this << " with type "
442 << m.type();
443 #endif
444 if (m.routing_id() == MSG_ROUTING_NONE &&
445 m.type() == HELLO_MESSAGE_TYPE) {
446 // The Hello message contains the process id and must include the
447 // shared secret, if we are waiting for it.
448 MessageIterator it = MessageIterator(m);
449 other_pid_ = it.NextInt();
450 if (waiting_for_shared_secret_ && (it.NextInt() != shared_secret_)) {
451 NOTREACHED();
452 // Something went wrong. Abort connection.
453 Close();
454 listener_->OnChannelError();
455 return false;
456 }
457 waiting_for_shared_secret_ = false;
458 listener_->OnChannelConnected(other_pid_);
459 } else {
460 mozilla::LogIPCMessage::Run run(&m);
461 listener_->OnMessageReceived(std::move(m));
462 }
463
464 incoming_message_.reset();
465 }
466
467 bytes_read = 0; // Get more data.
468 }
469
470 return true;
471 }
472
ProcessOutgoingMessages(MessageLoopForIO::IOContext * context,DWORD bytes_written)473 bool Channel::ChannelImpl::ProcessOutgoingMessages(
474 MessageLoopForIO::IOContext* context, DWORD bytes_written) {
475 DCHECK(!waiting_connect_); // Why are we trying to send messages if there's
476 // no connection?
477 ASSERT_OWNINGTHREAD(ChannelImpl);
478
479 if (output_state_.is_pending) {
480 DCHECK(context);
481 output_state_.is_pending = false;
482 if (!context || bytes_written == 0) {
483 DWORD err = GetLastError();
484 if (err != ERROR_BROKEN_PIPE) {
485 CHROMIUM_LOG(ERROR) << "pipe error: " << err;
486 }
487 return false;
488 }
489 // Message was sent.
490 DCHECK(!output_queue_.IsEmpty());
491 Message* m = output_queue_.FirstElement().get();
492
493 MOZ_RELEASE_ASSERT(partial_write_iter_.isSome());
494 Pickle::BufferList::IterImpl& iter = partial_write_iter_.ref();
495 iter.Advance(m->Buffers(), bytes_written);
496 if (iter.Done()) {
497 AddIPCProfilerMarker(*m, other_pid_, MessageDirection::eSending,
498 MessagePhase::TransferEnd);
499
500 partial_write_iter_.reset();
501 OutputQueuePop();
502 // m has been destroyed, so clear the dangling reference.
503 m = nullptr;
504 }
505 }
506
507 if (output_queue_.IsEmpty()) return true;
508
509 if (INVALID_HANDLE_VALUE == pipe_) return false;
510
511 // Write to pipe...
512 Message* m = output_queue_.FirstElement().get();
513
514 if (partial_write_iter_.isNothing()) {
515 AddIPCProfilerMarker(*m, other_pid_, MessageDirection::eSending,
516 MessagePhase::TransferStart);
517 Pickle::BufferList::IterImpl iter(m->Buffers());
518 partial_write_iter_.emplace(iter);
519 }
520
521 Pickle::BufferList::IterImpl& iter = partial_write_iter_.ref();
522
523 // Don't count this write for the purposes of late write checking. If this
524 // message results in a legitimate file write, that will show up when it
525 // happens.
526 mozilla::PushSuspendLateWriteChecks();
527 BOOL ok = WriteFile(pipe_, iter.Data(), iter.RemainingInSegment(),
528 &bytes_written, &output_state_.context.overlapped);
529 mozilla::PopSuspendLateWriteChecks();
530
531 if (!ok) {
532 DWORD err = GetLastError();
533 if (err == ERROR_IO_PENDING) {
534 output_state_.is_pending = true;
535
536 #ifdef IPC_MESSAGE_DEBUG_EXTRA
537 DLOG(INFO) << "sent pending message @" << m << " on channel @" << this
538 << " with type " << m->type();
539 #endif
540
541 return true;
542 }
543 if (err != ERROR_BROKEN_PIPE) {
544 CHROMIUM_LOG(ERROR) << "pipe error: " << err;
545 }
546 return false;
547 }
548
549 #ifdef IPC_MESSAGE_DEBUG_EXTRA
550 DLOG(INFO) << "sent message @" << m << " on channel @" << this
551 << " with type " << m->type();
552 #endif
553
554 output_state_.is_pending = true;
555 return true;
556 }
557
OnIOCompleted(MessageLoopForIO::IOContext * context,DWORD bytes_transfered,DWORD error)558 void Channel::ChannelImpl::OnIOCompleted(MessageLoopForIO::IOContext* context,
559 DWORD bytes_transfered, DWORD error) {
560 bool ok;
561 ASSERT_OWNINGTHREAD(ChannelImpl);
562 if (context == &input_state_.context) {
563 if (waiting_connect_) {
564 if (!ProcessConnection()) return;
565 // We may have some messages queued up to send...
566 if (!output_queue_.IsEmpty() && !output_state_.is_pending)
567 ProcessOutgoingMessages(NULL, 0);
568 if (input_state_.is_pending) return;
569 // else, fall-through and look for incoming messages...
570 }
571 // we don't support recursion through OnMessageReceived yet!
572 DCHECK(!processing_incoming_);
573 processing_incoming_ = true;
574 ok = ProcessIncomingMessages(context, bytes_transfered);
575 processing_incoming_ = false;
576 } else {
577 DCHECK(context == &output_state_.context);
578 ok = ProcessOutgoingMessages(context, bytes_transfered);
579 }
580 if (!ok && INVALID_HANDLE_VALUE != pipe_) {
581 // We don't want to re-enter Close().
582 Close();
583 listener_->OnChannelError();
584 }
585 }
586
Unsound_IsClosed() const587 bool Channel::ChannelImpl::Unsound_IsClosed() const { return closed_; }
588
Unsound_NumQueuedMessages() const589 uint32_t Channel::ChannelImpl::Unsound_NumQueuedMessages() const {
590 return output_queue_length_;
591 }
592
593 //------------------------------------------------------------------------------
594 // Channel's methods simply call through to ChannelImpl.
Channel(const ChannelId & channel_id,Mode mode,Listener * listener)595 Channel::Channel(const ChannelId& channel_id, Mode mode, Listener* listener)
596 : channel_impl_(new ChannelImpl(channel_id, mode, listener)) {
597 MOZ_COUNT_CTOR(IPC::Channel);
598 }
599
Channel(const ChannelId & channel_id,void * server_pipe,Mode mode,Listener * listener)600 Channel::Channel(const ChannelId& channel_id, void* server_pipe, Mode mode,
601 Listener* listener)
602 : channel_impl_(new ChannelImpl(channel_id, server_pipe, mode, listener)) {
603 MOZ_COUNT_CTOR(IPC::Channel);
604 }
605
~Channel()606 Channel::~Channel() {
607 MOZ_COUNT_DTOR(IPC::Channel);
608 delete channel_impl_;
609 }
610
Connect()611 bool Channel::Connect() { return channel_impl_->Connect(); }
612
Close()613 void Channel::Close() { channel_impl_->Close(); }
614
GetServerPipeHandle() const615 void* Channel::GetServerPipeHandle() const {
616 return channel_impl_->GetServerPipeHandle();
617 }
618
set_listener(Listener * listener)619 Channel::Listener* Channel::set_listener(Listener* listener) {
620 return channel_impl_->set_listener(listener);
621 }
622
Send(mozilla::UniquePtr<Message> message)623 bool Channel::Send(mozilla::UniquePtr<Message> message) {
624 return channel_impl_->Send(std::move(message));
625 }
626
OtherPid() const627 int32_t Channel::OtherPid() const { return channel_impl_->OtherPid(); }
628
Unsound_IsClosed() const629 bool Channel::Unsound_IsClosed() const {
630 return channel_impl_->Unsound_IsClosed();
631 }
632
Unsound_NumQueuedMessages() const633 uint32_t Channel::Unsound_NumQueuedMessages() const {
634 return channel_impl_->Unsound_NumQueuedMessages();
635 }
636
637 namespace {
638
639 // Global atomic used to guarantee channel IDs are unique.
640 mozilla::Atomic<int> g_last_id;
641
642 } // namespace
643
644 // static
GenerateVerifiedChannelID()645 Channel::ChannelId Channel::GenerateVerifiedChannelID() {
646 // Windows pipes can be enumerated by low-privileged processes. So, we
647 // append a strong random value after the \ character. This value is not
648 // included in the pipe name, but sent as part of the client hello, to
649 // prevent hijacking the pipe name to spoof the client.
650 int secret;
651 do { // Guarantee we get a non-zero value.
652 secret = base::RandInt(0, std::numeric_limits<int>::max());
653 } while (secret == 0);
654 return StringPrintf(L"%d.%u.%d\\%d", base::GetCurrentProcId(), g_last_id++,
655 base::RandInt(0, std::numeric_limits<int32_t>::max()),
656 secret);
657 }
658
659 // static
ChannelIDForCurrentProcess()660 Channel::ChannelId Channel::ChannelIDForCurrentProcess() {
661 return CommandLine::ForCurrentProcess()->GetSwitchValue(
662 switches::kProcessChannelID);
663 }
664
665 } // namespace IPC
666