[osx] Whitelist NSException thrown by Cocoa on Yosemite.
[chromium-blink-merge.git] / ipc / ipc_channel_win.cc
blob27043730695602dcf0f9cf4fd7d09806d9ce15aa
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "ipc/ipc_channel_win.h"
7 #include <windows.h>
9 #include "base/auto_reset.h"
10 #include "base/bind.h"
11 #include "base/compiler_specific.h"
12 #include "base/logging.h"
13 #include "base/pickle.h"
14 #include "base/process/process_handle.h"
15 #include "base/rand_util.h"
16 #include "base/strings/string_number_conversions.h"
17 #include "base/strings/utf_string_conversions.h"
18 #include "base/threading/thread_checker.h"
19 #include "base/win/scoped_handle.h"
20 #include "ipc/ipc_listener.h"
21 #include "ipc/ipc_logging.h"
22 #include "ipc/ipc_message_utils.h"
24 namespace IPC {
26 ChannelWin::State::State(ChannelWin* channel) : is_pending(false) {
27 memset(&context.overlapped, 0, sizeof(context.overlapped));
28 context.handler = channel;
31 ChannelWin::State::~State() {
32 static_assert(offsetof(ChannelWin::State, context) == 0,
33 "ChannelWin::State should have context as its first data"
34 "member.");
37 ChannelWin::ChannelWin(const IPC::ChannelHandle &channel_handle,
38 Mode mode, Listener* listener)
39 : ChannelReader(listener),
40 input_state_(this),
41 output_state_(this),
42 peer_pid_(base::kNullProcessId),
43 waiting_connect_(mode & MODE_SERVER_FLAG),
44 processing_incoming_(false),
45 validate_client_(false),
46 client_secret_(0),
47 weak_factory_(this) {
48 CreatePipe(channel_handle, mode);
51 ChannelWin::~ChannelWin() {
52 Close();
55 void ChannelWin::Close() {
56 if (thread_check_.get())
57 DCHECK(thread_check_->CalledOnValidThread());
59 if (input_state_.is_pending || output_state_.is_pending)
60 CancelIo(pipe_.Get());
62 // Closing the handle at this point prevents us from issuing more requests
63 // form OnIOCompleted().
64 if (pipe_.IsValid())
65 pipe_.Close();
67 // Make sure all IO has completed.
68 base::Time start = base::Time::Now();
69 while (input_state_.is_pending || output_state_.is_pending) {
70 base::MessageLoopForIO::current()->WaitForIOCompletion(INFINITE, this);
73 while (!output_queue_.empty()) {
74 Message* m = output_queue_.front();
75 output_queue_.pop();
76 delete m;
80 bool ChannelWin::Send(Message* message) {
81 DCHECK(!message->HasAttachments());
82 DCHECK(thread_check_->CalledOnValidThread());
83 DVLOG(2) << "sending message @" << message << " on channel @" << this
84 << " with type " << message->type()
85 << " (" << output_queue_.size() << " in queue)";
87 #ifdef IPC_MESSAGE_LOG_ENABLED
88 Logging::GetInstance()->OnSendMessage(message, "");
89 #endif
91 message->TraceMessageBegin();
92 output_queue_.push(message);
93 // ensure waiting to write
94 if (!waiting_connect_) {
95 if (!output_state_.is_pending) {
96 if (!ProcessOutgoingMessages(NULL, 0))
97 return false;
101 return true;
104 base::ProcessId ChannelWin::GetPeerPID() const {
105 return peer_pid_;
108 base::ProcessId ChannelWin::GetSelfPID() const {
109 return GetCurrentProcessId();
112 // static
113 bool ChannelWin::IsNamedServerInitialized(
114 const std::string& channel_id) {
115 if (WaitNamedPipe(PipeName(channel_id, NULL).c_str(), 1))
116 return true;
117 // If ERROR_SEM_TIMEOUT occurred, the pipe exists but is handling another
118 // connection.
119 return GetLastError() == ERROR_SEM_TIMEOUT;
122 ChannelWin::ReadState ChannelWin::ReadData(
123 char* buffer,
124 int buffer_len,
125 int* /* bytes_read */) {
126 if (!pipe_.IsValid())
127 return READ_FAILED;
129 DWORD bytes_read = 0;
130 BOOL ok = ReadFile(pipe_.Get(), buffer, buffer_len,
131 &bytes_read, &input_state_.context.overlapped);
132 if (!ok) {
133 DWORD err = GetLastError();
134 if (err == ERROR_IO_PENDING) {
135 input_state_.is_pending = true;
136 return READ_PENDING;
138 LOG(ERROR) << "pipe error: " << err;
139 return READ_FAILED;
142 // We could return READ_SUCCEEDED here. But the way that this code is
143 // structured we instead go back to the message loop. Our completion port
144 // will be signalled even in the "synchronously completed" state.
146 // This allows us to potentially process some outgoing messages and
147 // interleave other work on this thread when we're getting hammered with
148 // input messages. Potentially, this could be tuned to be more efficient
149 // with some testing.
150 input_state_.is_pending = true;
151 return READ_PENDING;
154 bool ChannelWin::WillDispatchInputMessage(Message* msg) {
155 // Make sure we get a hello when client validation is required.
156 if (validate_client_)
157 return IsHelloMessage(*msg);
158 return true;
161 void ChannelWin::HandleInternalMessage(const Message& msg) {
162 DCHECK_EQ(msg.type(), static_cast<unsigned>(Channel::HELLO_MESSAGE_TYPE));
163 // The hello message contains one parameter containing the PID.
164 PickleIterator it(msg);
165 int32 claimed_pid;
166 bool failed = !it.ReadInt(&claimed_pid);
168 if (!failed && validate_client_) {
169 int32 secret;
170 failed = it.ReadInt(&secret) ? (secret != client_secret_) : true;
173 if (failed) {
174 NOTREACHED();
175 Close();
176 listener()->OnChannelError();
177 return;
180 peer_pid_ = claimed_pid;
181 // Validation completed.
182 validate_client_ = false;
183 listener()->OnChannelConnected(claimed_pid);
186 bool ChannelWin::DidEmptyInputBuffers() {
187 // We don't need to do anything here.
188 return true;
191 // static
192 const base::string16 ChannelWin::PipeName(
193 const std::string& channel_id, int32* secret) {
194 std::string name("\\\\.\\pipe\\chrome.");
196 // Prevent the shared secret from ending up in the pipe name.
197 size_t index = channel_id.find_first_of('\\');
198 if (index != std::string::npos) {
199 if (secret) // Retrieve the secret if asked for.
200 base::StringToInt(channel_id.substr(index + 1), secret);
201 return base::ASCIIToUTF16(name.append(channel_id.substr(0, index - 1)));
204 // This case is here to support predictable named pipes in tests.
205 if (secret)
206 *secret = 0;
207 return base::ASCIIToUTF16(name.append(channel_id));
210 bool ChannelWin::CreatePipe(const IPC::ChannelHandle &channel_handle,
211 Mode mode) {
212 DCHECK(!pipe_.IsValid());
213 base::string16 pipe_name;
214 // If we already have a valid pipe for channel just copy it.
215 if (channel_handle.pipe.handle) {
216 // TODO(rvargas) crbug.com/415294: ChannelHandle should either go away in
217 // favor of two independent entities (name/file), or it should be a move-
218 // only type with a base::File member. In any case, this code should not
219 // call DuplicateHandle.
220 DCHECK(channel_handle.name.empty());
221 pipe_name = L"Not Available"; // Just used for LOG
222 // Check that the given pipe confirms to the specified mode. We can
223 // only check for PIPE_TYPE_MESSAGE & PIPE_SERVER_END flags since the
224 // other flags (PIPE_TYPE_BYTE, and PIPE_CLIENT_END) are defined as 0.
225 DWORD flags = 0;
226 GetNamedPipeInfo(channel_handle.pipe.handle, &flags, NULL, NULL, NULL);
227 DCHECK(!(flags & PIPE_TYPE_MESSAGE));
228 if (((mode & MODE_SERVER_FLAG) && !(flags & PIPE_SERVER_END)) ||
229 ((mode & MODE_CLIENT_FLAG) && (flags & PIPE_SERVER_END))) {
230 LOG(WARNING) << "Inconsistent open mode. Mode :" << mode;
231 return false;
233 HANDLE local_handle;
234 if (!DuplicateHandle(GetCurrentProcess(),
235 channel_handle.pipe.handle,
236 GetCurrentProcess(),
237 &local_handle,
239 FALSE,
240 DUPLICATE_SAME_ACCESS)) {
241 LOG(WARNING) << "DuplicateHandle failed. Error :" << GetLastError();
242 return false;
244 pipe_.Set(local_handle);
245 } else if (mode & MODE_SERVER_FLAG) {
246 DCHECK(!channel_handle.pipe.handle);
247 const DWORD open_mode = PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED |
248 FILE_FLAG_FIRST_PIPE_INSTANCE;
249 pipe_name = PipeName(channel_handle.name, &client_secret_);
250 validate_client_ = !!client_secret_;
251 pipe_.Set(CreateNamedPipeW(pipe_name.c_str(),
252 open_mode,
253 PIPE_TYPE_BYTE | PIPE_READMODE_BYTE,
255 Channel::kReadBufferSize,
256 Channel::kReadBufferSize,
257 5000,
258 NULL));
259 } else if (mode & MODE_CLIENT_FLAG) {
260 DCHECK(!channel_handle.pipe.handle);
261 pipe_name = PipeName(channel_handle.name, &client_secret_);
262 pipe_.Set(CreateFileW(pipe_name.c_str(),
263 GENERIC_READ | GENERIC_WRITE,
265 NULL,
266 OPEN_EXISTING,
267 SECURITY_SQOS_PRESENT | SECURITY_ANONYMOUS |
268 FILE_FLAG_OVERLAPPED,
269 NULL));
270 } else {
271 NOTREACHED();
274 if (!pipe_.IsValid()) {
275 // If this process is being closed, the pipe may be gone already.
276 PLOG(WARNING) << "Unable to create pipe \"" << pipe_name << "\" in "
277 << (mode & MODE_SERVER_FLAG ? "server" : "client") << " mode";
278 return false;
281 // Create the Hello message to be sent when Connect is called
282 scoped_ptr<Message> m(new Message(MSG_ROUTING_NONE,
283 HELLO_MESSAGE_TYPE,
284 IPC::Message::PRIORITY_NORMAL));
286 // Don't send the secret to the untrusted process, and don't send a secret
287 // if the value is zero (for IPC backwards compatability).
288 int32 secret = validate_client_ ? 0 : client_secret_;
289 if (!m->WriteInt(GetCurrentProcessId()) ||
290 (secret && !m->WriteUInt32(secret))) {
291 pipe_.Close();
292 return false;
295 output_queue_.push(m.release());
296 return true;
299 bool ChannelWin::Connect() {
300 DLOG_IF(WARNING, thread_check_.get()) << "Connect called more than once";
302 if (!thread_check_.get())
303 thread_check_.reset(new base::ThreadChecker());
305 if (!pipe_.IsValid())
306 return false;
308 base::MessageLoopForIO::current()->RegisterIOHandler(pipe_.Get(), this);
310 // Check to see if there is a client connected to our pipe...
311 if (waiting_connect_)
312 ProcessConnection();
314 if (!input_state_.is_pending) {
315 // Complete setup asynchronously. By not setting input_state_.is_pending
316 // to true, we indicate to OnIOCompleted that this is the special
317 // initialization signal.
318 base::MessageLoopForIO::current()->PostTask(
319 FROM_HERE,
320 base::Bind(&ChannelWin::OnIOCompleted,
321 weak_factory_.GetWeakPtr(),
322 &input_state_.context,
324 0));
327 if (!waiting_connect_)
328 ProcessOutgoingMessages(NULL, 0);
329 return true;
332 bool ChannelWin::ProcessConnection() {
333 DCHECK(thread_check_->CalledOnValidThread());
334 if (input_state_.is_pending)
335 input_state_.is_pending = false;
337 // Do we have a client connected to our pipe?
338 if (!pipe_.IsValid())
339 return false;
341 BOOL ok = ConnectNamedPipe(pipe_.Get(), &input_state_.context.overlapped);
342 DWORD err = GetLastError();
343 if (ok) {
344 // Uhm, the API documentation says that this function should never
345 // return success when used in overlapped mode.
346 NOTREACHED();
347 return false;
350 switch (err) {
351 case ERROR_IO_PENDING:
352 input_state_.is_pending = true;
353 break;
354 case ERROR_PIPE_CONNECTED:
355 waiting_connect_ = false;
356 break;
357 case ERROR_NO_DATA:
358 // The pipe is being closed.
359 return false;
360 default:
361 NOTREACHED();
362 return false;
365 return true;
368 bool ChannelWin::ProcessOutgoingMessages(
369 base::MessageLoopForIO::IOContext* context,
370 DWORD bytes_written) {
371 DCHECK(!waiting_connect_); // Why are we trying to send messages if there's
372 // no connection?
373 DCHECK(thread_check_->CalledOnValidThread());
375 if (output_state_.is_pending) {
376 DCHECK(context);
377 output_state_.is_pending = false;
378 if (!context || bytes_written == 0) {
379 DWORD err = GetLastError();
380 LOG(ERROR) << "pipe error: " << err;
381 return false;
383 // Message was sent.
384 CHECK(!output_queue_.empty());
385 Message* m = output_queue_.front();
386 output_queue_.pop();
387 delete m;
390 if (output_queue_.empty())
391 return true;
393 if (!pipe_.IsValid())
394 return false;
396 // Write to pipe...
397 Message* m = output_queue_.front();
398 DCHECK(m->size() <= INT_MAX);
399 BOOL ok = WriteFile(pipe_.Get(),
400 m->data(),
401 static_cast<uint32>(m->size()),
402 NULL,
403 &output_state_.context.overlapped);
404 if (!ok) {
405 DWORD write_error = GetLastError();
406 if (write_error == ERROR_IO_PENDING) {
407 output_state_.is_pending = true;
409 DVLOG(2) << "sent pending message @" << m << " on channel @" << this
410 << " with type " << m->type();
412 return true;
414 LOG(ERROR) << "pipe error: " << write_error;
415 return false;
418 DVLOG(2) << "sent message @" << m << " on channel @" << this
419 << " with type " << m->type();
421 output_state_.is_pending = true;
422 return true;
425 void ChannelWin::OnIOCompleted(
426 base::MessageLoopForIO::IOContext* context,
427 DWORD bytes_transfered,
428 DWORD error) {
429 bool ok = true;
430 DCHECK(thread_check_->CalledOnValidThread());
431 if (context == &input_state_.context) {
432 if (waiting_connect_) {
433 if (!ProcessConnection())
434 return;
435 // We may have some messages queued up to send...
436 if (!output_queue_.empty() && !output_state_.is_pending)
437 ProcessOutgoingMessages(NULL, 0);
438 if (input_state_.is_pending)
439 return;
440 // else, fall-through and look for incoming messages...
443 // We don't support recursion through OnMessageReceived yet!
444 DCHECK(!processing_incoming_);
445 base::AutoReset<bool> auto_reset_processing_incoming(
446 &processing_incoming_, true);
448 // Process the new data.
449 if (input_state_.is_pending) {
450 // This is the normal case for everything except the initialization step.
451 input_state_.is_pending = false;
452 if (!bytes_transfered)
453 ok = false;
454 else if (pipe_.IsValid())
455 ok = AsyncReadComplete(bytes_transfered);
456 } else {
457 DCHECK(!bytes_transfered);
460 // Request more data.
461 if (ok)
462 ok = ProcessIncomingMessages();
463 } else {
464 DCHECK(context == &output_state_.context);
465 CHECK(output_state_.is_pending);
466 ok = ProcessOutgoingMessages(context, bytes_transfered);
468 if (!ok && pipe_.IsValid()) {
469 // We don't want to re-enter Close().
470 Close();
471 listener()->OnChannelError();
475 //------------------------------------------------------------------------------
476 // Channel's methods
478 // static
479 scoped_ptr<Channel> Channel::Create(
480 const IPC::ChannelHandle &channel_handle, Mode mode, Listener* listener) {
481 return scoped_ptr<Channel>(
482 new ChannelWin(channel_handle, mode, listener));
485 // static
486 bool Channel::IsNamedServerInitialized(const std::string& channel_id) {
487 return ChannelWin::IsNamedServerInitialized(channel_id);
490 // static
491 std::string Channel::GenerateVerifiedChannelID(const std::string& prefix) {
492 // Windows pipes can be enumerated by low-privileged processes. So, we
493 // append a strong random value after the \ character. This value is not
494 // included in the pipe name, but sent as part of the client hello, to
495 // hijacking the pipe name to spoof the client.
497 std::string id = prefix;
498 if (!id.empty())
499 id.append(".");
501 int secret;
502 do { // Guarantee we get a non-zero value.
503 secret = base::RandInt(0, std::numeric_limits<int>::max());
504 } while (secret == 0);
506 id.append(GenerateUniqueRandomChannelID());
507 return id.append(base::StringPrintf("\\%d", secret));
510 } // namespace IPC