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