diff options
Diffstat (limited to 'chromium/ipc')
53 files changed, 2008 insertions, 1012 deletions
diff --git a/chromium/ipc/BUILD.gn b/chromium/ipc/BUILD.gn new file mode 100644 index 00000000000..3bff55ee528 --- /dev/null +++ b/chromium/ipc/BUILD.gn @@ -0,0 +1,178 @@ +# Copyright (c) 2012 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +component("ipc") { + sources = [ + "file_descriptor_set_posix.cc", + "file_descriptor_set_posix.h", + "ipc_channel.cc", + "ipc_channel.h", + "ipc_channel_common.cc", + "ipc_channel_factory.cc", + "ipc_channel_factory.h", + "ipc_channel_handle.h", + "ipc_channel_nacl.cc", + "ipc_channel_nacl.h", + "ipc_channel_posix.cc", + "ipc_channel_posix.h", + "ipc_channel_proxy.cc", + "ipc_channel_proxy.h", + "ipc_channel_reader.cc", + "ipc_channel_reader.h", + "ipc_channel_win.cc", + "ipc_channel_win.h", + "ipc_descriptors.h", + "ipc_export.h", + "ipc_forwarding_message_filter.cc", + "ipc_forwarding_message_filter.h", + "ipc_listener.h", + "ipc_logging.cc", + "ipc_logging.h", + "ipc_message.cc", + "ipc_message.h", + "ipc_message_macros.h", + "ipc_message_start.h", + "ipc_message_utils.cc", + "ipc_message_utils.h", + "ipc_param_traits.h", + "ipc_platform_file.cc", + "ipc_platform_file.h", + "ipc_sender.h", + "ipc_switches.cc", + "ipc_switches.h", + "ipc_sync_channel.cc", + "ipc_sync_channel.h", + "ipc_sync_message.cc", + "ipc_sync_message.h", + "ipc_sync_message_filter.cc", + "ipc_sync_message_filter.h", + "message_filter.cc", + "message_filter.h", + "message_filter_router.cc", + "message_filter_router.h", + "param_traits_log_macros.h", + "param_traits_macros.h", + "param_traits_read_macros.h", + "param_traits_write_macros.h", + "struct_constructor_macros.h", + "struct_destructor_macros.h", + "unix_domain_socket_util.cc", + "unix_domain_socket_util.h", + ] + + if (!is_nacl) { + sources -= [ + "ipc_channel_nacl.cc", + "ipc_channel_nacl.h", + ] + } + + if (is_win || is_ios) { + sources -= [ + "ipc_channel_factory.cc", + "unix_domain_socket_util.cc", + ] + } + + defines = [ "IPC_IMPLEMENTATION" ] + + deps = [ + "//base", + # TODO(viettrungluu): Needed for base/lazy_instance.h, which is suspect. + "//base/third_party/dynamic_annotations", + ] +} + +# TODO(dpranke): crbug.com/360936. Get this to build and run on Android. +if (!is_android) { + test("ipc_tests") { + sources = [ + "file_descriptor_set_posix_unittest.cc", + "ipc_channel_posix_unittest.cc", + "ipc_channel_unittest.cc", + "ipc_fuzzing_tests.cc", + "ipc_message_unittest.cc", + "ipc_message_utils_unittest.cc", + "ipc_send_fds_test.cc", + "ipc_sync_channel_unittest.cc", + "ipc_sync_message_unittest.cc", + "ipc_sync_message_unittest.h", + "ipc_test_base.cc", + "ipc_test_base.h", + "sync_socket_unittest.cc", + "unix_domain_socket_util_unittest.cc", + ] + + if (is_win || is_ios) { + sources -= [ "unix_domain_socket_util_unittest.cc" ] + } + + # TODO(brettw) hook up Android testing. + #if (is_android && gtest_target_type == "shared_library") { + # deps += "/testing/android/native_test.gyp:native_testNative_code" + #} + + # TODO(brettw) hook up tcmalloc to this target. + #if (is_posix && !is_mac && !is_android) { + # if (use_allocator!="none") { + # deps += "/base/allocator" + # } + #} + + deps = [ + ":ipc", + ":test_support_ipc", + "//base", + "//base:i18n", + "//base/test:run_all_unittests", + "//base/test:test_support", + "//testing/gtest", + ] + } + + test("ipc_perftests") { + sources = [ + "ipc_perftests.cc", + "ipc_test_base.cc", + "ipc_test_base.h", + ] + + # TODO(brettw) hook up Android testing. + #if (is_android && gtest_target_type == "shared_library") { + # deps += "/testing/android/native_test.gyp:native_testNative_code" + #} + + # TODO(brettw) hook up tcmalloc to this target. + #if (is_posix && !is_mac && !is_android) { + # if (use_allocator!="none") { + # deps += "//base/allocator" + # } + #} + + deps = [ + ":ipc", + ":test_support_ipc", + "//base", + "//base:i18n", + "//base/test:test_support", + "//base/test:test_support_perf", + "//testing/gtest", + ] + } +} + +static_library("test_support_ipc") { + sources = [ + "ipc_multiprocess_test.cc", + "ipc_multiprocess_test.h", + "ipc_test_sink.cc", + "ipc_test_sink.h", + ] + deps = [ + ":ipc", + "//base", + "//testing/gtest", + ] +} + diff --git a/chromium/ipc/OWNERS b/chromium/ipc/OWNERS index 37a61fa0463..f929594d23e 100644 --- a/chromium/ipc/OWNERS +++ b/chromium/ipc/OWNERS @@ -11,7 +11,6 @@ dmichael@chromium.org # New IPC message files require a security review to avoid introducing # new sandbox escapes. per-file ipc_message_start.h=set noparent -per-file ipc_message_start.h=cdn@chromium.org per-file ipc_message_start.h=cevans@chromium.org per-file ipc_message_start.h=dcheng@chromium.org per-file ipc_message_start.h=jln@chromium.org diff --git a/chromium/ipc/ipc.gyp b/chromium/ipc/ipc.gyp index ace836b3a1b..a6e92691f11 100644 --- a/chromium/ipc/ipc.gyp +++ b/chromium/ipc/ipc.gyp @@ -46,6 +46,7 @@ 'sources': [ 'file_descriptor_set_posix_unittest.cc', 'ipc_channel_posix_unittest.cc', + 'ipc_channel_proxy_unittest.cc', 'ipc_channel_unittest.cc', 'ipc_fuzzing_tests.cc', 'ipc_message_unittest.cc', @@ -61,24 +62,19 @@ 'unix_domain_socket_util_unittest.cc', ], 'conditions': [ - ['toolkit_uses_gtk == 1', { - 'dependencies': [ - '../build/linux/system.gyp:gtk', - ], - }], ['OS == "win" or OS == "ios"', { 'sources!': [ 'unix_domain_socket_util_unittest.cc', ], }], - ['OS == "android" and gtest_target_type == "shared_library"', { + ['OS == "android"', { 'dependencies': [ '../testing/android/native_test.gyp:native_test_native_code', ], }], ['os_posix == 1 and OS != "mac" and OS != "android"', { 'conditions': [ - ['linux_use_tcmalloc==1', { + ['use_allocator!="none"', { 'dependencies': [ '../base/allocator/allocator.gyp:allocator', ], @@ -109,19 +105,14 @@ 'ipc_test_base.h', ], 'conditions': [ - ['toolkit_uses_gtk == 1', { - 'dependencies': [ - '../build/linux/system.gyp:gtk', - ], - }], - ['OS == "android" and gtest_target_type == "shared_library"', { + ['OS == "android"', { 'dependencies': [ '../testing/android/native_test.gyp:native_test_native_code', ], }], ['os_posix == 1 and OS != "mac" and OS != "android"', { 'conditions': [ - ['linux_use_tcmalloc==1', { + ['use_allocator!="none"', { 'dependencies': [ '../base/allocator/allocator.gyp:allocator', ], @@ -156,7 +147,7 @@ 'ipc_target': 1, }, 'dependencies': [ - '../base/base.gyp:base_nacl_win64', + '../base/base.gyp:base_win64', # TODO(viettrungluu): Needed for base/lazy_instance.h, which is # suspect. '../base/third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations_win64', @@ -176,10 +167,7 @@ }, ], }], - # Special target to wrap a gtest_target_type==shared_library - # ipc_tests into an android apk for execution. - # See base.gyp for TODO(jrg)s about this strategy. - ['OS == "android" and gtest_target_type == "shared_library"', { + ['OS == "android"', { 'targets': [ { 'target_name': 'ipc_tests_apk', @@ -189,7 +177,17 @@ ], 'variables': { 'test_suite_name': 'ipc_tests', - 'input_shlib_path': '<(SHARED_LIB_DIR)/<(SHARED_LIB_PREFIX)ipc_tests<(SHARED_LIB_SUFFIX)', + }, + 'includes': [ '../build/apk_test.gypi' ], + }, + { + 'target_name': 'ipc_perftests_apk', + 'type': 'none', + 'dependencies': [ + 'ipc_perftests', + ], + 'variables': { + 'test_suite_name': 'ipc_perftests', }, 'includes': [ '../build/apk_test.gypi' ], }], diff --git a/chromium/ipc/ipc.gypi b/chromium/ipc/ipc.gypi index ba80e421404..e7d5cc63f3b 100644 --- a/chromium/ipc/ipc.gypi +++ b/chromium/ipc/ipc.gypi @@ -15,6 +15,7 @@ 'file_descriptor_set_posix.h', 'ipc_channel.cc', 'ipc_channel.h', + 'ipc_channel_common.cc', 'ipc_channel_factory.cc', 'ipc_channel_factory.h', 'ipc_channel_handle.h', @@ -53,6 +54,10 @@ 'ipc_sync_message.h', 'ipc_sync_message_filter.cc', 'ipc_sync_message_filter.h', + 'message_filter.cc', + 'message_filter.h', + 'message_filter_router.cc', + 'message_filter_router.h', 'param_traits_log_macros.h', 'param_traits_macros.h', 'param_traits_read_macros.h', diff --git a/chromium/ipc/ipc_channel.h b/chromium/ipc/ipc_channel.h index 7e09a806f7a..bca3ea066c3 100644 --- a/chromium/ipc/ipc_channel.h +++ b/chromium/ipc/ipc_channel.h @@ -55,21 +55,15 @@ class IPC_EXPORT Channel : public Sender { }; // Some Standard Modes + // TODO(morrita): These are under deprecation work. You should use Create*() + // functions instead. enum Mode { MODE_NONE = MODE_NO_FLAG, MODE_SERVER = MODE_SERVER_FLAG, MODE_CLIENT = MODE_CLIENT_FLAG, - // Channels on Windows are named by default and accessible from other - // processes. On POSIX channels are anonymous by default and not accessible - // from other processes. Named channels work via named unix domain sockets. - // On Windows MODE_NAMED_SERVER is equivalent to MODE_SERVER and - // MODE_NAMED_CLIENT is equivalent to MODE_CLIENT. MODE_NAMED_SERVER = MODE_SERVER_FLAG | MODE_NAMED_FLAG, MODE_NAMED_CLIENT = MODE_CLIENT_FLAG | MODE_NAMED_FLAG, #if defined(OS_POSIX) - // An "open" named server accepts connections from ANY client. - // The caller must then implement their own access-control based on the - // client process' user Id. MODE_OPEN_NAMED_SERVER = MODE_OPEN_ACCESS_FLAG | MODE_SERVER_FLAG | MODE_NAMED_FLAG #endif @@ -106,15 +100,48 @@ class IPC_EXPORT Channel : public Sender { // the file descriptor in the channel handle is != -1, the channel takes // ownership of the file descriptor and will close it appropriately, otherwise // it will create a new descriptor internally. - // |mode| specifies whether this Channel is to operate in server mode or - // client mode. In server mode, the Channel is responsible for setting up the - // IPC object, whereas in client mode, the Channel merely connects to the - // already established IPC object. // |listener| receives a callback on the current thread for each newly // received message. // - Channel(const IPC::ChannelHandle &channel_handle, Mode mode, - Listener* listener); + // There are four type of modes how channels operate: + // + // - Server and named server: In these modes, the Channel is + // responsible for settingb up the IPC object + // - An "open" named server: It accepts connections from ANY client. + // The caller must then implement their own access-control based on the + // client process' user Id. + // - Client and named client: In these mode, the Channel merely + // connects to the already established IPC object. + // + // Each mode has its own Create*() API to create the Channel object. + // + // TODO(morrita): Replace CreateByModeForProxy() with one of above Create*(). + // + static scoped_ptr<Channel> Create( + const IPC::ChannelHandle &channel_handle, Mode mode,Listener* listener); + + static scoped_ptr<Channel> CreateClient( + const IPC::ChannelHandle &channel_handle, Listener* listener); + + // Channels on Windows are named by default and accessible from other + // processes. On POSIX channels are anonymous by default and not accessible + // from other processes. Named channels work via named unix domain sockets. + // On Windows MODE_NAMED_SERVER is equivalent to MODE_SERVER and + // MODE_NAMED_CLIENT is equivalent to MODE_CLIENT. + static scoped_ptr<Channel> CreateNamedServer( + const IPC::ChannelHandle &channel_handle, Listener* listener); + static scoped_ptr<Channel> CreateNamedClient( + const IPC::ChannelHandle &channel_handle, Listener* listener); +#if defined(OS_POSIX) + // An "open" named server accepts connections from ANY client. + // The caller must then implement their own access-control based on the + // client process' user Id. + static scoped_ptr<Channel> CreateOpenNamedServer( + const IPC::ChannelHandle &channel_handle, Listener* listener); +#endif + static scoped_ptr<Channel> CreateServer( + const IPC::ChannelHandle &channel_handle, Listener* listener); + virtual ~Channel(); @@ -123,14 +150,14 @@ class IPC_EXPORT Channel : public Sender { // connect to a pre-existing pipe. Note, calling Connect() // will not block the calling thread and may complete // asynchronously. - bool Connect() WARN_UNUSED_RESULT; + virtual bool Connect() WARN_UNUSED_RESULT = 0; // Close this Channel explicitly. May be called multiple times. // On POSIX calling close on an IPC channel that listens for connections will // cause it to close any accepted connections, and it will stop listening for // new connections. If you just want to close the currently accepted // connection and listen for new ones, use ResetToAcceptingConnectionState. - void Close(); + virtual void Close() = 0; // Get the process ID for the connected peer. // @@ -141,45 +168,25 @@ class IPC_EXPORT Channel : public Sender { // in response to a message from the remote side (which guarantees that it's // been connected), or you wait for the "connected" notification on the // listener. - base::ProcessId peer_pid() const; + virtual base::ProcessId GetPeerPID() const = 0; // Send a message over the Channel to the listener on the other end. // // |message| must be allocated using operator new. This object will be // deleted once the contents of the Message have been sent. - virtual bool Send(Message* message) OVERRIDE; + virtual bool Send(Message* message) = 0; -#if defined(OS_POSIX) +#if defined(OS_POSIX) && !defined(OS_NACL) // On POSIX an IPC::Channel wraps a socketpair(), this method returns the // FD # for the client end of the socket. // This method may only be called on the server side of a channel. // This method can be called on any thread. - int GetClientFileDescriptor() const; + virtual int GetClientFileDescriptor() const = 0; // Same as GetClientFileDescriptor, but transfers the ownership of the // file descriptor to the caller. // This method can be called on any thread. - int TakeClientFileDescriptor(); - - // On POSIX an IPC::Channel can either wrap an established socket, or it - // can wrap a socket that is listening for connections. Currently an - // IPC::Channel that listens for connections can only accept one connection - // at a time. - - // Returns true if the channel supports listening for connections. - bool AcceptsConnections() const; - - // Returns true if the channel supports listening for connections and is - // currently connected. - bool HasAcceptedConnection() const; - - // Returns true if the peer process' effective user id can be determined, in - // which case the supplied peer_euid is updated with it. - bool GetPeerEuid(uid_t* peer_euid) const; - - // Closes any currently connected socket, and returns to a listening state - // for more connections. - void ResetToAcceptingConnectionState(); + virtual int TakeClientFileDescriptor() = 0; #endif // defined(OS_POSIX) && !defined(OS_NACL) // Returns true if a named server channel is initialized on the given channel @@ -204,19 +211,22 @@ class IPC_EXPORT Channel : public Sender { static void SetGlobalPid(int pid); #endif - protected: - // Used in Chrome by the TestSink to provide a dummy channel implementation - // for testing. TestSink overrides the "interesting" functions in Channel so - // no actual implementation is needed. This will cause un-overridden calls to - // segfault. Do not use outside of test code! - Channel() : channel_impl_(0) { } - - private: - // PIMPL to which all channel calls are delegated. - class ChannelImpl; - ChannelImpl *channel_impl_; +#if defined(OS_ANDROID) + // Most tests are single process and work the same on all platforms. However + // in some cases we want to test multi-process, and Android differs in that it + // can't 'exec' after forking. This callback resets any data in the forked + // process such that it acts similar to if it was exec'd, for tests. + static void NotifyProcessForkedForTesting(); +#endif + }; +#if defined(OS_POSIX) +// SocketPair() creates a pair of socket FDs suitable for using with +// IPC::Channel. +IPC_EXPORT bool SocketPair(int* fd1, int* fd2); +#endif + } // namespace IPC #endif // IPC_IPC_CHANNEL_H_ diff --git a/chromium/ipc/ipc_channel_common.cc b/chromium/ipc/ipc_channel_common.cc new file mode 100644 index 00000000000..d7347cc7a1b --- /dev/null +++ b/chromium/ipc/ipc_channel_common.cc @@ -0,0 +1,47 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ipc/ipc_channel.h" + +namespace IPC { + +// static +scoped_ptr<Channel> Channel::CreateClient( + const IPC::ChannelHandle &channel_handle, Listener* listener) { + return Channel::Create(channel_handle, Channel::MODE_CLIENT, listener); +} + +// static +scoped_ptr<Channel> Channel::CreateNamedServer( + const IPC::ChannelHandle &channel_handle, Listener* listener) { + return Channel::Create(channel_handle, Channel::MODE_NAMED_SERVER, listener); +} + +// static +scoped_ptr<Channel> Channel::CreateNamedClient( + const IPC::ChannelHandle &channel_handle, Listener* listener) { + return Channel::Create(channel_handle, Channel::MODE_NAMED_CLIENT, listener); +} + +#if defined(OS_POSIX) +// static +scoped_ptr<Channel> Channel::CreateOpenNamedServer( + const IPC::ChannelHandle &channel_handle, Listener* listener) { + return Channel::Create(channel_handle, + Channel::MODE_OPEN_NAMED_SERVER, + listener); +} +#endif + +// static +scoped_ptr<Channel> Channel::CreateServer( + const IPC::ChannelHandle &channel_handle, Listener* listener) { + return Channel::Create(channel_handle, Channel::MODE_SERVER, listener); +} + +Channel::~Channel() { +} + +} // namespace IPC + diff --git a/chromium/ipc/ipc_channel_factory.cc b/chromium/ipc/ipc_channel_factory.cc index 5c24284f95d..244024c2f6d 100644 --- a/chromium/ipc/ipc_channel_factory.cc +++ b/chromium/ipc/ipc_channel_factory.cc @@ -5,6 +5,7 @@ #include "ipc/ipc_channel_factory.h" #include "base/file_util.h" +#include "base/files/scoped_file.h" #include "base/logging.h" #include "ipc/unix_domain_socket_util.h" @@ -51,21 +52,20 @@ void ChannelFactory::OnFileCanReadWithoutBlocking(int fd) { delegate_->OnListenError(); return; } + base::ScopedFD scoped_fd(new_fd); - if (new_fd < 0) { + if (!scoped_fd.is_valid()) { // The accept() failed, but not in such a way that the factory needs to be // shut down. return; } - file_util::ScopedFD scoped_fd(&new_fd); - // Verify that the IPC channel peer is running as the same user. - if (!IsPeerAuthorized(new_fd)) + if (!IsPeerAuthorized(scoped_fd.get())) return; ChannelHandle handle(std::string(), - base::FileDescriptor(*scoped_fd.release(), true)); + base::FileDescriptor(scoped_fd.release(), true)); delegate_->OnClientConnected(handle); } diff --git a/chromium/ipc/ipc_channel_nacl.cc b/chromium/ipc/ipc_channel_nacl.cc index 1ecd5718245..0928ba63305 100644 --- a/chromium/ipc/ipc_channel_nacl.cc +++ b/chromium/ipc/ipc_channel_nacl.cc @@ -17,6 +17,7 @@ #include "base/task_runner_util.h" #include "base/threading/simple_thread.h" #include "ipc/file_descriptor_set_posix.h" +#include "ipc/ipc_listener.h" #include "ipc/ipc_logging.h" #include "native_client/src/public/imc_syscalls.h" #include "native_client/src/public/imc_types.h" @@ -61,7 +62,7 @@ bool ReadDataOnReaderThread(int pipe, MessageContents* contents) { } // namespace -class Channel::ChannelImpl::ReaderThreadRunner +class ChannelNacl::ReaderThreadRunner : public base::DelegateSimpleThread::Delegate { public: // |pipe|: A file descriptor from which we will read using imc_recvmsg. @@ -90,7 +91,7 @@ class Channel::ChannelImpl::ReaderThreadRunner DISALLOW_COPY_AND_ASSIGN(ReaderThreadRunner); }; -Channel::ChannelImpl::ReaderThreadRunner::ReaderThreadRunner( +ChannelNacl::ReaderThreadRunner::ReaderThreadRunner( int pipe, base::Callback<void (scoped_ptr<MessageContents>)> data_read_callback, base::Callback<void ()> failure_callback, @@ -101,7 +102,7 @@ Channel::ChannelImpl::ReaderThreadRunner::ReaderThreadRunner( main_message_loop_(main_message_loop) { } -void Channel::ChannelImpl::ReaderThreadRunner::Run() { +void ChannelNacl::ReaderThreadRunner::Run() { while (true) { scoped_ptr<MessageContents> msg_contents(new MessageContents); bool success = ReadDataOnReaderThread(pipe_, msg_contents.get()); @@ -117,9 +118,9 @@ void Channel::ChannelImpl::ReaderThreadRunner::Run() { } } -Channel::ChannelImpl::ChannelImpl(const IPC::ChannelHandle& channel_handle, - Mode mode, - Listener* listener) +ChannelNacl::ChannelNacl(const IPC::ChannelHandle& channel_handle, + Mode mode, + Listener* listener) : ChannelReader(listener), mode_(mode), waiting_connect_(true), @@ -134,13 +135,19 @@ Channel::ChannelImpl::ChannelImpl(const IPC::ChannelHandle& channel_handle, } } -Channel::ChannelImpl::~ChannelImpl() { +ChannelNacl::~ChannelNacl() { Close(); } -bool Channel::ChannelImpl::Connect() { +base::ProcessId ChannelNacl::GetPeerPID() const { + // This shouldn't actually get used in the untrusted side of the proxy, and we + // don't have the real pid anyway. + return -1; +} + +bool ChannelNacl::Connect() { if (pipe_ == -1) { - DLOG(INFO) << "Channel creation failed: " << pipe_name_; + DLOG(WARNING) << "Channel creation failed: " << pipe_name_; return false; } @@ -152,9 +159,9 @@ bool Channel::ChannelImpl::Connect() { reader_thread_runner_.reset( new ReaderThreadRunner( pipe_, - base::Bind(&Channel::ChannelImpl::DidRecvMsg, + base::Bind(&ChannelNacl::DidRecvMsg, weak_ptr_factory_.GetWeakPtr()), - base::Bind(&Channel::ChannelImpl::ReadDidFail, + base::Bind(&ChannelNacl::ReadDidFail, weak_ptr_factory_.GetWeakPtr()), base::MessageLoopProxy::current())); reader_thread_.reset( @@ -164,10 +171,14 @@ bool Channel::ChannelImpl::Connect() { waiting_connect_ = false; // If there were any messages queued before connection, send them. ProcessOutgoingMessages(); + base::MessageLoopProxy::current()->PostTask(FROM_HERE, + base::Bind(&ChannelNacl::CallOnChannelConnected, + weak_ptr_factory_.GetWeakPtr())); + return true; } -void Channel::ChannelImpl::Close() { +void ChannelNacl::Close() { // For now, we assume that at shutdown, the reader thread will be woken with // a failure (see NaClIPCAdapter::BlockingRead and CloseChannel). Or... we // might simply be killed with no chance to clean up anyway :-). @@ -184,7 +195,7 @@ void Channel::ChannelImpl::Close() { output_queue_.clear(); } -bool Channel::ChannelImpl::Send(Message* message) { +bool ChannelNacl::Send(Message* message) { DVLOG(2) << "sending message @" << message << " on channel @" << this << " with type " << message->type(); scoped_ptr<Message> message_ptr(message); @@ -201,7 +212,7 @@ bool Channel::ChannelImpl::Send(Message* message) { return true; } -void Channel::ChannelImpl::DidRecvMsg(scoped_ptr<MessageContents> contents) { +void ChannelNacl::DidRecvMsg(scoped_ptr<MessageContents> contents) { // Close sets the pipe to -1. It's possible we'll get a buffer sent to us from // the reader thread after Close is called. If so, we ignore it. if (pipe_ == -1) @@ -222,11 +233,11 @@ void Channel::ChannelImpl::DidRecvMsg(scoped_ptr<MessageContents> contents) { ProcessIncomingMessages(); } -void Channel::ChannelImpl::ReadDidFail() { +void ChannelNacl::ReadDidFail() { Close(); } -bool Channel::ChannelImpl::CreatePipe( +bool ChannelNacl::CreatePipe( const IPC::ChannelHandle& channel_handle) { DCHECK(pipe_ == -1); @@ -245,7 +256,7 @@ bool Channel::ChannelImpl::CreatePipe( return true; } -bool Channel::ChannelImpl::ProcessOutgoingMessages() { +bool ChannelNacl::ProcessOutgoingMessages() { DCHECK(!waiting_connect_); // Why are we trying to send messages if there's // no connection? if (output_queue_.empty()) @@ -293,7 +304,11 @@ bool Channel::ChannelImpl::ProcessOutgoingMessages() { return true; } -Channel::ChannelImpl::ReadState Channel::ChannelImpl::ReadData( +void ChannelNacl::CallOnChannelConnected() { + listener()->OnChannelConnected(GetPeerPID()); +} + +ChannelNacl::ReadState ChannelNacl::ReadData( char* buffer, int buffer_len, int* bytes_read) { @@ -324,7 +339,7 @@ Channel::ChannelImpl::ReadState Channel::ChannelImpl::ReadData( return READ_SUCCEEDED; } -bool Channel::ChannelImpl::WillDispatchInputMessage(Message* msg) { +bool ChannelNacl::WillDispatchInputMessage(Message* msg) { uint16 header_fds = msg->header()->num_fds; CHECK(header_fds == input_fds_.size()); if (header_fds == 0) @@ -339,46 +354,24 @@ bool Channel::ChannelImpl::WillDispatchInputMessage(Message* msg) { return true; } -bool Channel::ChannelImpl::DidEmptyInputBuffers() { +bool ChannelNacl::DidEmptyInputBuffers() { // When the input data buffer is empty, the fds should be too. return input_fds_.empty(); } -void Channel::ChannelImpl::HandleInternalMessage(const Message& msg) { +void ChannelNacl::HandleInternalMessage(const Message& msg) { // The trusted side IPC::Channel should handle the "hello" handshake; we // should not receive the "Hello" message. NOTREACHED(); } -//------------------------------------------------------------------------------ -// Channel's methods simply call through to ChannelImpl. - -Channel::Channel(const IPC::ChannelHandle& channel_handle, - Mode mode, - Listener* listener) - : channel_impl_(new ChannelImpl(channel_handle, mode, listener)) { -} - -Channel::~Channel() { - delete channel_impl_; -} - -bool Channel::Connect() { - return channel_impl_->Connect(); -} - -void Channel::Close() { - channel_impl_->Close(); -} - -base::ProcessId Channel::peer_pid() const { - // This shouldn't actually get used in the untrusted side of the proxy, and we - // don't have the real pid anyway. - return -1; -} +// Channel's methods -bool Channel::Send(Message* message) { - return channel_impl_->Send(message); +// static +scoped_ptr<Channel> Channel::Create( + const IPC::ChannelHandle &channel_handle, Mode mode, Listener* listener) { + return scoped_ptr<Channel>( + new ChannelNacl(channel_handle, mode, listener)); } } // namespace IPC diff --git a/chromium/ipc/ipc_channel_nacl.h b/chromium/ipc/ipc_channel_nacl.h index a21730eb813..c47892d25c4 100644 --- a/chromium/ipc/ipc_channel_nacl.h +++ b/chromium/ipc/ipc_channel_nacl.h @@ -22,7 +22,7 @@ namespace IPC { // descriptors). struct MessageContents; -// Similar to the Posix version of ChannelImpl but for Native Client code. +// Similar to the ChannelPosix but for Native Client code. // This is somewhat different because sendmsg/recvmsg here do not follow POSIX // semantics. Instead, they are implemented by a custom embedding of // NaClDescCustom. See NaClIPCAdapter for the trusted-side implementation. @@ -31,18 +31,20 @@ struct MessageContents; // sharing handles. We also currently do not support passing file descriptors or // named pipes, and we use background threads to emulate signaling when we can // read or write without blocking. -class Channel::ChannelImpl : public internal::ChannelReader { +class ChannelNacl : public Channel, + public internal::ChannelReader { public: // Mirror methods of Channel, see ipc_channel.h for description. - ChannelImpl(const IPC::ChannelHandle& channel_handle, + ChannelNacl(const IPC::ChannelHandle& channel_handle, Mode mode, Listener* listener); - virtual ~ChannelImpl(); + virtual ~ChannelNacl(); // Channel implementation. - bool Connect(); - void Close(); - bool Send(Message* message); + virtual base::ProcessId GetPeerPID() const OVERRIDE; + virtual bool Connect() OVERRIDE; + virtual void Close() OVERRIDE; + virtual bool Send(Message* message) OVERRIDE; // Posted to the main thread by ReaderThreadRunner. void DidRecvMsg(scoped_ptr<MessageContents> contents); @@ -53,6 +55,7 @@ class Channel::ChannelImpl : public internal::ChannelReader { bool CreatePipe(const IPC::ChannelHandle& channel_handle); bool ProcessOutgoingMessages(); + void CallOnChannelConnected(); // ChannelReader implementation. virtual ReadState ReadData(char* buffer, @@ -108,9 +111,9 @@ class Channel::ChannelImpl : public internal::ChannelReader { // called. Normally after we're connected, the queue is empty. std::deque<linked_ptr<Message> > output_queue_; - base::WeakPtrFactory<ChannelImpl> weak_ptr_factory_; + base::WeakPtrFactory<ChannelNacl> weak_ptr_factory_; - DISALLOW_IMPLICIT_CONSTRUCTORS(ChannelImpl); + DISALLOW_IMPLICIT_CONSTRUCTORS(ChannelNacl); }; } // namespace IPC diff --git a/chromium/ipc/ipc_channel_posix.cc b/chromium/ipc/ipc_channel_posix.cc index 87885329001..8ddf73a2442 100644 --- a/chromium/ipc/ipc_channel_posix.cc +++ b/chromium/ipc/ipc_channel_posix.cc @@ -135,6 +135,9 @@ class PipeMap { ChannelToFDMap map_; friend struct DefaultSingletonTraits<PipeMap>; +#if defined(OS_ANDROID) + friend void ::IPC::Channel::NotifyProcessForkedForTesting(); +#endif }; //------------------------------------------------------------------------------ @@ -159,14 +162,23 @@ bool SocketWriteErrorIsRecoverable() { } } // namespace + +#if defined(OS_ANDROID) +// When we fork for simple tests on Android, we can't 'exec', so we need to +// reset these entries manually to get the expected testing behavior. +void Channel::NotifyProcessForkedForTesting() { + PipeMap::GetInstance()->map_.clear(); +} +#endif + //------------------------------------------------------------------------------ #if defined(OS_LINUX) -int Channel::ChannelImpl::global_pid_ = 0; +int ChannelPosix::global_pid_ = 0; #endif // OS_LINUX -Channel::ChannelImpl::ChannelImpl(const IPC::ChannelHandle& channel_handle, - Mode mode, Listener* listener) +ChannelPosix::ChannelPosix(const IPC::ChannelHandle& channel_handle, + Mode mode, Listener* listener) : ChannelReader(listener), mode_(mode), peer_pid_(base::kNullProcessId), @@ -191,7 +203,7 @@ Channel::ChannelImpl::ChannelImpl(const IPC::ChannelHandle& channel_handle, } } -Channel::ChannelImpl::~ChannelImpl() { +ChannelPosix::~ChannelPosix() { Close(); } @@ -219,7 +231,7 @@ bool SocketPair(int* fd1, int* fd2) { return true; } -bool Channel::ChannelImpl::CreatePipe( +bool ChannelPosix::CreatePipe( const IPC::ChannelHandle& channel_handle) { DCHECK(server_listen_pipe_ == -1 && pipe_ == -1); @@ -227,7 +239,7 @@ bool Channel::ChannelImpl::CreatePipe( // 1) It's a channel wrapping a pipe that is given to us. // 2) It's for a named channel, so we create it. // 3) It's for a client that we implement ourself. This is used - // in unittesting. + // in single-process unittesting. // 4) It's the initial IPC channel: // 4a) Client side: Pull the pipe out of the GlobalDescriptors set. // 4b) Server side: create the pipe. @@ -325,9 +337,9 @@ bool Channel::ChannelImpl::CreatePipe( return true; } -bool Channel::ChannelImpl::Connect() { +bool ChannelPosix::Connect() { if (server_listen_pipe_ == -1 && pipe_ == -1) { - DLOG(INFO) << "Channel creation failed: " << pipe_name_; + DLOG(WARNING) << "Channel creation failed: " << pipe_name_; return false; } @@ -347,7 +359,7 @@ bool Channel::ChannelImpl::Connect() { return did_connect; } -void Channel::ChannelImpl::CloseFileDescriptors(Message* msg) { +void ChannelPosix::CloseFileDescriptors(Message* msg) { #if defined(OS_MACOSX) // There is a bug on OSX which makes it dangerous to close // a file descriptor while it is in transit. So instead we @@ -368,7 +380,7 @@ void Channel::ChannelImpl::CloseFileDescriptors(Message* msg) { #endif } -bool Channel::ChannelImpl::ProcessOutgoingMessages() { +bool ChannelPosix::ProcessOutgoingMessages() { DCHECK(!waiting_connect_); // Why are we trying to send messages if there's // no connection? if (output_queue_.empty()) @@ -464,15 +476,19 @@ bool Channel::ChannelImpl::ProcessOutgoingMessages() { CloseFileDescriptors(msg); if (bytes_written < 0 && !SocketWriteErrorIsRecoverable()) { + // We can't close the pipe here, because calling OnChannelError + // may destroy this object, and that would be bad if we are + // called from Send(). Instead, we return false and hope the + // caller will close the pipe. If they do not, the pipe will + // still be closed next time OnFileCanReadWithoutBlocking is + // called. #if defined(OS_MACOSX) // On OSX writing to a pipe with no listener returns EPERM. if (errno == EPERM) { - Close(); return false; } #endif // OS_MACOSX if (errno == EPIPE) { - Close(); return false; } PLOG(ERROR) << "pipe error on " @@ -510,7 +526,7 @@ bool Channel::ChannelImpl::ProcessOutgoingMessages() { return true; } -bool Channel::ChannelImpl::Send(Message* message) { +bool ChannelPosix::Send(Message* message) { DVLOG(2) << "sending message @" << message << " on channel @" << this << " with type " << message->type() << " (" << output_queue_.size() << " in queue)"; @@ -528,12 +544,12 @@ bool Channel::ChannelImpl::Send(Message* message) { return true; } -int Channel::ChannelImpl::GetClientFileDescriptor() { +int ChannelPosix::GetClientFileDescriptor() const { base::AutoLock lock(client_pipe_lock_); return client_pipe_; } -int Channel::ChannelImpl::TakeClientFileDescriptor() { +int ChannelPosix::TakeClientFileDescriptor() { base::AutoLock lock(client_pipe_lock_); int fd = client_pipe_; if (client_pipe_ != -1) { @@ -543,7 +559,7 @@ int Channel::ChannelImpl::TakeClientFileDescriptor() { return fd; } -void Channel::ChannelImpl::CloseClientFileDescriptor() { +void ChannelPosix::CloseClientFileDescriptor() { base::AutoLock lock(client_pipe_lock_); if (client_pipe_ != -1) { PipeMap::GetInstance()->Remove(pipe_name_); @@ -553,20 +569,20 @@ void Channel::ChannelImpl::CloseClientFileDescriptor() { } } -bool Channel::ChannelImpl::AcceptsConnections() const { +bool ChannelPosix::AcceptsConnections() const { return server_listen_pipe_ != -1; } -bool Channel::ChannelImpl::HasAcceptedConnection() const { +bool ChannelPosix::HasAcceptedConnection() const { return AcceptsConnections() && pipe_ != -1; } -bool Channel::ChannelImpl::GetPeerEuid(uid_t* peer_euid) const { +bool ChannelPosix::GetPeerEuid(uid_t* peer_euid) const { DCHECK(!(mode_ & MODE_SERVER) || HasAcceptedConnection()); return IPC::GetPeerEuid(pipe_, peer_euid); } -void Channel::ChannelImpl::ResetToAcceptingConnectionState() { +void ChannelPosix::ResetToAcceptingConnectionState() { // Unregister libevent for the unix domain socket and close it. read_watcher_.StopWatchingFileDescriptor(); write_watcher_.StopWatchingFileDescriptor(); @@ -610,20 +626,20 @@ void Channel::ChannelImpl::ResetToAcceptingConnectionState() { } // static -bool Channel::ChannelImpl::IsNamedServerInitialized( +bool ChannelPosix::IsNamedServerInitialized( const std::string& channel_id) { return base::PathExists(base::FilePath(channel_id)); } #if defined(OS_LINUX) // static -void Channel::ChannelImpl::SetGlobalPid(int pid) { +void ChannelPosix::SetGlobalPid(int pid) { global_pid_ = pid; } #endif // OS_LINUX // Called by libevent when we can read from the pipe without blocking. -void Channel::ChannelImpl::OnFileCanReadWithoutBlocking(int fd) { +void ChannelPosix::OnFileCanReadWithoutBlocking(int fd) { if (fd == server_listen_pipe_) { int new_pipe = 0; if (!ServerAcceptConnection(server_listen_pipe_, &new_pipe) || @@ -680,7 +696,7 @@ void Channel::ChannelImpl::OnFileCanReadWithoutBlocking(int fd) { // If we're a server and handshaking, then we want to make sure that we // only send our handshake message after we've processed the client's. // This gives us a chance to kill the client if the incoming handshake - // is invalid. This also flushes any closefd messagse. + // is invalid. This also flushes any closefd messages. if (!is_blocked_on_write_) { if (!ProcessOutgoingMessages()) { ClosePipeOnError(); @@ -689,7 +705,7 @@ void Channel::ChannelImpl::OnFileCanReadWithoutBlocking(int fd) { } // Called by libevent when we can write to the pipe without blocking. -void Channel::ChannelImpl::OnFileCanWriteWithoutBlocking(int fd) { +void ChannelPosix::OnFileCanWriteWithoutBlocking(int fd) { DCHECK_EQ(pipe_, fd); is_blocked_on_write_ = false; if (!ProcessOutgoingMessages()) { @@ -697,7 +713,7 @@ void Channel::ChannelImpl::OnFileCanWriteWithoutBlocking(int fd) { } } -bool Channel::ChannelImpl::AcceptConnection() { +bool ChannelPosix::AcceptConnection() { base::MessageLoopForIO::current()->WatchFileDescriptor( pipe_, true, base::MessageLoopForIO::WATCH_READ, &read_watcher_, this); QueueHelloMessage(); @@ -717,7 +733,7 @@ bool Channel::ChannelImpl::AcceptConnection() { } } -void Channel::ChannelImpl::ClosePipeOnError() { +void ChannelPosix::ClosePipeOnError() { if (HasAcceptedConnection()) { ResetToAcceptingConnectionState(); listener()->OnChannelError(); @@ -731,7 +747,7 @@ void Channel::ChannelImpl::ClosePipeOnError() { } } -int Channel::ChannelImpl::GetHelloMessageProcId() { +int ChannelPosix::GetHelloMessageProcId() { int pid = base::GetCurrentProcId(); #if defined(OS_LINUX) // Our process may be in a sandbox with a separate PID namespace. @@ -742,7 +758,7 @@ int Channel::ChannelImpl::GetHelloMessageProcId() { return pid; } -void Channel::ChannelImpl::QueueHelloMessage() { +void ChannelPosix::QueueHelloMessage() { // Create the Hello message scoped_ptr<Message> msg(new Message(MSG_ROUTING_NONE, HELLO_MESSAGE_TYPE, @@ -763,7 +779,7 @@ void Channel::ChannelImpl::QueueHelloMessage() { output_queue_.push(msg.release()); } -Channel::ChannelImpl::ReadState Channel::ChannelImpl::ReadData( +ChannelPosix::ReadState ChannelPosix::ReadData( char* buffer, int buffer_len, int* bytes_read) { @@ -821,7 +837,7 @@ Channel::ChannelImpl::ReadState Channel::ChannelImpl::ReadData( } #if defined(IPC_USES_READWRITE) -bool Channel::ChannelImpl::ReadFileDescriptorsFromFDPipe() { +bool ChannelPosix::ReadFileDescriptorsFromFDPipe() { char dummy; struct iovec fd_pipe_iov = { &dummy, 1 }; @@ -846,7 +862,7 @@ bool Channel::ChannelImpl::ReadFileDescriptorsFromFDPipe() { // // This will read from the input_fds_ (READWRITE mode only) and read more // handles from the FD pipe if necessary. -bool Channel::ChannelImpl::WillDispatchInputMessage(Message* msg) { +bool ChannelPosix::WillDispatchInputMessage(Message* msg) { uint16 header_fds = msg->header()->num_fds; if (!header_fds) return true; // Nothing to do. @@ -886,14 +902,14 @@ bool Channel::ChannelImpl::WillDispatchInputMessage(Message* msg) { return true; } -bool Channel::ChannelImpl::DidEmptyInputBuffers() { +bool ChannelPosix::DidEmptyInputBuffers() { // When the input data buffer is empty, the fds should be too. If this is // not the case, we probably have a rogue renderer which is trying to fill // our descriptor table. return input_fds_.empty(); } -bool Channel::ChannelImpl::ExtractFileDescriptorsFromMsghdr(msghdr* msg) { +bool ChannelPosix::ExtractFileDescriptorsFromMsghdr(msghdr* msg) { // Check that there are any control messages. On OSX, CMSG_FIRSTHDR will // return an invalid non-NULL pointer in the case that controllen == 0. if (msg->msg_controllen == 0) @@ -925,7 +941,7 @@ bool Channel::ChannelImpl::ExtractFileDescriptorsFromMsghdr(msghdr* msg) { return true; } -void Channel::ChannelImpl::ClearInputFDs() { +void ChannelPosix::ClearInputFDs() { for (size_t i = 0; i < input_fds_.size(); ++i) { if (IGNORE_EINTR(close(input_fds_[i])) < 0) PLOG(ERROR) << "close "; @@ -933,7 +949,7 @@ void Channel::ChannelImpl::ClearInputFDs() { input_fds_.clear(); } -void Channel::ChannelImpl::QueueCloseFDMessage(int fd, int hops) { +void ChannelPosix::QueueCloseFDMessage(int fd, int hops) { switch (hops) { case 1: case 2: { @@ -955,7 +971,7 @@ void Channel::ChannelImpl::QueueCloseFDMessage(int fd, int hops) { } } -void Channel::ChannelImpl::HandleInternalMessage(const Message& msg) { +void ChannelPosix::HandleInternalMessage(const Message& msg) { // The Hello message contains only the process id. PickleIterator iter(msg); @@ -1009,7 +1025,7 @@ void Channel::ChannelImpl::HandleInternalMessage(const Message& msg) { } } -void Channel::ChannelImpl::Close() { +void ChannelPosix::Close() { // Close can be called multiple time, so we need to make sure we're // idempotent. @@ -1030,61 +1046,18 @@ void Channel::ChannelImpl::Close() { CloseClientFileDescriptor(); } -//------------------------------------------------------------------------------ -// Channel's methods simply call through to ChannelImpl. -Channel::Channel(const IPC::ChannelHandle& channel_handle, Mode mode, - Listener* listener) - : channel_impl_(new ChannelImpl(channel_handle, mode, listener)) { -} - -Channel::~Channel() { - delete channel_impl_; -} - -bool Channel::Connect() { - return channel_impl_->Connect(); -} - -void Channel::Close() { - if (channel_impl_) - channel_impl_->Close(); -} - -base::ProcessId Channel::peer_pid() const { - return channel_impl_->peer_pid(); -} - -bool Channel::Send(Message* message) { - return channel_impl_->Send(message); +base::ProcessId ChannelPosix::GetPeerPID() const { + return peer_pid_; } -int Channel::GetClientFileDescriptor() const { - return channel_impl_->GetClientFileDescriptor(); -} - -int Channel::TakeClientFileDescriptor() { - return channel_impl_->TakeClientFileDescriptor(); -} - -bool Channel::AcceptsConnections() const { - return channel_impl_->AcceptsConnections(); -} - -bool Channel::HasAcceptedConnection() const { - return channel_impl_->HasAcceptedConnection(); -} - -bool Channel::GetPeerEuid(uid_t* peer_euid) const { - return channel_impl_->GetPeerEuid(peer_euid); -} - -void Channel::ResetToAcceptingConnectionState() { - channel_impl_->ResetToAcceptingConnectionState(); -} +//------------------------------------------------------------------------------ +// Channel's methods // static -bool Channel::IsNamedServerInitialized(const std::string& channel_id) { - return ChannelImpl::IsNamedServerInitialized(channel_id); +scoped_ptr<Channel> Channel::Create( + const IPC::ChannelHandle &channel_handle, Mode mode, Listener* listener) { + return make_scoped_ptr(new ChannelPosix( + channel_handle, mode, listener)).PassAs<Channel>(); } // static @@ -1100,10 +1073,15 @@ std::string Channel::GenerateVerifiedChannelID(const std::string& prefix) { } +bool Channel::IsNamedServerInitialized( + const std::string& channel_id) { + return ChannelPosix::IsNamedServerInitialized(channel_id); +} + #if defined(OS_LINUX) // static void Channel::SetGlobalPid(int pid) { - ChannelImpl::SetGlobalPid(pid); + ChannelPosix::SetGlobalPid(pid); } #endif // OS_LINUX diff --git a/chromium/ipc/ipc_channel_posix.h b/chromium/ipc/ipc_channel_posix.h index 1e587c13eb0..7f17b2fc487 100644 --- a/chromium/ipc/ipc_channel_posix.h +++ b/chromium/ipc/ipc_channel_posix.h @@ -49,24 +49,39 @@ namespace IPC { -class Channel::ChannelImpl : public internal::ChannelReader, - public base::MessageLoopForIO::Watcher { +class IPC_EXPORT ChannelPosix : public Channel, + public internal::ChannelReader, + public base::MessageLoopForIO::Watcher { public: - // Mirror methods of Channel, see ipc_channel.h for description. - ChannelImpl(const IPC::ChannelHandle& channel_handle, Mode mode, - Listener* listener); - virtual ~ChannelImpl(); - bool Connect(); - void Close(); - bool Send(Message* message); - int GetClientFileDescriptor(); - int TakeClientFileDescriptor(); - void CloseClientFileDescriptor(); + ChannelPosix(const IPC::ChannelHandle& channel_handle, Mode mode, + Listener* listener); + virtual ~ChannelPosix(); + + // Channel implementation + virtual bool Connect() OVERRIDE; + virtual void Close() OVERRIDE; + virtual bool Send(Message* message) OVERRIDE; + virtual base::ProcessId GetPeerPID() const OVERRIDE; + virtual int GetClientFileDescriptor() const OVERRIDE; + virtual int TakeClientFileDescriptor() OVERRIDE; + + // Returns true if the channel supports listening for connections. bool AcceptsConnections() const; + + // Returns true if the channel supports listening for connections and is + // currently connected. bool HasAcceptedConnection() const; - bool GetPeerEuid(uid_t* peer_euid) const; + + // Closes any currently connected socket, and returns to a listening state + // for more connections. void ResetToAcceptingConnectionState(); - base::ProcessId peer_pid() const { return peer_pid_; } + + // Returns true if the peer process' effective user id can be determined, in + // which case the supplied peer_euid is updated with it. + bool GetPeerEuid(uid_t* peer_euid) const; + + void CloseClientFileDescriptor(); + static bool IsNamedServerInitialized(const std::string& channel_id); #if defined(OS_LINUX) static void SetGlobalPid(int pid); @@ -144,7 +159,7 @@ class Channel::ChannelImpl : public internal::ChannelReader, // For a server, the client end of our socketpair() -- the other end of our // pipe_ that is passed to the client. int client_pipe_; - base::Lock client_pipe_lock_; // Lock that protects |client_pipe_|. + mutable base::Lock client_pipe_lock_; // Lock that protects |client_pipe_|. #if defined(IPC_USES_READWRITE) // Linux/BSD use a dedicated socketpair() for passing file descriptors. @@ -202,7 +217,7 @@ class Channel::ChannelImpl : public internal::ChannelReader, static int global_pid_; #endif // OS_LINUX - DISALLOW_IMPLICIT_CONSTRUCTORS(ChannelImpl); + DISALLOW_IMPLICIT_CONSTRUCTORS(ChannelPosix); }; } // namespace IPC diff --git a/chromium/ipc/ipc_channel_posix_unittest.cc b/chromium/ipc/ipc_channel_posix_unittest.cc index dbd854e16c6..a4b4f8dbb95 100644 --- a/chromium/ipc/ipc_channel_posix_unittest.cc +++ b/chromium/ipc/ipc_channel_posix_unittest.cc @@ -41,7 +41,9 @@ class IPCChannelPosixTestListener : public IPC::Listener { }; IPCChannelPosixTestListener(bool quit_only_on_message) - : status_(DISCONNECTED), quit_only_on_message_(quit_only_on_message) {} + : status_(DISCONNECTED), + quit_only_on_message_(quit_only_on_message) { + } virtual ~IPCChannelPosixTestListener() {} @@ -61,9 +63,7 @@ class IPCChannelPosixTestListener : public IPC::Listener { virtual void OnChannelError() OVERRIDE { status_ = CHANNEL_ERROR; - if (!quit_only_on_message_) { - QuitRunLoop(); - } + QuitRunLoop(); } virtual void OnChannelDenied() OVERRIDE { @@ -83,7 +83,13 @@ class IPCChannelPosixTestListener : public IPC::Listener { STATUS status() { return status_; } void QuitRunLoop() { - base::MessageLoopForIO::current()->QuitNow(); + base::MessageLoopForIO* loop = base::MessageLoopForIO::current(); + if (loop->is_running()) { + loop->QuitNow(); + } else { + // Die as soon as Run is called. + loop->PostTask(FROM_HERE, loop->QuitClosure()); + } } private: @@ -186,7 +192,7 @@ void IPCChannelPosixTest::SpinRunLoop(base::TimeDelta delay) { // in the case of a bad test. Usually, the run loop will quit sooner than // that because all tests use a IPCChannelPosixTestListener which quits the // current run loop on any channel activity. - loop->PostDelayedTask(FROM_HERE, base::MessageLoop::QuitClosure(), delay); + loop->PostDelayedTask(FROM_HERE, loop->QuitClosure(), delay); loop->Run(); } @@ -198,12 +204,13 @@ TEST_F(IPCChannelPosixTest, BasicListen) { IPC::ChannelHandle handle(kChannelName); SetUpSocket(&handle, IPC::Channel::MODE_NAMED_SERVER); unlink(handle.name.c_str()); - IPC::Channel channel(handle, IPC::Channel::MODE_NAMED_SERVER, NULL); - ASSERT_TRUE(channel.Connect()); - ASSERT_TRUE(channel.AcceptsConnections()); - ASSERT_FALSE(channel.HasAcceptedConnection()); - channel.ResetToAcceptingConnectionState(); - ASSERT_FALSE(channel.HasAcceptedConnection()); + scoped_ptr<IPC::ChannelPosix> channel( + new IPC::ChannelPosix(handle, IPC::Channel::MODE_NAMED_SERVER, NULL)); + ASSERT_TRUE(channel->Connect()); + ASSERT_TRUE(channel->AcceptsConnections()); + ASSERT_FALSE(channel->HasAcceptedConnection()); + channel->ResetToAcceptingConnectionState(); + ASSERT_FALSE(channel->HasAcceptedConnection()); } TEST_F(IPCChannelPosixTest, BasicConnected) { @@ -215,17 +222,66 @@ TEST_F(IPCChannelPosixTest, BasicConnected) { base::FileDescriptor fd(pipe_fds[0], false); IPC::ChannelHandle handle(socket_name, fd); - IPC::Channel channel(handle, IPC::Channel::MODE_SERVER, NULL); - ASSERT_TRUE(channel.Connect()); - ASSERT_FALSE(channel.AcceptsConnections()); - channel.Close(); + scoped_ptr<IPC::ChannelPosix> channel(new IPC::ChannelPosix( + handle, IPC::Channel::MODE_SERVER, NULL)); + ASSERT_TRUE(channel->Connect()); + ASSERT_FALSE(channel->AcceptsConnections()); + channel->Close(); ASSERT_TRUE(IGNORE_EINTR(close(pipe_fds[1])) == 0); // Make sure that we can use the socket that is created for us by // a standard channel. - IPC::Channel channel2(socket_name, IPC::Channel::MODE_SERVER, NULL); - ASSERT_TRUE(channel2.Connect()); - ASSERT_FALSE(channel2.AcceptsConnections()); + scoped_ptr<IPC::ChannelPosix> channel2(new IPC::ChannelPosix( + socket_name, IPC::Channel::MODE_SERVER, NULL)); + ASSERT_TRUE(channel2->Connect()); + ASSERT_FALSE(channel2->AcceptsConnections()); +} + +// If a connection closes right before a Send() call, we may end up closing +// the connection without notifying the listener, which can cause hangs in +// sync_message_filter and others. Make sure the listener is notified. +TEST_F(IPCChannelPosixTest, SendHangTest) { + IPCChannelPosixTestListener out_listener(true); + IPCChannelPosixTestListener in_listener(true); + IPC::ChannelHandle in_handle("IN"); + scoped_ptr<IPC::ChannelPosix> in_chan(new IPC::ChannelPosix( + in_handle, IPC::Channel::MODE_SERVER, &in_listener)); + base::FileDescriptor out_fd( + in_chan->TakeClientFileDescriptor(), false); + IPC::ChannelHandle out_handle("OUT", out_fd); + scoped_ptr<IPC::ChannelPosix> out_chan(new IPC::ChannelPosix( + out_handle, IPC::Channel::MODE_CLIENT, &out_listener)); + ASSERT_TRUE(in_chan->Connect()); + ASSERT_TRUE(out_chan->Connect()); + in_chan->Close(); // simulate remote process dying at an unfortunate time. + // Send will fail, because it cannot write the message. + ASSERT_FALSE(out_chan->Send(new IPC::Message( + 0, // routing_id + kQuitMessage, // message type + IPC::Message::PRIORITY_NORMAL))); + SpinRunLoop(TestTimeouts::action_max_timeout()); + ASSERT_EQ(IPCChannelPosixTestListener::CHANNEL_ERROR, out_listener.status()); +} + +// If a connection closes right before a Connect() call, we may end up closing +// the connection without notifying the listener, which can cause hangs in +// sync_message_filter and others. Make sure the listener is notified. +TEST_F(IPCChannelPosixTest, AcceptHangTest) { + IPCChannelPosixTestListener out_listener(true); + IPCChannelPosixTestListener in_listener(true); + IPC::ChannelHandle in_handle("IN"); + scoped_ptr<IPC::ChannelPosix> in_chan(new IPC::ChannelPosix( + in_handle, IPC::Channel::MODE_SERVER, &in_listener)); + base::FileDescriptor out_fd( + in_chan->TakeClientFileDescriptor(), false); + IPC::ChannelHandle out_handle("OUT", out_fd); + scoped_ptr<IPC::ChannelPosix> out_chan(new IPC::ChannelPosix( + out_handle, IPC::Channel::MODE_CLIENT, &out_listener)); + ASSERT_TRUE(in_chan->Connect()); + in_chan->Close(); // simulate remote process dying at an unfortunate time. + ASSERT_FALSE(out_chan->Connect()); + SpinRunLoop(TestTimeouts::action_max_timeout()); + ASSERT_EQ(IPCChannelPosixTestListener::CHANNEL_ERROR, out_listener.status()); } TEST_F(IPCChannelPosixTest, AdvancedConnected) { @@ -233,27 +289,27 @@ TEST_F(IPCChannelPosixTest, AdvancedConnected) { IPCChannelPosixTestListener listener(false); IPC::ChannelHandle chan_handle(GetConnectionSocketName()); SetUpSocket(&chan_handle, IPC::Channel::MODE_NAMED_SERVER); - IPC::Channel channel(chan_handle, IPC::Channel::MODE_NAMED_SERVER, &listener); - ASSERT_TRUE(channel.Connect()); - ASSERT_TRUE(channel.AcceptsConnections()); - ASSERT_FALSE(channel.HasAcceptedConnection()); + scoped_ptr<IPC::ChannelPosix> channel(new IPC::ChannelPosix( + chan_handle, IPC::Channel::MODE_NAMED_SERVER, &listener)); + ASSERT_TRUE(channel->Connect()); + ASSERT_TRUE(channel->AcceptsConnections()); + ASSERT_FALSE(channel->HasAcceptedConnection()); - base::ProcessHandle handle = SpawnChild("IPCChannelPosixTestConnectionProc", - false); + base::ProcessHandle handle = SpawnChild("IPCChannelPosixTestConnectionProc"); ASSERT_TRUE(handle); SpinRunLoop(TestTimeouts::action_max_timeout()); ASSERT_EQ(IPCChannelPosixTestListener::CONNECTED, listener.status()); - ASSERT_TRUE(channel.HasAcceptedConnection()); + ASSERT_TRUE(channel->HasAcceptedConnection()); IPC::Message* message = new IPC::Message(0, // routing_id kQuitMessage, // message type IPC::Message::PRIORITY_NORMAL); - channel.Send(message); + channel->Send(message); SpinRunLoop(TestTimeouts::action_timeout()); int exit_code = 0; EXPECT_TRUE(base::WaitForExitCode(handle, &exit_code)); EXPECT_EQ(0, exit_code); ASSERT_EQ(IPCChannelPosixTestListener::CHANNEL_ERROR, listener.status()); - ASSERT_FALSE(channel.HasAcceptedConnection()); + ASSERT_FALSE(channel->HasAcceptedConnection()); } TEST_F(IPCChannelPosixTest, ResetState) { @@ -263,44 +319,44 @@ TEST_F(IPCChannelPosixTest, ResetState) { IPCChannelPosixTestListener listener(false); IPC::ChannelHandle chan_handle(GetConnectionSocketName()); SetUpSocket(&chan_handle, IPC::Channel::MODE_NAMED_SERVER); - IPC::Channel channel(chan_handle, IPC::Channel::MODE_NAMED_SERVER, &listener); - ASSERT_TRUE(channel.Connect()); - ASSERT_TRUE(channel.AcceptsConnections()); - ASSERT_FALSE(channel.HasAcceptedConnection()); + scoped_ptr<IPC::ChannelPosix> channel(new IPC::ChannelPosix( + chan_handle, IPC::Channel::MODE_NAMED_SERVER, &listener)); + ASSERT_TRUE(channel->Connect()); + ASSERT_TRUE(channel->AcceptsConnections()); + ASSERT_FALSE(channel->HasAcceptedConnection()); - base::ProcessHandle handle = SpawnChild("IPCChannelPosixTestConnectionProc", - false); + base::ProcessHandle handle = SpawnChild("IPCChannelPosixTestConnectionProc"); ASSERT_TRUE(handle); SpinRunLoop(TestTimeouts::action_max_timeout()); ASSERT_EQ(IPCChannelPosixTestListener::CONNECTED, listener.status()); - ASSERT_TRUE(channel.HasAcceptedConnection()); - channel.ResetToAcceptingConnectionState(); - ASSERT_FALSE(channel.HasAcceptedConnection()); + ASSERT_TRUE(channel->HasAcceptedConnection()); + channel->ResetToAcceptingConnectionState(); + ASSERT_FALSE(channel->HasAcceptedConnection()); - base::ProcessHandle handle2 = SpawnChild("IPCChannelPosixTestConnectionProc", - false); + base::ProcessHandle handle2 = SpawnChild("IPCChannelPosixTestConnectionProc"); ASSERT_TRUE(handle2); SpinRunLoop(TestTimeouts::action_max_timeout()); ASSERT_EQ(IPCChannelPosixTestListener::CONNECTED, listener.status()); - ASSERT_TRUE(channel.HasAcceptedConnection()); + ASSERT_TRUE(channel->HasAcceptedConnection()); IPC::Message* message = new IPC::Message(0, // routing_id kQuitMessage, // message type IPC::Message::PRIORITY_NORMAL); - channel.Send(message); + channel->Send(message); SpinRunLoop(TestTimeouts::action_timeout()); EXPECT_TRUE(base::KillProcess(handle, 0, false)); int exit_code = 0; EXPECT_TRUE(base::WaitForExitCode(handle2, &exit_code)); EXPECT_EQ(0, exit_code); ASSERT_EQ(IPCChannelPosixTestListener::CHANNEL_ERROR, listener.status()); - ASSERT_FALSE(channel.HasAcceptedConnection()); + ASSERT_FALSE(channel->HasAcceptedConnection()); } TEST_F(IPCChannelPosixTest, BadChannelName) { // Test empty name IPC::ChannelHandle handle(""); - IPC::Channel channel(handle, IPC::Channel::MODE_NAMED_SERVER, NULL); - ASSERT_FALSE(channel.Connect()); + scoped_ptr<IPC::ChannelPosix> channel(new IPC::ChannelPosix( + handle, IPC::Channel::MODE_NAMED_SERVER, NULL)); + ASSERT_FALSE(channel->Connect()); // Test name that is too long. const char *kTooLongName = "This_is_a_very_long_name_to_proactively_implement" @@ -312,8 +368,9 @@ TEST_F(IPCChannelPosixTest, BadChannelName) { "leading-edge_processes"; EXPECT_GE(strlen(kTooLongName), IPC::kMaxSocketNameLength); IPC::ChannelHandle handle2(kTooLongName); - IPC::Channel channel2(handle2, IPC::Channel::MODE_NAMED_SERVER, NULL); - EXPECT_FALSE(channel2.Connect()); + scoped_ptr<IPC::ChannelPosix> channel2(new IPC::ChannelPosix( + handle2, IPC::Channel::MODE_NAMED_SERVER, NULL)); + EXPECT_FALSE(channel2->Connect()); } TEST_F(IPCChannelPosixTest, MultiConnection) { @@ -322,35 +379,34 @@ TEST_F(IPCChannelPosixTest, MultiConnection) { IPCChannelPosixTestListener listener(false); IPC::ChannelHandle chan_handle(GetConnectionSocketName()); SetUpSocket(&chan_handle, IPC::Channel::MODE_NAMED_SERVER); - IPC::Channel channel(chan_handle, IPC::Channel::MODE_NAMED_SERVER, &listener); - ASSERT_TRUE(channel.Connect()); - ASSERT_TRUE(channel.AcceptsConnections()); - ASSERT_FALSE(channel.HasAcceptedConnection()); + scoped_ptr<IPC::ChannelPosix> channel(new IPC::ChannelPosix( + chan_handle, IPC::Channel::MODE_NAMED_SERVER, &listener)); + ASSERT_TRUE(channel->Connect()); + ASSERT_TRUE(channel->AcceptsConnections()); + ASSERT_FALSE(channel->HasAcceptedConnection()); - base::ProcessHandle handle = SpawnChild("IPCChannelPosixTestConnectionProc", - false); + base::ProcessHandle handle = SpawnChild("IPCChannelPosixTestConnectionProc"); ASSERT_TRUE(handle); SpinRunLoop(TestTimeouts::action_max_timeout()); ASSERT_EQ(IPCChannelPosixTestListener::CONNECTED, listener.status()); - ASSERT_TRUE(channel.HasAcceptedConnection()); - base::ProcessHandle handle2 = SpawnChild("IPCChannelPosixFailConnectionProc", - false); + ASSERT_TRUE(channel->HasAcceptedConnection()); + base::ProcessHandle handle2 = SpawnChild("IPCChannelPosixFailConnectionProc"); ASSERT_TRUE(handle2); SpinRunLoop(TestTimeouts::action_max_timeout()); int exit_code = 0; EXPECT_TRUE(base::WaitForExitCode(handle2, &exit_code)); EXPECT_EQ(exit_code, 0); ASSERT_EQ(IPCChannelPosixTestListener::DENIED, listener.status()); - ASSERT_TRUE(channel.HasAcceptedConnection()); + ASSERT_TRUE(channel->HasAcceptedConnection()); IPC::Message* message = new IPC::Message(0, // routing_id kQuitMessage, // message type IPC::Message::PRIORITY_NORMAL); - channel.Send(message); + channel->Send(message); SpinRunLoop(TestTimeouts::action_timeout()); EXPECT_TRUE(base::WaitForExitCode(handle, &exit_code)); EXPECT_EQ(exit_code, 0); ASSERT_EQ(IPCChannelPosixTestListener::CHANNEL_ERROR, listener.status()); - ASSERT_FALSE(channel.HasAcceptedConnection()); + ASSERT_FALSE(channel->HasAcceptedConnection()); } TEST_F(IPCChannelPosixTest, DoubleServer) { @@ -358,18 +414,21 @@ TEST_F(IPCChannelPosixTest, DoubleServer) { IPCChannelPosixTestListener listener(false); IPCChannelPosixTestListener listener2(false); IPC::ChannelHandle chan_handle(GetConnectionSocketName()); - IPC::Channel channel(chan_handle, IPC::Channel::MODE_SERVER, &listener); - IPC::Channel channel2(chan_handle, IPC::Channel::MODE_SERVER, &listener2); - ASSERT_TRUE(channel.Connect()); - ASSERT_FALSE(channel2.Connect()); + scoped_ptr<IPC::ChannelPosix> channel(new IPC::ChannelPosix( + chan_handle, IPC::Channel::MODE_SERVER, &listener)); + scoped_ptr<IPC::ChannelPosix> channel2(new IPC::ChannelPosix( + chan_handle, IPC::Channel::MODE_SERVER, &listener2)); + ASSERT_TRUE(channel->Connect()); + ASSERT_FALSE(channel2->Connect()); } TEST_F(IPCChannelPosixTest, BadMode) { // Test setting up two servers with a bad mode. IPCChannelPosixTestListener listener(false); IPC::ChannelHandle chan_handle(GetConnectionSocketName()); - IPC::Channel channel(chan_handle, IPC::Channel::MODE_NONE, &listener); - ASSERT_FALSE(channel.Connect()); + scoped_ptr<IPC::ChannelPosix> channel(new IPC::ChannelPosix( + chan_handle, IPC::Channel::MODE_NONE, &listener)); + ASSERT_FALSE(channel->Connect()); } TEST_F(IPCChannelPosixTest, IsNamedServerInitialized) { @@ -379,10 +438,11 @@ TEST_F(IPCChannelPosixTest, IsNamedServerInitialized) { ASSERT_TRUE(base::DeleteFile(base::FilePath(connection_socket_name), false)); ASSERT_FALSE(IPC::Channel::IsNamedServerInitialized( connection_socket_name)); - IPC::Channel channel(chan_handle, IPC::Channel::MODE_NAMED_SERVER, &listener); + scoped_ptr<IPC::ChannelPosix> channel(new IPC::ChannelPosix( + chan_handle, IPC::Channel::MODE_NAMED_SERVER, &listener)); ASSERT_TRUE(IPC::Channel::IsNamedServerInitialized( connection_socket_name)); - channel.Close(); + channel->Close(); ASSERT_FALSE(IPC::Channel::IsNamedServerInitialized( connection_socket_name)); } @@ -393,8 +453,9 @@ MULTIPROCESS_TEST_MAIN(IPCChannelPosixTestConnectionProc) { IPCChannelPosixTestListener listener(true); IPC::ChannelHandle handle(IPCChannelPosixTest::GetConnectionSocketName()); IPCChannelPosixTest::SetUpSocket(&handle, IPC::Channel::MODE_NAMED_CLIENT); - IPC::Channel channel(handle, IPC::Channel::MODE_NAMED_CLIENT, &listener); - EXPECT_TRUE(channel.Connect()); + scoped_ptr<IPC::ChannelPosix> channel(new IPC::ChannelPosix( + handle, IPC::Channel::MODE_NAMED_CLIENT, &listener)); + EXPECT_TRUE(channel->Connect()); IPCChannelPosixTest::SpinRunLoop(TestTimeouts::action_max_timeout()); EXPECT_EQ(IPCChannelPosixTestListener::MESSAGE_RECEIVED, listener.status()); return 0; @@ -406,14 +467,15 @@ MULTIPROCESS_TEST_MAIN(IPCChannelPosixFailConnectionProc) { IPCChannelPosixTestListener listener(false); IPC::ChannelHandle handle(IPCChannelPosixTest::GetConnectionSocketName()); IPCChannelPosixTest::SetUpSocket(&handle, IPC::Channel::MODE_NAMED_CLIENT); - IPC::Channel channel(handle, IPC::Channel::MODE_NAMED_CLIENT, &listener); + scoped_ptr<IPC::ChannelPosix> channel(new IPC::ChannelPosix( + handle, IPC::Channel::MODE_NAMED_CLIENT, &listener)); // In this case connect may succeed or fail depending on if the packet // actually gets sent at sendmsg. Since we never delay on send, we may not // see the error. However even if connect succeeds, eventually we will get an // error back since the channel will be closed when we attempt to read from // it. - bool connected = channel.Connect(); + bool connected = channel->Connect(); if (connected) { IPCChannelPosixTest::SpinRunLoop(TestTimeouts::action_max_timeout()); EXPECT_EQ(IPCChannelPosixTestListener::CHANNEL_ERROR, listener.status()); diff --git a/chromium/ipc/ipc_channel_proxy.cc b/chromium/ipc/ipc_channel_proxy.cc index 18ed3041336..7441c6543b6 100644 --- a/chromium/ipc/ipc_channel_proxy.cc +++ b/chromium/ipc/ipc_channel_proxy.cc @@ -2,52 +2,43 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "ipc/ipc_channel_proxy.h" + #include "base/bind.h" #include "base/compiler_specific.h" -#include "base/debug/trace_event.h" #include "base/location.h" #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" #include "base/single_thread_task_runner.h" #include "base/thread_task_runner_handle.h" -#include "ipc/ipc_channel_proxy.h" #include "ipc/ipc_listener.h" #include "ipc/ipc_logging.h" #include "ipc/ipc_message_macros.h" -#include "ipc/ipc_message_utils.h" +#include "ipc/message_filter.h" +#include "ipc/message_filter_router.h" namespace IPC { //------------------------------------------------------------------------------ -ChannelProxy::MessageFilter::MessageFilter() {} - -void ChannelProxy::MessageFilter::OnFilterAdded(Channel* channel) {} - -void ChannelProxy::MessageFilter::OnFilterRemoved() {} - -void ChannelProxy::MessageFilter::OnChannelConnected(int32 peer_pid) {} - -void ChannelProxy::MessageFilter::OnChannelError() {} - -void ChannelProxy::MessageFilter::OnChannelClosing() {} - -bool ChannelProxy::MessageFilter::OnMessageReceived(const Message& message) { - return false; -} - -ChannelProxy::MessageFilter::~MessageFilter() {} - -//------------------------------------------------------------------------------ - ChannelProxy::Context::Context(Listener* listener, base::SingleThreadTaskRunner* ipc_task_runner) : listener_task_runner_(base::ThreadTaskRunnerHandle::Get()), listener_(listener), ipc_task_runner_(ipc_task_runner), channel_connected_called_(false), + message_filter_router_(new MessageFilterRouter()), peer_pid_(base::kNullProcessId) { DCHECK(ipc_task_runner_.get()); + // The Listener thread where Messages are handled must be a separate thread + // to avoid oversubscribing the IO thread. If you trigger this error, you + // need to either: + // 1) Create the ChannelProxy on a different thread, or + // 2) Just use Channel + // Note, we currently make an exception for a NULL listener. That usage + // basically works, but is outside the intent of ChannelProxy. This support + // will disappear, so please don't rely on it. See crbug.com/364241 + DCHECK(!listener || (ipc_task_runner_.get() != listener_task_runner_.get())); } ChannelProxy::Context::~Context() { @@ -59,26 +50,29 @@ void ChannelProxy::Context::ClearIPCTaskRunner() { void ChannelProxy::Context::CreateChannel(const IPC::ChannelHandle& handle, const Channel::Mode& mode) { - DCHECK(channel_.get() == NULL); + DCHECK(!channel_); channel_id_ = handle.name; - channel_.reset(new Channel(handle, mode, this)); + channel_ = Channel::Create(handle, mode, this); } bool ChannelProxy::Context::TryFilters(const Message& message) { + DCHECK(message_filter_router_); #ifdef IPC_MESSAGE_LOG_ENABLED Logging* logger = Logging::GetInstance(); if (logger->Enabled()) logger->OnPreDispatchMessage(message); #endif - for (size_t i = 0; i < filters_.size(); ++i) { - if (filters_[i]->OnMessageReceived(message)) { + if (message_filter_router_->TryFilters(message)) { + if (message.dispatch_error()) { + listener_task_runner_->PostTask( + FROM_HERE, base::Bind(&Context::OnDispatchBadMessage, this, message)); + } #ifdef IPC_MESSAGE_LOG_ENABLED - if (logger->Enabled()) - logger->OnPostDispatchMessage(message, channel_id_); + if (logger->Enabled()) + logger->OnPostDispatchMessage(message, channel_id_); #endif - return true; - } + return true; } return false; } @@ -93,10 +87,6 @@ bool ChannelProxy::Context::OnMessageReceived(const Message& message) { // Called on the IPC::Channel thread bool ChannelProxy::Context::OnMessageReceivedNoFilter(const Message& message) { - // NOTE: This code relies on the listener's message loop not going away while - // this thread is active. That should be a reasonable assumption, but it - // feels risky. We may want to invent some more indirect way of referring to - // a MessageLoop if this becomes a problem. listener_task_runner_->PostTask( FROM_HERE, base::Bind(&Context::OnDispatchMessage, this, message)); return true; @@ -104,17 +94,15 @@ bool ChannelProxy::Context::OnMessageReceivedNoFilter(const Message& message) { // Called on the IPC::Channel thread void ChannelProxy::Context::OnChannelConnected(int32 peer_pid) { + // We cache off the peer_pid so it can be safely accessed from both threads. + peer_pid_ = channel_->GetPeerPID(); + // Add any pending filters. This avoids a race condition where someone // creates a ChannelProxy, calls AddFilter, and then right after starts the // peer process. The IO thread could receive a message before the task to add // the filter is run on the IO thread. OnAddFilter(); - // We cache off the peer_pid so it can be safely accessed from both threads. - peer_pid_ = channel_->peer_pid(); - for (size_t i = 0; i < filters_.size(); ++i) - filters_[i]->OnChannelConnected(peer_pid); - // See above comment about using listener_task_runner_ here. listener_task_runner_->PostTask( FROM_HERE, base::Bind(&Context::OnDispatchConnected, this)); @@ -151,7 +139,7 @@ void ChannelProxy::Context::OnChannelOpened() { void ChannelProxy::Context::OnChannelClosed() { // It's okay for IPC::ChannelProxy::Close to be called more than once, which // would result in this branch being taken. - if (!channel_.get()) + if (!channel_) return; for (size_t i = 0; i < filters_.size(); ++i) { @@ -160,7 +148,11 @@ void ChannelProxy::Context::OnChannelClosed() { } // We don't need the filters anymore. + message_filter_router_->Clear(); filters_.clear(); + // We don't need the lock, because at this point, the listener thread can't + // access it any more. + pending_filters_.clear(); channel_.reset(); @@ -175,16 +167,24 @@ void ChannelProxy::Context::Clear() { // Called on the IPC::Channel thread void ChannelProxy::Context::OnSendMessage(scoped_ptr<Message> message) { - if (!channel_.get()) { + if (!channel_) { OnChannelClosed(); return; } + if (!channel_->Send(message.release())) OnChannelError(); } // Called on the IPC::Channel thread void ChannelProxy::Context::OnAddFilter() { + // Our OnChannelConnected method has not yet been called, so we can't be + // sure that channel_ is valid yet. When OnChannelConnected *is* called, + // it invokes OnAddFilter, so any pending filter(s) will be added at that + // time. + if (peer_pid_ == base::kNullProcessId) + return; + std::vector<scoped_refptr<MessageFilter> > new_filters; { base::AutoLock auto_lock(pending_filters_lock_); @@ -194,21 +194,34 @@ void ChannelProxy::Context::OnAddFilter() { for (size_t i = 0; i < new_filters.size(); ++i) { filters_.push_back(new_filters[i]); - // If the channel has already been created, then we need to send this - // message so that the filter gets access to the Channel. - if (channel_.get()) - new_filters[i]->OnFilterAdded(channel_.get()); - // Ditto for if the channel has been connected. - if (peer_pid_) - new_filters[i]->OnChannelConnected(peer_pid_); + message_filter_router_->AddFilter(new_filters[i].get()); + + // The channel has already been created and connected, so we need to + // inform the filters right now. + new_filters[i]->OnFilterAdded(channel_.get()); + new_filters[i]->OnChannelConnected(peer_pid_); } } // Called on the IPC::Channel thread void ChannelProxy::Context::OnRemoveFilter(MessageFilter* filter) { - if (!channel_.get()) + if (peer_pid_ == base::kNullProcessId) { + // The channel is not yet connected, so any filters are still pending. + base::AutoLock auto_lock(pending_filters_lock_); + for (size_t i = 0; i < pending_filters_.size(); ++i) { + if (pending_filters_[i].get() == filter) { + filter->OnFilterRemoved(); + pending_filters_.erase(pending_filters_.begin() + i); + return; + } + } + return; + } + if (!channel_) return; // The filters have already been deleted. + message_filter_router_->RemoveFilter(filter); + for (size_t i = 0; i < filters_.size(); ++i) { if (filters_[i].get() == filter) { filter->OnFilterRemoved(); @@ -234,10 +247,10 @@ void ChannelProxy::Context::OnDispatchMessage(const Message& message) { Logging* logger = Logging::GetInstance(); std::string name; logger->GetMessageText(message.type(), &name, &message, NULL); - TRACE_EVENT1("task", "ChannelProxy::Context::OnDispatchMessage", + TRACE_EVENT1("ipc", "ChannelProxy::Context::OnDispatchMessage", "name", name); #else - TRACE_EVENT2("task", "ChannelProxy::Context::OnDispatchMessage", + TRACE_EVENT2("ipc", "ChannelProxy::Context::OnDispatchMessage", "class", IPC_MESSAGE_ID_CLASS(message.type()), "line", IPC_MESSAGE_ID_LINE(message.type())); #endif @@ -258,6 +271,8 @@ void ChannelProxy::Context::OnDispatchMessage(const Message& message) { #endif listener_->OnMessageReceived(message); + if (message.dispatch_error()) + listener_->OnBadMessageReceived(message); #ifdef IPC_MESSAGE_LOG_ENABLED if (logger->Enabled()) @@ -281,15 +296,23 @@ void ChannelProxy::Context::OnDispatchError() { listener_->OnChannelError(); } +// Called on the listener's thread +void ChannelProxy::Context::OnDispatchBadMessage(const Message& message) { + if (listener_) + listener_->OnBadMessageReceived(message); +} + //----------------------------------------------------------------------------- -ChannelProxy::ChannelProxy(const IPC::ChannelHandle& channel_handle, - Channel::Mode mode, - Listener* listener, - base::SingleThreadTaskRunner* ipc_task_runner) - : context_(new Context(listener, ipc_task_runner)), - did_init_(false) { - Init(channel_handle, mode, true); +// static +scoped_ptr<ChannelProxy> ChannelProxy::Create( + const IPC::ChannelHandle& channel_handle, + Channel::Mode mode, + Listener* listener, + base::SingleThreadTaskRunner* ipc_task_runner) { + scoped_ptr<ChannelProxy> channel(new ChannelProxy(listener, ipc_task_runner)); + channel->Init(channel_handle, mode, true); + return channel.Pass(); } ChannelProxy::ChannelProxy(Context* context) @@ -297,6 +320,11 @@ ChannelProxy::ChannelProxy(Context* context) did_init_(false) { } +ChannelProxy::ChannelProxy(Listener* listener, + base::SingleThreadTaskRunner* ipc_task_runner) + : context_(new Context(listener, ipc_task_runner)), did_init_(false) { +} + ChannelProxy::~ChannelProxy() { DCHECK(CalledOnValidThread()); @@ -408,15 +436,6 @@ int ChannelProxy::TakeClientFileDescriptor() { DCHECK(channel) << context_.get()->channel_id_; return channel->TakeClientFileDescriptor(); } - -bool ChannelProxy::GetPeerEuid(uid_t* peer_euid) const { - DCHECK(CalledOnValidThread()); - - Channel* channel = context_.get()->channel_.get(); - // Channel must have been created first. - DCHECK(channel) << context_.get()->channel_id_; - return channel->GetPeerEuid(peer_euid); -} #endif //----------------------------------------------------------------------------- diff --git a/chromium/ipc/ipc_channel_proxy.h b/chromium/ipc/ipc_channel_proxy.h index 1f5ecf450df..2e483acbf4d 100644 --- a/chromium/ipc/ipc_channel_proxy.h +++ b/chromium/ipc/ipc_channel_proxy.h @@ -22,6 +22,8 @@ class SingleThreadTaskRunner; namespace IPC { +class MessageFilter; +class MessageFilterRouter; class SendCallbackHelper; //----------------------------------------------------------------------------- @@ -54,47 +56,6 @@ class SendCallbackHelper; // class IPC_EXPORT ChannelProxy : public Sender, public base::NonThreadSafe { public: - - // A class that receives messages on the thread where the IPC channel is - // running. It can choose to prevent the default action for an IPC message. - class IPC_EXPORT MessageFilter - : public base::RefCountedThreadSafe<MessageFilter> { - public: - MessageFilter(); - - // Called on the background thread to provide the filter with access to the - // channel. Called when the IPC channel is initialized or when AddFilter - // is called if the channel is already initialized. - virtual void OnFilterAdded(Channel* channel); - - // Called on the background thread when the filter has been removed from - // the ChannelProxy and when the Channel is closing. After a filter is - // removed, it will not be called again. - virtual void OnFilterRemoved(); - - // Called to inform the filter that the IPC channel is connected and we - // have received the internal Hello message from the peer. - virtual void OnChannelConnected(int32 peer_pid); - - // Called when there is an error on the channel, typically that the channel - // has been closed. - virtual void OnChannelError(); - - // Called to inform the filter that the IPC channel will be destroyed. - // OnFilterRemoved is called immediately after this. - virtual void OnChannelClosing(); - - // Return true to indicate that the message was handled, or false to let - // the message be handled in the default way. - virtual bool OnMessageReceived(const Message& message); - - protected: - virtual ~MessageFilter(); - - private: - friend class base::RefCountedThreadSafe<MessageFilter>; - }; - // Initializes a channel proxy. The channel_handle and mode parameters are // passed directly to the underlying IPC::Channel. The listener is called on // the thread that creates the ChannelProxy. The filter's OnMessageReceived @@ -103,10 +64,11 @@ class IPC_EXPORT ChannelProxy : public Sender, public base::NonThreadSafe { // on the background thread. Any message not handled by the filter will be // dispatched to the listener. The given task runner correspond to a thread // on which IPC::Channel is created and used (e.g. IO thread). - ChannelProxy(const IPC::ChannelHandle& channel_handle, - Channel::Mode mode, - Listener* listener, - base::SingleThreadTaskRunner* ipc_task_runner); + static scoped_ptr<ChannelProxy> Create( + const IPC::ChannelHandle& channel_handle, + Channel::Mode mode, + Listener* listener, + base::SingleThreadTaskRunner* ipc_task_runner); virtual ~ChannelProxy(); @@ -121,7 +83,7 @@ class IPC_EXPORT ChannelProxy : public Sender, public base::NonThreadSafe { // background thread processes the command to close the channel. It is ok to // call this method multiple times. Redundant calls are ignored. // - // WARNING: The MessageFilter object held by the ChannelProxy is also + // WARNING: MessageFilter objects held by the ChannelProxy is also // released asynchronously, and it may in fact have its final reference // released on the background thread. The caller should be careful to deal // with / allow for this possibility. @@ -148,13 +110,12 @@ class IPC_EXPORT ChannelProxy : public Sender, public base::NonThreadSafe { // Get the process ID for the connected peer. // Returns base::kNullProcessId if the peer is not connected yet. - base::ProcessId peer_pid() const { return context_->peer_pid_; } + base::ProcessId GetPeerPID() const { return context_->peer_pid_; } #if defined(OS_POSIX) && !defined(OS_NACL) // Calls through to the underlying channel's methods. int GetClientFileDescriptor(); int TakeClientFileDescriptor(); - bool GetPeerEuid(uid_t* peer_euid) const; #endif // defined(OS_POSIX) protected: @@ -163,6 +124,9 @@ class IPC_EXPORT ChannelProxy : public Sender, public base::NonThreadSafe { // to the internal state. ChannelProxy(Context* context); + ChannelProxy(Listener* listener, + base::SingleThreadTaskRunner* ipc_task_runner); + // Used internally to hold state that is referenced on the IPC thread. class Context : public base::RefCountedThreadSafe<Context>, public Listener { @@ -219,6 +183,7 @@ class IPC_EXPORT ChannelProxy : public Sender, public base::NonThreadSafe { void AddFilter(MessageFilter* filter); void OnDispatchConnected(); void OnDispatchError(); + void OnDispatchBadMessage(const Message& message); scoped_refptr<base::SingleThreadTaskRunner> listener_task_runner_; Listener* listener_; @@ -226,10 +191,18 @@ class IPC_EXPORT ChannelProxy : public Sender, public base::NonThreadSafe { // List of filters. This is only accessed on the IPC thread. std::vector<scoped_refptr<MessageFilter> > filters_; scoped_refptr<base::SingleThreadTaskRunner> ipc_task_runner_; + + // Note, channel_ may be set on the Listener thread or the IPC thread. + // But once it has been set, it must only be read or cleared on the IPC + // thread. scoped_ptr<Channel> channel_; std::string channel_id_; bool channel_connected_called_; + // Routes a given message to a proper subset of |filters_|, depending + // on which message classes a filter might support. + scoped_ptr<MessageFilterRouter> message_filter_router_; + // Holds filters between the AddFilter call on the listerner thread and the // IPC thread when they're added to filters_. std::vector<scoped_refptr<MessageFilter> > pending_filters_; diff --git a/chromium/ipc/ipc_channel_proxy_unittest.cc b/chromium/ipc/ipc_channel_proxy_unittest.cc new file mode 100644 index 00000000000..81b65ec21c4 --- /dev/null +++ b/chromium/ipc/ipc_channel_proxy_unittest.cc @@ -0,0 +1,441 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "build/build_config.h" + +#include "base/message_loop/message_loop.h" +#include "base/pickle.h" +#include "base/threading/thread.h" +#include "ipc/ipc_message.h" +#include "ipc/ipc_test_base.h" +#include "ipc/message_filter.h" + +// Get basic type definitions. +#define IPC_MESSAGE_IMPL +#include "ipc/ipc_channel_proxy_unittest_messages.h" + +// Generate constructors. +#include "ipc/struct_constructor_macros.h" +#include "ipc/ipc_channel_proxy_unittest_messages.h" + +// Generate destructors. +#include "ipc/struct_destructor_macros.h" +#include "ipc/ipc_channel_proxy_unittest_messages.h" + +// Generate param traits write methods. +#include "ipc/param_traits_write_macros.h" +namespace IPC { +#include "ipc/ipc_channel_proxy_unittest_messages.h" +} // namespace IPC + +// Generate param traits read methods. +#include "ipc/param_traits_read_macros.h" +namespace IPC { +#include "ipc/ipc_channel_proxy_unittest_messages.h" +} // namespace IPC + +// Generate param traits log methods. +#include "ipc/param_traits_log_macros.h" +namespace IPC { +#include "ipc/ipc_channel_proxy_unittest_messages.h" +} // namespace IPC + + +namespace { + +class QuitListener : public IPC::Listener { + public: + QuitListener() : bad_message_received_(false) {} + virtual ~QuitListener() {} + + virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE { + IPC_BEGIN_MESSAGE_MAP(QuitListener, message) + IPC_MESSAGE_HANDLER(WorkerMsg_Quit, OnQuit) + IPC_MESSAGE_HANDLER(TestMsg_BadMessage, OnBadMessage) + IPC_END_MESSAGE_MAP() + return true; + } + + virtual void OnBadMessageReceived(const IPC::Message& message) OVERRIDE { + bad_message_received_ = true; + } + + void OnQuit() { + base::MessageLoop::current()->QuitWhenIdle(); + } + + void OnBadMessage(const BadType& bad_type) { + // Should never be called since IPC wouldn't be deserialized correctly. + CHECK(false); + } + + bool bad_message_received_; +}; + +class ChannelReflectorListener : public IPC::Listener { + public: + ChannelReflectorListener() : channel_(NULL) {} + virtual ~ChannelReflectorListener() {} + + void Init(IPC::Channel* channel) { + DCHECK(!channel_); + channel_ = channel; + } + + virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE { + IPC_BEGIN_MESSAGE_MAP(ChannelReflectorListener, message) + IPC_MESSAGE_HANDLER(TestMsg_Bounce, OnTestBounce) + IPC_MESSAGE_HANDLER(TestMsg_SendBadMessage, OnSendBadMessage) + IPC_MESSAGE_HANDLER(UtilityMsg_Bounce, OnUtilityBounce) + IPC_MESSAGE_HANDLER(WorkerMsg_Bounce, OnBounce) + IPC_MESSAGE_HANDLER(WorkerMsg_Quit, OnQuit) + IPC_END_MESSAGE_MAP() + return true; + } + + void OnTestBounce() { + channel_->Send(new TestMsg_Bounce()); + } + + void OnSendBadMessage() { + channel_->Send(new TestMsg_BadMessage(BadType())); + } + + void OnUtilityBounce() { + channel_->Send(new UtilityMsg_Bounce()); + } + + void OnBounce() { + channel_->Send(new WorkerMsg_Bounce()); + } + + void OnQuit() { + channel_->Send(new WorkerMsg_Quit()); + base::MessageLoop::current()->QuitWhenIdle(); + } + + private: + IPC::Channel* channel_; +}; + +class MessageCountFilter : public IPC::MessageFilter { + public: + enum FilterEvent { + NONE, + FILTER_ADDED, + CHANNEL_CONNECTED, + CHANNEL_ERROR, + CHANNEL_CLOSING, + FILTER_REMOVED + }; + MessageCountFilter() + : messages_received_(0), + supported_message_class_(0), + is_global_filter_(true), + last_filter_event_(NONE), + message_filtering_enabled_(false) {} + + MessageCountFilter(uint32 supported_message_class) + : messages_received_(0), + supported_message_class_(supported_message_class), + is_global_filter_(false), + last_filter_event_(NONE), + message_filtering_enabled_(false) {} + + virtual void OnFilterAdded(IPC::Sender* sender) OVERRIDE { + EXPECT_TRUE(sender); + EXPECT_EQ(NONE, last_filter_event_); + last_filter_event_ = FILTER_ADDED; + } + + virtual void OnChannelConnected(int32_t peer_pid) OVERRIDE { + EXPECT_EQ(FILTER_ADDED, last_filter_event_); + EXPECT_NE(static_cast<int32_t>(base::kNullProcessId), peer_pid); + last_filter_event_ = CHANNEL_CONNECTED; + } + + virtual void OnChannelError() OVERRIDE { + EXPECT_EQ(CHANNEL_CONNECTED, last_filter_event_); + last_filter_event_ = CHANNEL_ERROR; + } + + virtual void OnChannelClosing() OVERRIDE { + // We may or may not have gotten OnChannelError; if not, the last event has + // to be OnChannelConnected. + if (last_filter_event_ != CHANNEL_ERROR) + EXPECT_EQ(CHANNEL_CONNECTED, last_filter_event_); + last_filter_event_ = CHANNEL_CLOSING; + } + + virtual void OnFilterRemoved() OVERRIDE { + // If the channel didn't get a chance to connect, we might see the + // OnFilterRemoved event with no other events preceding it. We still want + // OnFilterRemoved to be called to allow for deleting the Filter. + if (last_filter_event_ != NONE) + EXPECT_EQ(CHANNEL_CLOSING, last_filter_event_); + last_filter_event_ = FILTER_REMOVED; + } + + virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE { + // We should always get the OnFilterAdded and OnChannelConnected events + // prior to any messages. + EXPECT_EQ(CHANNEL_CONNECTED, last_filter_event_); + + if (!is_global_filter_) { + EXPECT_EQ(supported_message_class_, IPC_MESSAGE_CLASS(message)); + } + ++messages_received_; + + if (!message_filtering_enabled_) + return false; + + bool handled = true; + IPC_BEGIN_MESSAGE_MAP(MessageCountFilter, message) + IPC_MESSAGE_HANDLER(TestMsg_BadMessage, OnBadMessage) + IPC_MESSAGE_UNHANDLED(handled = false) + IPC_END_MESSAGE_MAP() + return handled; + } + + void OnBadMessage(const BadType& bad_type) { + // Should never be called since IPC wouldn't be deserialized correctly. + CHECK(false); + } + + virtual bool GetSupportedMessageClasses( + std::vector<uint32>* supported_message_classes) const OVERRIDE { + if (is_global_filter_) + return false; + supported_message_classes->push_back(supported_message_class_); + return true; + } + + void set_message_filtering_enabled(bool enabled) { + message_filtering_enabled_ = enabled; + } + + size_t messages_received() const { return messages_received_; } + FilterEvent last_filter_event() const { return last_filter_event_; } + + private: + virtual ~MessageCountFilter() {} + + size_t messages_received_; + uint32 supported_message_class_; + bool is_global_filter_; + + FilterEvent last_filter_event_; + bool message_filtering_enabled_; +}; + +class IPCChannelProxyTest : public IPCTestBase { + public: + IPCChannelProxyTest() {} + virtual ~IPCChannelProxyTest() {} + + virtual void SetUp() OVERRIDE { + IPCTestBase::SetUp(); + + Init("ChannelProxyClient"); + + thread_.reset(new base::Thread("ChannelProxyTestServerThread")); + base::Thread::Options options; + options.message_loop_type = base::MessageLoop::TYPE_IO; + thread_->StartWithOptions(options); + + listener_.reset(new QuitListener()); + CreateChannelProxy(listener_.get(), thread_->message_loop_proxy().get()); + + ASSERT_TRUE(StartClient()); + } + + virtual void TearDown() { + DestroyChannelProxy(); + thread_.reset(); + listener_.reset(); + IPCTestBase::TearDown(); + } + + void SendQuitMessageAndWaitForIdle() { + sender()->Send(new WorkerMsg_Quit); + base::MessageLoop::current()->Run(); + EXPECT_TRUE(WaitForClientShutdown()); + } + + bool DidListenerGetBadMessage() { + return listener_->bad_message_received_; + } + + private: + scoped_ptr<base::Thread> thread_; + scoped_ptr<QuitListener> listener_; +}; + +TEST_F(IPCChannelProxyTest, MessageClassFilters) { + // Construct a filter per message class. + std::vector<scoped_refptr<MessageCountFilter> > class_filters; + class_filters.push_back(make_scoped_refptr( + new MessageCountFilter(TestMsgStart))); + class_filters.push_back(make_scoped_refptr( + new MessageCountFilter(UtilityMsgStart))); + for (size_t i = 0; i < class_filters.size(); ++i) + channel_proxy()->AddFilter(class_filters[i].get()); + + // Send a message for each class; each filter should receive just one message. + sender()->Send(new TestMsg_Bounce()); + sender()->Send(new UtilityMsg_Bounce()); + + // Send some messages not assigned to a specific or valid message class. + sender()->Send(new WorkerMsg_Bounce); + + // Each filter should have received just the one sent message of the + // corresponding class. + SendQuitMessageAndWaitForIdle(); + for (size_t i = 0; i < class_filters.size(); ++i) + EXPECT_EQ(1U, class_filters[i]->messages_received()); +} + +TEST_F(IPCChannelProxyTest, GlobalAndMessageClassFilters) { + // Add a class and global filter. + scoped_refptr<MessageCountFilter> class_filter( + new MessageCountFilter(TestMsgStart)); + class_filter->set_message_filtering_enabled(false); + channel_proxy()->AddFilter(class_filter.get()); + + scoped_refptr<MessageCountFilter> global_filter(new MessageCountFilter()); + global_filter->set_message_filtering_enabled(false); + channel_proxy()->AddFilter(global_filter.get()); + + // A message of class Test should be seen by both the global filter and + // Test-specific filter. + sender()->Send(new TestMsg_Bounce); + + // A message of a different class should be seen only by the global filter. + sender()->Send(new UtilityMsg_Bounce); + + // Flush all messages. + SendQuitMessageAndWaitForIdle(); + + // The class filter should have received only the class-specific message. + EXPECT_EQ(1U, class_filter->messages_received()); + + // The global filter should have received both messages, as well as the final + // QUIT message. + EXPECT_EQ(3U, global_filter->messages_received()); +} + +TEST_F(IPCChannelProxyTest, FilterRemoval) { + // Add a class and global filter. + scoped_refptr<MessageCountFilter> class_filter( + new MessageCountFilter(TestMsgStart)); + scoped_refptr<MessageCountFilter> global_filter(new MessageCountFilter()); + + // Add and remove both types of filters. + channel_proxy()->AddFilter(class_filter.get()); + channel_proxy()->AddFilter(global_filter.get()); + channel_proxy()->RemoveFilter(global_filter.get()); + channel_proxy()->RemoveFilter(class_filter.get()); + + // Send some messages; they should not be seen by either filter. + sender()->Send(new TestMsg_Bounce); + sender()->Send(new UtilityMsg_Bounce); + + // Ensure that the filters were removed and did not receive any messages. + SendQuitMessageAndWaitForIdle(); + EXPECT_EQ(MessageCountFilter::FILTER_REMOVED, + global_filter->last_filter_event()); + EXPECT_EQ(MessageCountFilter::FILTER_REMOVED, + class_filter->last_filter_event()); + EXPECT_EQ(0U, class_filter->messages_received()); + EXPECT_EQ(0U, global_filter->messages_received()); +} + +// The test that follow trigger DCHECKS in debug build. +#if defined(NDEBUG) && !defined(DCHECK_ALWAYS_ON) + +TEST_F(IPCChannelProxyTest, BadMessageOnListenerThread) { + scoped_refptr<MessageCountFilter> class_filter( + new MessageCountFilter(TestMsgStart)); + class_filter->set_message_filtering_enabled(false); + channel_proxy()->AddFilter(class_filter.get()); + + sender()->Send(new TestMsg_SendBadMessage()); + + SendQuitMessageAndWaitForIdle(); + EXPECT_TRUE(DidListenerGetBadMessage()); +} + +TEST_F(IPCChannelProxyTest, BadMessageOnIPCThread) { + scoped_refptr<MessageCountFilter> class_filter( + new MessageCountFilter(TestMsgStart)); + class_filter->set_message_filtering_enabled(true); + channel_proxy()->AddFilter(class_filter.get()); + + sender()->Send(new TestMsg_SendBadMessage()); + + SendQuitMessageAndWaitForIdle(); + EXPECT_TRUE(DidListenerGetBadMessage()); +} + +class IPCChannelBadMessageTest : public IPCTestBase { + public: + IPCChannelBadMessageTest() {} + virtual ~IPCChannelBadMessageTest() {} + + virtual void SetUp() OVERRIDE { + IPCTestBase::SetUp(); + + Init("ChannelProxyClient"); + + listener_.reset(new QuitListener()); + CreateChannel(listener_.get()); + ASSERT_TRUE(ConnectChannel()); + + ASSERT_TRUE(StartClient()); + } + + virtual void TearDown() { + listener_.reset(); + IPCTestBase::TearDown(); + } + + void SendQuitMessageAndWaitForIdle() { + sender()->Send(new WorkerMsg_Quit); + base::MessageLoop::current()->Run(); + EXPECT_TRUE(WaitForClientShutdown()); + } + + bool DidListenerGetBadMessage() { + return listener_->bad_message_received_; + } + + private: + scoped_ptr<QuitListener> listener_; +}; + +#if !defined(OS_WIN) + // TODO(jam): for some reason this is flaky on win buildbots. +TEST_F(IPCChannelBadMessageTest, BadMessage) { + sender()->Send(new TestMsg_SendBadMessage()); + SendQuitMessageAndWaitForIdle(); + EXPECT_TRUE(DidListenerGetBadMessage()); +} +#endif + +#endif + +MULTIPROCESS_IPC_TEST_CLIENT_MAIN(ChannelProxyClient) { + base::MessageLoopForIO main_message_loop; + ChannelReflectorListener listener; + scoped_ptr<IPC::Channel> channel(IPC::Channel::CreateClient( + IPCTestBase::GetChannelName("ChannelProxyClient"), + &listener)); + CHECK(channel->Connect()); + listener.Init(channel.get()); + + base::MessageLoop::current()->Run(); + return 0; +} + +} // namespace diff --git a/chromium/ipc/ipc_channel_proxy_unittest_messages.h b/chromium/ipc/ipc_channel_proxy_unittest_messages.h new file mode 100644 index 00000000000..0108f1b7c78 --- /dev/null +++ b/chromium/ipc/ipc_channel_proxy_unittest_messages.h @@ -0,0 +1,44 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ipc/ipc_message_macros.h" + +// Singly-included section for enums and custom IPC traits. +#ifndef IPC_CHANNEL_PROXY_UNITTEST_MESSAGES_H_ +#define IPC_CHANNEL_PROXY_UNITTEST_MESSAGES_H_ + +class BadType { + public: + BadType() {} +}; + +namespace IPC { + +template <> +struct ParamTraits<BadType> { + static void Write(Message* m, const BadType& p) {} + static bool Read(const Message* m, PickleIterator* iter, BadType* r) { + return false; + } + static void Log(const BadType& p, std::string* l) {} +}; + +} + +#endif // IPC_CHANNEL_PROXY_UNITTEST_MESSAGES_H_ + + +#define IPC_MESSAGE_START TestMsgStart +IPC_MESSAGE_CONTROL0(TestMsg_Bounce) +IPC_MESSAGE_CONTROL0(TestMsg_SendBadMessage) +IPC_MESSAGE_CONTROL1(TestMsg_BadMessage, BadType) + +#undef IPC_MESSAGE_START +#define IPC_MESSAGE_START UtilityMsgStart +IPC_MESSAGE_CONTROL0(UtilityMsg_Bounce) + +#undef IPC_MESSAGE_START +#define IPC_MESSAGE_START WorkerMsgStart +IPC_MESSAGE_CONTROL0(WorkerMsg_Bounce) +IPC_MESSAGE_CONTROL0(WorkerMsg_Quit) diff --git a/chromium/ipc/ipc_channel_reader.cc b/chromium/ipc/ipc_channel_reader.cc index 2ee7449ea8d..9a3cc3c50bb 100644 --- a/chromium/ipc/ipc_channel_reader.cc +++ b/chromium/ipc/ipc_channel_reader.cc @@ -83,9 +83,10 @@ bool ChannelReader::DispatchInputData(const char* input_data, Logging* logger = Logging::GetInstance(); std::string name; logger->GetMessageText(m.type(), &name, &m, NULL); - TRACE_EVENT1("ipc", "ChannelReader::DispatchInputData", "name", name); + TRACE_EVENT1("ipc,toplevel", "ChannelReader::DispatchInputData", + "name", name); #else - TRACE_EVENT2("ipc", "ChannelReader::DispatchInputData", + TRACE_EVENT2("ipc,toplevel", "ChannelReader::DispatchInputData", "class", IPC_MESSAGE_ID_CLASS(m.type()), "line", IPC_MESSAGE_ID_LINE(m.type())); #endif @@ -94,6 +95,8 @@ bool ChannelReader::DispatchInputData(const char* input_data, HandleInternalMessage(m); else listener_->OnMessageReceived(m); + if (m.dispatch_error()) + listener_->OnBadMessageReceived(m); p = message_tail; } else { // Last message is partial. diff --git a/chromium/ipc/ipc_channel_unittest.cc b/chromium/ipc/ipc_channel_unittest.cc index eea432a15e7..b9665dbed73 100644 --- a/chromium/ipc/ipc_channel_unittest.cc +++ b/chromium/ipc/ipc_channel_unittest.cc @@ -254,12 +254,12 @@ MULTIPROCESS_IPC_TEST_CLIENT_MAIN(GenericClient) { GenericChannelListener listener; // Set up IPC channel. - IPC::Channel channel(IPCTestBase::GetChannelName("GenericClient"), - IPC::Channel::MODE_CLIENT, - &listener); - CHECK(channel.Connect()); - listener.Init(&channel); - Send(&channel, "hello from child"); + scoped_ptr<IPC::Channel> channel(IPC::Channel::CreateClient( + IPCTestBase::GetChannelName("GenericClient"), + &listener)); + CHECK(channel->Connect()); + listener.Init(channel.get()); + Send(channel.get(), "hello from child"); base::MessageLoop::current()->Run(); return 0; diff --git a/chromium/ipc/ipc_channel_win.cc b/chromium/ipc/ipc_channel_win.cc index 8c08500355e..4023097bead 100644 --- a/chromium/ipc/ipc_channel_win.cc +++ b/chromium/ipc/ipc_channel_win.cc @@ -23,17 +23,17 @@ namespace IPC { -Channel::ChannelImpl::State::State(ChannelImpl* channel) : is_pending(false) { +ChannelWin::State::State(ChannelWin* channel) : is_pending(false) { memset(&context.overlapped, 0, sizeof(context.overlapped)); context.handler = channel; } -Channel::ChannelImpl::State::~State() { - COMPILE_ASSERT(!offsetof(Channel::ChannelImpl::State, context), +ChannelWin::State::~State() { + COMPILE_ASSERT(!offsetof(ChannelWin::State, context), starts_with_io_context); } -Channel::ChannelImpl::ChannelImpl(const IPC::ChannelHandle &channel_handle, +ChannelWin::ChannelWin(const IPC::ChannelHandle &channel_handle, Mode mode, Listener* listener) : ChannelReader(listener), input_state_(this), @@ -48,11 +48,11 @@ Channel::ChannelImpl::ChannelImpl(const IPC::ChannelHandle &channel_handle, CreatePipe(channel_handle, mode); } -Channel::ChannelImpl::~ChannelImpl() { +ChannelWin::~ChannelWin() { Close(); } -void Channel::ChannelImpl::Close() { +void ChannelWin::Close() { if (thread_check_.get()) { DCHECK(thread_check_->CalledOnValidThread()); } @@ -80,7 +80,7 @@ void Channel::ChannelImpl::Close() { } } -bool Channel::ChannelImpl::Send(Message* message) { +bool ChannelWin::Send(Message* message) { DCHECK(thread_check_->CalledOnValidThread()); DVLOG(2) << "sending message @" << message << " on channel @" << this << " with type " << message->type() @@ -103,8 +103,12 @@ bool Channel::ChannelImpl::Send(Message* message) { return true; } +base::ProcessId ChannelWin::GetPeerPID() const { + return peer_pid_; +} + // static -bool Channel::ChannelImpl::IsNamedServerInitialized( +bool ChannelWin::IsNamedServerInitialized( const std::string& channel_id) { if (WaitNamedPipe(PipeName(channel_id, NULL).c_str(), 1)) return true; @@ -113,7 +117,7 @@ bool Channel::ChannelImpl::IsNamedServerInitialized( return GetLastError() == ERROR_SEM_TIMEOUT; } -Channel::ChannelImpl::ReadState Channel::ChannelImpl::ReadData( +ChannelWin::ReadState ChannelWin::ReadData( char* buffer, int buffer_len, int* /* bytes_read */) { @@ -145,14 +149,14 @@ Channel::ChannelImpl::ReadState Channel::ChannelImpl::ReadData( return READ_PENDING; } -bool Channel::ChannelImpl::WillDispatchInputMessage(Message* msg) { +bool ChannelWin::WillDispatchInputMessage(Message* msg) { // Make sure we get a hello when client validation is required. if (validate_client_) return IsHelloMessage(*msg); return true; } -void Channel::ChannelImpl::HandleInternalMessage(const Message& msg) { +void ChannelWin::HandleInternalMessage(const Message& msg) { DCHECK_EQ(msg.type(), static_cast<unsigned>(Channel::HELLO_MESSAGE_TYPE)); // The hello message contains one parameter containing the PID. PickleIterator it(msg); @@ -177,13 +181,13 @@ void Channel::ChannelImpl::HandleInternalMessage(const Message& msg) { listener()->OnChannelConnected(claimed_pid); } -bool Channel::ChannelImpl::DidEmptyInputBuffers() { +bool ChannelWin::DidEmptyInputBuffers() { // We don't need to do anything here. return true; } // static -const string16 Channel::ChannelImpl::PipeName( +const base::string16 ChannelWin::PipeName( const std::string& channel_id, int32* secret) { std::string name("\\\\.\\pipe\\chrome."); @@ -192,19 +196,19 @@ const string16 Channel::ChannelImpl::PipeName( if (index != std::string::npos) { if (secret) // Retrieve the secret if asked for. base::StringToInt(channel_id.substr(index + 1), secret); - return ASCIIToWide(name.append(channel_id.substr(0, index - 1))); + return base::ASCIIToWide(name.append(channel_id.substr(0, index - 1))); } // This case is here to support predictable named pipes in tests. if (secret) *secret = 0; - return ASCIIToWide(name.append(channel_id)); + return base::ASCIIToWide(name.append(channel_id)); } -bool Channel::ChannelImpl::CreatePipe(const IPC::ChannelHandle &channel_handle, +bool ChannelWin::CreatePipe(const IPC::ChannelHandle &channel_handle, Mode mode) { DCHECK_EQ(INVALID_HANDLE_VALUE, pipe_); - string16 pipe_name; + base::string16 pipe_name; // If we already have a valid pipe for channel just copy it. if (channel_handle.pipe.handle) { DCHECK(channel_handle.name.empty()); @@ -286,7 +290,7 @@ bool Channel::ChannelImpl::CreatePipe(const IPC::ChannelHandle &channel_handle, return true; } -bool Channel::ChannelImpl::Connect() { +bool ChannelWin::Connect() { DLOG_IF(WARNING, thread_check_.get()) << "Connect called more than once"; if (!thread_check_.get()) @@ -307,7 +311,7 @@ bool Channel::ChannelImpl::Connect() { // initialization signal. base::MessageLoopForIO::current()->PostTask( FROM_HERE, - base::Bind(&Channel::ChannelImpl::OnIOCompleted, + base::Bind(&ChannelWin::OnIOCompleted, weak_factory_.GetWeakPtr(), &input_state_.context, 0, @@ -319,7 +323,7 @@ bool Channel::ChannelImpl::Connect() { return true; } -bool Channel::ChannelImpl::ProcessConnection() { +bool ChannelWin::ProcessConnection() { DCHECK(thread_check_->CalledOnValidThread()); if (input_state_.is_pending) input_state_.is_pending = false; @@ -356,7 +360,7 @@ bool Channel::ChannelImpl::ProcessConnection() { return true; } -bool Channel::ChannelImpl::ProcessOutgoingMessages( +bool ChannelWin::ProcessOutgoingMessages( base::MessageLoopForIO::IOContext* context, DWORD bytes_written) { DCHECK(!waiting_connect_); // Why are we trying to send messages if there's @@ -372,7 +376,7 @@ bool Channel::ChannelImpl::ProcessOutgoingMessages( return false; } // Message was sent. - DCHECK(!output_queue_.empty()); + CHECK(!output_queue_.empty()); Message* m = output_queue_.front(); output_queue_.pop(); delete m; @@ -413,7 +417,7 @@ bool Channel::ChannelImpl::ProcessOutgoingMessages( return true; } -void Channel::ChannelImpl::OnIOCompleted( +void ChannelWin::OnIOCompleted( base::MessageLoopForIO::IOContext* context, DWORD bytes_transfered, DWORD error) { @@ -463,36 +467,18 @@ void Channel::ChannelImpl::OnIOCompleted( } //------------------------------------------------------------------------------ -// Channel's methods simply call through to ChannelImpl. -Channel::Channel(const IPC::ChannelHandle &channel_handle, Mode mode, - Listener* listener) - : channel_impl_(new ChannelImpl(channel_handle, mode, listener)) { -} - -Channel::~Channel() { - delete channel_impl_; -} - -bool Channel::Connect() { - return channel_impl_->Connect(); -} - -void Channel::Close() { - if (channel_impl_) - channel_impl_->Close(); -} +// Channel's methods -base::ProcessId Channel::peer_pid() const { - return channel_impl_->peer_pid(); -} - -bool Channel::Send(Message* message) { - return channel_impl_->Send(message); +// static +scoped_ptr<Channel> Channel::Create( + const IPC::ChannelHandle &channel_handle, Mode mode, Listener* listener) { + return scoped_ptr<Channel>( + new ChannelWin(channel_handle, mode, listener)); } // static bool Channel::IsNamedServerInitialized(const std::string& channel_id) { - return ChannelImpl::IsNamedServerInitialized(channel_id); + return ChannelWin::IsNamedServerInitialized(channel_id); } // static diff --git a/chromium/ipc/ipc_channel_win.h b/chromium/ipc/ipc_channel_win.h index a544f8be4bb..29042bf1ca3 100644 --- a/chromium/ipc/ipc_channel_win.h +++ b/chromium/ipc/ipc_channel_win.h @@ -21,18 +21,23 @@ class ThreadChecker; namespace IPC { -class Channel::ChannelImpl : public internal::ChannelReader, - public base::MessageLoopForIO::IOHandler { +class ChannelWin : public Channel, + public internal::ChannelReader, + public base::MessageLoopForIO::IOHandler { public: // Mirror methods of Channel, see ipc_channel.h for description. - ChannelImpl(const IPC::ChannelHandle &channel_handle, Mode mode, - Listener* listener); - ~ChannelImpl(); - bool Connect(); - void Close(); - bool Send(Message* message); + ChannelWin(const IPC::ChannelHandle &channel_handle, Mode mode, + Listener* listener); + ~ChannelWin(); + + // Channel implementation + virtual bool Connect() OVERRIDE; + virtual void Close() OVERRIDE; + virtual bool Send(Message* message) OVERRIDE; + virtual base::ProcessId GetPeerPID() const OVERRIDE; + static bool IsNamedServerInitialized(const std::string& channel_id); - base::ProcessId peer_pid() const { return peer_pid_; } + private: // ChannelReader implementation. @@ -43,8 +48,8 @@ class Channel::ChannelImpl : public internal::ChannelReader, bool DidEmptyInputBuffers() OVERRIDE; virtual void HandleInternalMessage(const Message& msg) OVERRIDE; - static const string16 PipeName(const std::string& channel_id, - int32* secret); + static const base::string16 PipeName(const std::string& channel_id, + int32* secret); bool CreatePipe(const IPC::ChannelHandle &channel_handle, Mode mode); bool ProcessConnection(); @@ -58,7 +63,7 @@ class Channel::ChannelImpl : public internal::ChannelReader, private: struct State { - explicit State(ChannelImpl* channel); + explicit State(ChannelWin* channel); ~State(); base::MessageLoopForIO::IOContext context; bool is_pending; @@ -94,11 +99,11 @@ class Channel::ChannelImpl : public internal::ChannelReader, int32 client_secret_; - base::WeakPtrFactory<ChannelImpl> weak_factory_; + base::WeakPtrFactory<ChannelWin> weak_factory_; scoped_ptr<base::ThreadChecker> thread_check_; - DISALLOW_COPY_AND_ASSIGN(ChannelImpl); + DISALLOW_COPY_AND_ASSIGN(ChannelWin); }; } // namespace IPC diff --git a/chromium/ipc/ipc_forwarding_message_filter.cc b/chromium/ipc/ipc_forwarding_message_filter.cc index 342aeb718dc..9857bdf67dc 100644 --- a/chromium/ipc/ipc_forwarding_message_filter.cc +++ b/chromium/ipc/ipc_forwarding_message_filter.cc @@ -6,6 +6,7 @@ #include "base/bind.h" #include "base/location.h" +#include "ipc/ipc_message.h" namespace IPC { diff --git a/chromium/ipc/ipc_forwarding_message_filter.h b/chromium/ipc/ipc_forwarding_message_filter.h index 919a44d7599..597fc6090ee 100644 --- a/chromium/ipc/ipc_forwarding_message_filter.h +++ b/chromium/ipc/ipc_forwarding_message_filter.h @@ -12,7 +12,7 @@ #include "base/callback_forward.h" #include "base/synchronization/lock.h" #include "base/task_runner.h" -#include "ipc/ipc_channel_proxy.h" +#include "ipc/message_filter.h" namespace IPC { @@ -23,7 +23,7 @@ namespace IPC { // // The user of this class implements ForwardingMessageFilter::Client, // which will receive the intercepted messages, on the specified target thread. -class IPC_EXPORT ForwardingMessageFilter : public ChannelProxy::MessageFilter { +class IPC_EXPORT ForwardingMessageFilter : public MessageFilter { public: // The handler is invoked on the thread associated with @@ -44,11 +44,10 @@ class IPC_EXPORT ForwardingMessageFilter : public ChannelProxy::MessageFilter { void AddRoute(int routing_id, const Handler& handler); void RemoveRoute(int routing_id); - // ChannelProxy::MessageFilter methods: + // MessageFilter methods: virtual bool OnMessageReceived(const Message& message) OVERRIDE; private: - friend class ChannelProxy::MessageFilter; virtual ~ForwardingMessageFilter(); std::set<int> message_ids_to_filter_; diff --git a/chromium/ipc/ipc_fuzzing_tests.cc b/chromium/ipc/ipc_fuzzing_tests.cc index 3d2d497c5f8..4abcc4c6254 100644 --- a/chromium/ipc/ipc_fuzzing_tests.cc +++ b/chromium/ipc/ipc_fuzzing_tests.cc @@ -247,11 +247,11 @@ class FuzzerClientListener : public SimpleListener { MULTIPROCESS_IPC_TEST_CLIENT_MAIN(FuzzServerClient) { base::MessageLoopForIO main_message_loop; FuzzerServerListener listener; - IPC::Channel channel(IPCTestBase::GetChannelName("FuzzServerClient"), - IPC::Channel::MODE_CLIENT, - &listener); - CHECK(channel.Connect()); - listener.Init(&channel); + scoped_ptr<IPC::Channel> channel(IPC::Channel::CreateClient( + IPCTestBase::GetChannelName("FuzzServerClient"), + &listener)); + CHECK(channel->Connect()); + listener.Init(channel.get()); base::MessageLoop::current()->Run(); return 0; } @@ -345,69 +345,4 @@ TEST_F(IPCFuzzingTest, MsgBadPayloadArgs) { DestroyChannel(); } -// This class is for testing the IPC_BEGIN_MESSAGE_MAP_EX macros. -class ServerMacroExTest { - public: - ServerMacroExTest() : unhandled_msgs_(0) { - } - - virtual ~ServerMacroExTest() { - } - - virtual bool OnMessageReceived(const IPC::Message& msg) { - bool msg_is_ok = false; - IPC_BEGIN_MESSAGE_MAP_EX(ServerMacroExTest, msg, msg_is_ok) - IPC_MESSAGE_HANDLER(MsgClassIS, OnMsgClassISMessage) - IPC_MESSAGE_HANDLER(MsgClassSI, OnMsgClassSIMessage) - IPC_MESSAGE_UNHANDLED(++unhandled_msgs_) - IPC_END_MESSAGE_MAP_EX() - return msg_is_ok; - } - - int unhandled_msgs() const { - return unhandled_msgs_; - } - - private: - void OnMsgClassISMessage(int value, const std::wstring& text) { - } - void OnMsgClassSIMessage(const std::wstring& text, int value) { - } - - int unhandled_msgs_; - - DISALLOW_COPY_AND_ASSIGN(ServerMacroExTest); -}; - -TEST_F(IPCFuzzingTest, MsgMapExMacro) { - IPC::Message* msg = NULL; - ServerMacroExTest server; - - // Test the regular messages. - msg = new MsgClassIS(3, L"text3"); - EXPECT_TRUE(server.OnMessageReceived(*msg)); - delete msg; - msg = new MsgClassSI(L"text2", 2); - EXPECT_TRUE(server.OnMessageReceived(*msg)); - delete msg; - -#if defined(NDEBUG) && !defined(DCHECK_ALWAYS_ON) - // Test a bad message. - msg = new IPC::Message(MSG_ROUTING_CONTROL, MsgClassSI::ID, - IPC::Message::PRIORITY_NORMAL); - msg->WriteInt(2); - EXPECT_FALSE(server.OnMessageReceived(*msg)); - delete msg; - - msg = new IPC::Message(MSG_ROUTING_CONTROL, MsgClassIS::ID, - IPC::Message::PRIORITY_NORMAL); - msg->WriteInt(0x64); - msg->WriteInt(0x32); - EXPECT_FALSE(server.OnMessageReceived(*msg)); - delete msg; - - EXPECT_EQ(0, server.unhandled_msgs()); -#endif -} - } // namespace diff --git a/chromium/ipc/ipc_listener.h b/chromium/ipc/ipc_listener.h index 9189eec7d11..733bc46d7c8 100644 --- a/chromium/ipc/ipc_listener.h +++ b/chromium/ipc/ipc_listener.h @@ -28,6 +28,9 @@ class IPC_EXPORT Listener { // This method is not called when a channel is closed normally. virtual void OnChannelError() {} + // Called when a message's deserialization failed. + virtual void OnBadMessageReceived(const Message& message) {} + #if defined(OS_POSIX) // Called on the server side when a channel that listens for connections // denies an attempt to connect. diff --git a/chromium/ipc/ipc_message.cc b/chromium/ipc/ipc_message.cc index cf3a65e077f..1ac4d6e3026 100644 --- a/chromium/ipc/ipc_message.cc +++ b/chromium/ipc/ipc_message.cc @@ -4,7 +4,7 @@ #include "ipc/ipc_message.h" -#include "base/atomicops.h" +#include "base/atomic_sequence_num.h" #include "base/logging.h" #include "build/build_config.h" @@ -14,7 +14,7 @@ namespace { -base::subtle::Atomic32 g_ref_num = 0; +base::StaticAtomicSequenceNumber g_ref_num; // Create a reference number for identifying IPC messages in traces. The return // values has the reference number stored in the upper 24 bits, leaving the low @@ -22,7 +22,7 @@ base::subtle::Atomic32 g_ref_num = 0; inline uint32 GetRefNumUpper24() { base::debug::TraceLog* trace_log = base::debug::TraceLog::GetInstance(); int32 pid = trace_log ? trace_log->process_id() : 0; - int32 count = base::subtle::NoBarrier_AtomicIncrement(&g_ref_num, 1); + int32 count = g_ref_num.GetNext(); // The 24 bit hash is composed of 14 bits of the count and 10 bits of the // Process ID. With the current trace event buffer cap, the 14-bit count did // not appear to wrap during a trace. Note that it is not a big deal if @@ -47,7 +47,7 @@ Message::Message() header()->num_fds = 0; header()->pad = 0; #endif - InitLoggingVariables(); + Init(); } Message::Message(int32 routing_id, uint32 type, PriorityValue priority) @@ -60,21 +60,22 @@ Message::Message(int32 routing_id, uint32 type, PriorityValue priority) header()->num_fds = 0; header()->pad = 0; #endif - InitLoggingVariables(); + Init(); } Message::Message(const char* data, int data_len) : Pickle(data, data_len) { - InitLoggingVariables(); + Init(); } Message::Message(const Message& other) : Pickle(other) { - InitLoggingVariables(); + Init(); #if defined(OS_POSIX) file_descriptor_set_ = other.file_descriptor_set_; #endif } -void Message::InitLoggingVariables() { +void Message::Init() { + dispatch_error_ = false; #ifdef IPC_MESSAGE_LOG_ENABLED received_time_ = 0; dont_log_ = false; diff --git a/chromium/ipc/ipc_message.h b/chromium/ipc/ipc_message.h index 42090166b38..fc37d72f284 100644 --- a/chromium/ipc/ipc_message.h +++ b/chromium/ipc/ipc_message.h @@ -121,6 +121,14 @@ class IPC_EXPORT Message : public Pickle { return (header()->flags & PUMPING_MSGS_BIT) != 0; } + void set_dispatch_error() const { + dispatch_error_ = true; + } + + bool dispatch_error() const { + return dispatch_error_; + } + uint32 type() const { return header()->type; } @@ -141,31 +149,17 @@ class IPC_EXPORT Message : public Pickle { // call. void SetHeaderValues(int32 routing, uint32 type, uint32 flags); - template<class T, class S> - static bool Dispatch(const Message* msg, T* obj, S* sender, + template<class T, class S, class P> + static bool Dispatch(const Message* msg, T* obj, S* sender, P* parameter, void (T::*func)()) { (obj->*func)(); return true; } - template<class T, class S> - static bool Dispatch(const Message* msg, T* obj, S* sender, - void (T::*func)() const) { - (obj->*func)(); - return true; - } - - template<class T, class S> - static bool Dispatch(const Message* msg, T* obj, S* sender, - void (T::*func)(const Message&)) { - (obj->*func)(*msg); - return true; - } - - template<class T, class S> - static bool Dispatch(const Message* msg, T* obj, S* sender, - void (T::*func)(const Message&) const) { - (obj->*func)(*msg); + template<class T, class S, class P> + static bool Dispatch(const Message* msg, T* obj, S* sender, P* parameter, + void (T::*func)(P*)) { + (obj->*func)(parameter); return true; } @@ -217,15 +211,20 @@ class IPC_EXPORT Message : public Pickle { // Called to trace when message is sent. void TraceMessageBegin() { - TRACE_EVENT_FLOW_BEGIN0("ipc", "IPC", header()->flags); + TRACE_EVENT_FLOW_BEGIN0(TRACE_DISABLED_BY_DEFAULT("ipc.flow"), "IPC", + header()->flags); } // Called to trace when message is received. void TraceMessageEnd() { - TRACE_EVENT_FLOW_END0("ipc", "IPC", header()->flags); + TRACE_EVENT_FLOW_END0(TRACE_DISABLED_BY_DEFAULT("ipc.flow"), "IPC", + header()->flags); } protected: friend class Channel; + friend class ChannelNacl; + friend class ChannelPosix; + friend class ChannelWin; friend class MessageReplyDeserializer; friend class SyncMessage; @@ -248,7 +247,10 @@ class IPC_EXPORT Message : public Pickle { return headerT<Header>(); } - void InitLoggingVariables(); + void Init(); + + // Used internally to support IPC::Listener::OnBadMessageReceived. + mutable bool dispatch_error_; #if defined(OS_POSIX) // The set of file descriptors associated with this message. diff --git a/chromium/ipc/ipc_message_macros.h b/chromium/ipc/ipc_message_macros.h index c4e250cf9e6..8be5c532bf0 100644 --- a/chromium/ipc/ipc_message_macros.h +++ b/chromium/ipc/ipc_message_macros.h @@ -113,8 +113,8 @@ // class_some_other_class; // Another incomplete class declaration // #endif // SOME_GUARD_MACRO // #ifdef IPC_MESSAGE_IMPL -// #inlcude "path/to/some_class.h" // Full class declaration -// #inlcude "path/to/some_other_class.h" // Full class declaration +// #include "path/to/some_class.h" // Full class declaration +// #include "path/to/some_other_class.h" // Full class declaration // #endif // IPC_MESSAGE_IMPL // (.. IPC macros using some_class and some_other_class ...) // @@ -434,9 +434,12 @@ // The following macros define the common set of methods provided by ASYNC // message classes. +// This macro is for all the async IPCs that don't pass an extra parameter using +// IPC_BEGIN_MESSAGE_MAP_WITH_PARAM. #define IPC_ASYNC_MESSAGE_METHODS_GENERIC \ - template<class T, class S, class Method> \ - static bool Dispatch(const Message* msg, T* obj, S* sender, Method func) { \ + template<class T, class S, class P, class Method> \ + static bool Dispatch(const Message* msg, T* obj, S* sender, P* parameter, \ + Method func) { \ Schema::Param p; \ if (Read(msg, &p)) { \ DispatchToMethod(obj, func, p); \ @@ -444,124 +447,86 @@ } \ return false; \ } + +// The following macros are for for async IPCs which have a dispatcher with an +// extra parameter specified using IPC_BEGIN_MESSAGE_MAP_WITH_PARAM. #define IPC_ASYNC_MESSAGE_METHODS_1 \ IPC_ASYNC_MESSAGE_METHODS_GENERIC \ - template<class T, class S, typename TA> \ - static bool Dispatch(const Message* msg, T* obj, S* sender, \ - void (T::*func)(const Message&, TA)) { \ + template<class T, class S, class P, typename TA> \ + static bool Dispatch(const Message* msg, T* obj, S* sender, P* parameter, \ + void (T::*func)(P*, TA)) { \ Schema::Param p; \ if (Read(msg, &p)) { \ - (obj->*func)(*msg, p.a); \ + (obj->*func)(parameter, p.a); \ return true; \ } \ return false; \ } #define IPC_ASYNC_MESSAGE_METHODS_2 \ IPC_ASYNC_MESSAGE_METHODS_GENERIC \ - template<class T, class S, typename TA, typename TB> \ - static bool Dispatch(const Message* msg, T* obj, S* sender, \ - void (T::*func)(const Message&, TA, TB)) { \ + template<class T, class S, class P, typename TA, typename TB> \ + static bool Dispatch(const Message* msg, T* obj, S* sender, P* parameter, \ + void (T::*func)(P*, TA, TB)) { \ Schema::Param p; \ if (Read(msg, &p)) { \ - (obj->*func)(*msg, p.a, p.b); \ + (obj->*func)(parameter, p.a, p.b); \ return true; \ } \ return false; \ - } \ - template<typename TA, typename TB> \ - static bool Read(const IPC::Message* msg, TA* a, TB* b) { \ - Schema::Param p; \ - if (!Read(msg, &p)) \ - return false; \ - *a = p.a; \ - *b = p.b; \ - return true; \ } #define IPC_ASYNC_MESSAGE_METHODS_3 \ IPC_ASYNC_MESSAGE_METHODS_GENERIC \ - template<class T, class S, typename TA, typename TB, typename TC> \ - static bool Dispatch(const Message* msg, T* obj, S* sender, \ - void (T::*func)(const Message&, TA, TB, TC)) { \ + template<class T, class S, class P, typename TA, typename TB, typename TC> \ + static bool Dispatch(const Message* msg, T* obj, S* sender, P* parameter, \ + void (T::*func)(P*, TA, TB, TC)) { \ Schema::Param p; \ if (Read(msg, &p)) { \ - (obj->*func)(*msg, p.a, p.b, p.c); \ + (obj->*func)(parameter, p.a, p.b, p.c); \ return true; \ } \ return false; \ - } \ - template<typename TA, typename TB, typename TC> \ - static bool Read(const IPC::Message* msg, TA* a, TB* b, TC* c) { \ - Schema::Param p; \ - if (!Read(msg, &p)) \ - return false; \ - *a = p.a; \ - *b = p.b; \ - *c = p.c; \ - return true; \ } #define IPC_ASYNC_MESSAGE_METHODS_4 \ IPC_ASYNC_MESSAGE_METHODS_GENERIC \ - template<class T, class S, typename TA, typename TB, typename TC, \ + template<class T, class S, class P, typename TA, typename TB, typename TC, \ typename TD> \ - static bool Dispatch(const Message* msg, T* obj, S* sender, \ - void (T::*func)(const Message&, TA, TB, TC, TD)) { \ + static bool Dispatch(const Message* msg, T* obj, S* sender, P* parameter, \ + void (T::*func)(P*, TA, TB, TC, TD)) { \ Schema::Param p; \ if (Read(msg, &p)) { \ - (obj->*func)(*msg, p.a, p.b, p.c, p.d); \ + (obj->*func)(parameter, p.a, p.b, p.c, p.d); \ return true; \ } \ return false; \ - } \ - template<typename TA, typename TB, typename TC, typename TD> \ - static bool Read(const IPC::Message* msg, TA* a, TB* b, TC* c, TD* d) { \ - Schema::Param p; \ - if (!Read(msg, &p)) \ - return false; \ - *a = p.a; \ - *b = p.b; \ - *c = p.c; \ - *d = p.d; \ - return true; \ } #define IPC_ASYNC_MESSAGE_METHODS_5 \ IPC_ASYNC_MESSAGE_METHODS_GENERIC \ - template<class T, class S, typename TA, typename TB, typename TC, \ + template<class T, class S, class P, typename TA, typename TB, typename TC, \ typename TD, typename TE> \ - static bool Dispatch(const Message* msg, T* obj, S* sender, \ - void (T::*func)(const Message&, TA, TB, TC, TD, TE)) { \ + static bool Dispatch(const Message* msg, T* obj, S* sender, P* parameter, \ + void (T::*func)(P*, TA, TB, TC, TD, TE)) { \ Schema::Param p; \ if (Read(msg, &p)) { \ - (obj->*func)(*msg, p.a, p.b, p.c, p.d, p.e); \ + (obj->*func)(parameter, p.a, p.b, p.c, p.d, p.e); \ return true; \ } \ return false; \ - } \ - template<typename TA, typename TB, typename TC, typename TD, typename TE> \ - static bool Read(const IPC::Message* msg, TA* a, TB* b, TC* c, TD* d, \ - TE* e) { \ - Schema::Param p; \ - if (!Read(msg, &p)) \ - return false; \ - *a = p.a; \ - *b = p.b; \ - *c = p.c; \ - *d = p.d; \ - *e = p.e; \ - return true; \ } // The following macros define the common set of methods provided by SYNC // message classes. #define IPC_SYNC_MESSAGE_METHODS_GENERIC \ - template<class T, class S, class Method> \ - static bool Dispatch(const Message* msg, T* obj, S* sender, Method func) { \ + template<class T, class S, class P, class Method> \ + static bool Dispatch(const Message* msg, T* obj, S* sender, P* parameter, \ + Method func) { \ Schema::SendParam send_params; \ bool ok = ReadSendParam(msg, &send_params); \ return Schema::DispatchWithSendParams(ok, send_params, msg, obj, sender, \ func); \ } \ - template<class T, class Method> \ - static bool DispatchDelayReply(const Message* msg, T* obj, Method func) { \ + template<class T, class P, class Method> \ + static bool DispatchDelayReply(const Message* msg, T* obj, P* parameter, \ + Method func) { \ Schema::SendParam send_params; \ bool ok = ReadSendParam(msg, &send_params); \ return Schema::DispatchDelayReplyWithSendParams(ok, send_params, msg, \ @@ -911,45 +876,48 @@ #define IPC_MESSAGE_ID_CLASS(id) ((id) >> 16) #define IPC_MESSAGE_ID_LINE(id) ((id) & 0xffff) -// Message crackers and handlers. -// Prefer to use the IPC_BEGIN_MESSAGE_MAP_EX to the older macros since they -// allow you to detect when a message could not be de-serialized. Usage: +// Message crackers and handlers. Usage: // // bool MyClass::OnMessageReceived(const IPC::Message& msg) { // bool handled = true; -// bool msg_is_good = false; -// IPC_BEGIN_MESSAGE_MAP_EX(MyClass, msg, msg_is_good) +// IPC_BEGIN_MESSAGE_MAP(MyClass, msg) // IPC_MESSAGE_HANDLER(MsgClassOne, OnMsgClassOne) // ...more handlers here ... // IPC_MESSAGE_HANDLER(MsgClassTen, OnMsgClassTen) // IPC_MESSAGE_UNHANDLED(handled = false) -// IPC_END_MESSAGE_MAP_EX() -// if (!msg_is_good) { -// // Signal error here or terminate offending process. -// } +// IPC_END_MESSAGE_MAP() // return handled; // } -#define IPC_BEGIN_MESSAGE_MAP_EX(class_name, msg, msg_is_ok) \ - { \ - typedef class_name _IpcMessageHandlerClass; \ - const IPC::Message& ipc_message__ = msg; \ - bool& msg_is_ok__ = msg_is_ok; \ - switch (ipc_message__.type()) { \ - #define IPC_BEGIN_MESSAGE_MAP(class_name, msg) \ { \ typedef class_name _IpcMessageHandlerClass; \ + void* param__ = NULL; \ const IPC::Message& ipc_message__ = msg; \ - bool msg_is_ok__ = true; \ - switch (ipc_message__.type()) { \ + switch (ipc_message__.type()) { + +// gcc gives the following error now when using decltype so type typeof there: +// error: identifier 'decltype' will become a keyword in C++0x [-Werror=c++0x-compat] +#if defined(OS_WIN) +#define IPC_DECLTYPE decltype +#else +#define IPC_DECLTYPE typeof +#endif + +#define IPC_BEGIN_MESSAGE_MAP_WITH_PARAM(class_name, msg, param) \ + { \ + typedef class_name _IpcMessageHandlerClass; \ + IPC_DECLTYPE(param) param__ = param; \ + const IPC::Message& ipc_message__ = msg; \ + switch (ipc_message__.type()) { #define IPC_MESSAGE_FORWARD(msg_class, obj, member_func) \ case msg_class::ID: { \ TRACK_RUN_IN_IPC_HANDLER(member_func); \ - msg_is_ok__ = msg_class::Dispatch(&ipc_message__, obj, this, \ - &member_func); \ + if (!msg_class::Dispatch(&ipc_message__, obj, this, param__, \ + &member_func)) \ + ipc_message__.set_dispatch_error(); \ } \ break; @@ -959,8 +927,9 @@ #define IPC_MESSAGE_FORWARD_DELAY_REPLY(msg_class, obj, member_func) \ case msg_class::ID: { \ TRACK_RUN_IN_IPC_HANDLER(member_func); \ - msg_is_ok__ = msg_class::DispatchDelayReply(&ipc_message__, obj, \ - &member_func); \ + if (!msg_class::DispatchDelayReply(&ipc_message__, obj, param__, \ + &member_func)) \ + ipc_message__.set_dispatch_error(); \ } \ break; @@ -997,11 +966,6 @@ #define IPC_END_MESSAGE_MAP() \ } \ - DCHECK(msg_is_ok__); \ -} - -#define IPC_END_MESSAGE_MAP_EX() \ - } \ } // This corresponds to an enum value from IPCMessageStart. diff --git a/chromium/ipc/ipc_message_start.h b/chromium/ipc/ipc_message_start.h index 7ce2bdd8542..8d813a28c63 100644 --- a/chromium/ipc/ipc_message_start.h +++ b/chromium/ipc/ipc_message_start.h @@ -48,7 +48,7 @@ enum IPCMessageStart { DesktopNotificationMsgStart, GeolocationMsgStart, AudioMsgStart, - MIDIMsgStart, + MidiMsgStart, ChromeMsgStart, DragMsgStart, PrintMsgStart, @@ -66,6 +66,7 @@ enum IPCMessageStart { GamepadMsgStart, ShellMsgStart, AccessibilityMsgStart, + PrefetchMsgStart, PrerenderMsgStart, ChromotingMsgStart, OldBrowserPluginMsgStart, @@ -92,6 +93,20 @@ enum IPCMessageStart { EncryptedMediaMsgStart, ServiceWorkerMsgStart, MessagePortMsgStart, + EmbeddedWorkerMsgStart, + EmbeddedWorkerContextMsgStart, + CastMsgStart, + CdmMsgStart, + ScreenOrientationMsgStart, + MediaStreamTrackMetricsHostMsgStart, + ChromeExtensionMsgStart, + MojoMsgStart, + TranslateMsgStart, + PushMessagingMsgStart, + GinJavaBridgeMsgStart, + BatteryStatusMsgStart, + UtilityPrintingMsgStart, + AecDumpMsgStart, LastIPCMsgStart // Must come last. }; diff --git a/chromium/ipc/ipc_message_unittest.cc b/chromium/ipc/ipc_message_unittest.cc index 971314a290c..5b3a78d3273 100644 --- a/chromium/ipc/ipc_message_unittest.cc +++ b/chromium/ipc/ipc_message_unittest.cc @@ -11,6 +11,19 @@ #include "ipc/ipc_message_utils.h" #include "testing/gtest/include/gtest/gtest.h" +// IPC messages for testing ---------------------------------------------------- + +#define IPC_MESSAGE_IMPL +#include "ipc/ipc_message_macros.h" + +#define IPC_MESSAGE_START TestMsgStart + +IPC_MESSAGE_CONTROL0(TestMsgClassEmpty) + +IPC_MESSAGE_CONTROL1(TestMsgClassI, int) + +IPC_SYNC_MESSAGE_CONTROL1_1(TestMsgClassIS, int, std::string) + namespace { TEST(IPCMessageTest, ListValue) { @@ -70,4 +83,70 @@ TEST(IPCMessageTest, DictionaryValue) { EXPECT_FALSE(IPC::ReadParam(&bad_msg, &iter, &output)); } +class IPCMessageParameterTest : public testing::Test { + public: + IPCMessageParameterTest() : extra_param_("extra_param"), called_(false) {} + + bool OnMessageReceived(const IPC::Message& message) { + bool handled = true; + IPC_BEGIN_MESSAGE_MAP_WITH_PARAM(IPCMessageParameterTest, message, + &extra_param_) + IPC_MESSAGE_HANDLER(TestMsgClassEmpty, OnEmpty) + IPC_MESSAGE_HANDLER(TestMsgClassI, OnInt) + //IPC_MESSAGE_HANDLER(TestMsgClassIS, OnSync) + IPC_MESSAGE_UNHANDLED(handled = false) + IPC_END_MESSAGE_MAP() + + return handled; + } + + void OnEmpty(std::string* extra_param) { + EXPECT_EQ(extra_param, &extra_param_); + called_ = true; + } + + void OnInt(std::string* extra_param, int foo) { + EXPECT_EQ(extra_param, &extra_param_); + EXPECT_EQ(foo, 42); + called_ = true; + } + + /* TODO: handle sync IPCs + void OnSync(std::string* extra_param, int foo, std::string* out) { + EXPECT_EQ(extra_param, &extra_param_); + EXPECT_EQ(foo, 42); + called_ = true; + *out = std::string("out"); + } + + bool Send(IPC::Message* reply) { + delete reply; + return true; + }*/ + + std::string extra_param_; + bool called_; +}; + +TEST_F(IPCMessageParameterTest, EmptyDispatcherWithParam) { + TestMsgClassEmpty message; + EXPECT_TRUE(OnMessageReceived(message)); + EXPECT_TRUE(called_); +} + +TEST_F(IPCMessageParameterTest, OneIntegerWithParam) { + TestMsgClassI message(42); + EXPECT_TRUE(OnMessageReceived(message)); + EXPECT_TRUE(called_); +} + +/* TODO: handle sync IPCs +TEST_F(IPCMessageParameterTest, Sync) { + std::string output; + TestMsgClassIS message(42, &output); + EXPECT_TRUE(OnMessageReceived(message)); + EXPECT_TRUE(called_); + EXPECT_EQ(output, std::string("out")); +}*/ + } // namespace diff --git a/chromium/ipc/ipc_message_utils.cc b/chromium/ipc/ipc_message_utils.cc index 0390648f457..a67c5f5cf9e 100644 --- a/chromium/ipc/ipc_message_utils.cc +++ b/chromium/ipc/ipc_message_utils.cc @@ -340,12 +340,12 @@ void ParamTraits<std::string>::Log(const param_type& p, std::string* l) { } void ParamTraits<std::wstring>::Log(const param_type& p, std::string* l) { - l->append(WideToUTF8(p)); + l->append(base::WideToUTF8(p)); } #if !defined(WCHAR_T_IS_UTF16) void ParamTraits<base::string16>::Log(const param_type& p, std::string* l) { - l->append(UTF16ToUTF8(p)); + l->append(base::UTF16ToUTF8(p)); } #endif @@ -555,8 +555,8 @@ void ParamTraits<base::NullableString16>::Log(const param_type& p, l->append(")"); } -void ParamTraits<base::PlatformFileInfo>::Write(Message* m, - const param_type& p) { +void ParamTraits<base::File::Info>::Write(Message* m, + const param_type& p) { WriteParam(m, p.size); WriteParam(m, p.is_directory); WriteParam(m, p.last_modified.ToDoubleT()); @@ -564,9 +564,9 @@ void ParamTraits<base::PlatformFileInfo>::Write(Message* m, WriteParam(m, p.creation_time.ToDoubleT()); } -bool ParamTraits<base::PlatformFileInfo>::Read(const Message* m, - PickleIterator* iter, - param_type* p) { +bool ParamTraits<base::File::Info>::Read(const Message* m, + PickleIterator* iter, + param_type* p) { double last_modified; double last_accessed; double creation_time; @@ -584,8 +584,8 @@ bool ParamTraits<base::PlatformFileInfo>::Read(const Message* m, return result; } -void ParamTraits<base::PlatformFileInfo>::Log(const param_type& p, - std::string* l) { +void ParamTraits<base::File::Info>::Log(const param_type& p, + std::string* l) { l->append("("); LogParam(p.size, l); l->append(","); diff --git a/chromium/ipc/ipc_message_utils.h b/chromium/ipc/ipc_message_utils.h index 24b38af68ee..6bfd103c6d8 100644 --- a/chromium/ipc/ipc_message_utils.h +++ b/chromium/ipc/ipc_message_utils.h @@ -11,9 +11,11 @@ #include <string> #include <vector> +#include "base/containers/small_map.h" +#include "base/files/file.h" #include "base/format_macros.h" +#include "base/memory/scoped_ptr.h" #include "base/memory/scoped_vector.h" -#include "base/platform_file.h" #include "base/strings/string16.h" #include "base/strings/string_util.h" #include "base/strings/stringprintf.h" @@ -474,15 +476,15 @@ struct IPC_EXPORT ParamTraits<base::NullableString16> { }; template <> -struct IPC_EXPORT ParamTraits<base::PlatformFileInfo> { - typedef base::PlatformFileInfo param_type; +struct IPC_EXPORT ParamTraits<base::File::Info> { + typedef base::File::Info param_type; static void Write(Message* m, const param_type& p); static bool Read(const Message* m, PickleIterator* iter, param_type* r); static void Log(const param_type& p, std::string* l); }; template <> -struct SimilarTypeTraits<base::PlatformFileError> { +struct SimilarTypeTraits<base::File::Error> { typedef int Type; }; @@ -670,6 +672,75 @@ struct ParamTraits<ScopedVector<P> > { } }; +template <typename NormalMap, + int kArraySize, + typename EqualKey, + typename MapInit> +struct ParamTraits<base::SmallMap<NormalMap, kArraySize, EqualKey, MapInit> > { + typedef base::SmallMap<NormalMap, kArraySize, EqualKey, MapInit> param_type; + typedef typename param_type::key_type K; + typedef typename param_type::data_type V; + static void Write(Message* m, const param_type& p) { + WriteParam(m, static_cast<int>(p.size())); + typename param_type::const_iterator iter; + for (iter = p.begin(); iter != p.end(); ++iter) { + WriteParam(m, iter->first); + WriteParam(m, iter->second); + } + } + static bool Read(const Message* m, PickleIterator* iter, param_type* r) { + int size; + if (!m->ReadLength(iter, &size)) + return false; + for (int i = 0; i < size; ++i) { + K key; + if (!ReadParam(m, iter, &key)) + return false; + V& value = (*r)[key]; + if (!ReadParam(m, iter, &value)) + return false; + } + return true; + } + static void Log(const param_type& p, std::string* l) { + l->append("<base::SmallMap>"); + } +}; + +template <class P> +struct ParamTraits<scoped_ptr<P> > { + typedef scoped_ptr<P> param_type; + static void Write(Message* m, const param_type& p) { + bool valid = !!p; + WriteParam(m, valid); + if (valid) + WriteParam(m, *p); + } + static bool Read(const Message* m, PickleIterator* iter, param_type* r) { + bool valid = false; + if (!ReadParam(m, iter, &valid)) + return false; + + if (!valid) { + r->reset(); + return true; + } + + param_type temp(new P()); + if (!ReadParam(m, iter, temp.get())) + return false; + + r->swap(temp); + return true; + } + static void Log(const param_type& p, std::string* l) { + if (p) + LogParam(*p, l); + else + l->append("NULL"); + } +}; + // IPC types ParamTraits ------------------------------------------------------- // A ChannelHandle is basically a platform-inspecific wrapper around the diff --git a/chromium/ipc/ipc_multiprocess_test.cc b/chromium/ipc/ipc_multiprocess_test.cc index 8e3c03a1db6..8da67b2c8a6 100644 --- a/chromium/ipc/ipc_multiprocess_test.cc +++ b/chromium/ipc/ipc_multiprocess_test.cc @@ -4,6 +4,7 @@ #include "build/build_config.h" +#include "ipc/ipc_channel.h" #include "ipc/ipc_multiprocess_test.h" #if defined(OS_POSIX) @@ -14,6 +15,12 @@ namespace internal { void MultiProcessTestIPCSetUp() { +#if defined(OS_ANDROID) + // On Android we can't 'exec'. So for simple multi-process tests + // we need to reset some global data after forking to get the same + // behavior in simple multi-process tests. + IPC::Channel::NotifyProcessForkedForTesting(); +#endif #if defined(OS_POSIX) base::GlobalDescriptors::GetInstance()->Set(kPrimaryIPCChannel, kPrimaryIPCChannel + base::GlobalDescriptors::kBaseDescriptor); diff --git a/chromium/ipc/ipc_untrusted.gyp b/chromium/ipc/ipc_nacl.gyp index 7b0c5483faf..3119f46cae1 100644 --- a/chromium/ipc/ipc_untrusted.gyp +++ b/chromium/ipc/ipc_nacl.gyp @@ -1,4 +1,4 @@ -# Copyright (c) 2012 The Chromium Authors. All rights reserved. +# Copyright 2014 The Chromium Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. @@ -14,19 +14,19 @@ ['disable_nacl==0 and disable_nacl_untrusted==0', { 'targets': [ { - 'target_name': 'ipc_untrusted', + 'target_name': 'ipc_nacl', 'type': 'none', 'variables': { 'ipc_target': 1, 'nacl_untrusted_build': 1, - 'nlib_target': 'libipc_untrusted.a', + 'nlib_target': 'libipc_nacl.a', 'build_glibc': 0, 'build_newlib': 0, 'build_irt': 1, }, 'dependencies': [ '<(DEPTH)/native_client/tools.gyp:prep_toolchain', - '../base/base_untrusted.gyp:base_untrusted', + '../base/base_nacl.gyp:base_nacl', ], }, ], diff --git a/chromium/ipc/ipc_perftests.cc b/chromium/ipc/ipc_perftests.cc index e7eeab9d8ea..3feabda9d10 100644 --- a/chromium/ipc/ipc_perftests.cc +++ b/chromium/ipc/ipc_perftests.cc @@ -230,12 +230,13 @@ TEST_F(IPCChannelPerfTest, Performance) { ASSERT_TRUE(ConnectChannel()); ASSERT_TRUE(StartClient()); - const size_t kMsgSizeBase = 12; - const int kMsgSizeMaxExp = 5; - int msg_count = 100000; - size_t msg_size = kMsgSizeBase; - for (int i = 1; i <= kMsgSizeMaxExp; i++) { - listener.SetTestParams(msg_count, msg_size); + // Test several sizes. We use 12^N for message size, and limit the message + // count to keep the test duration reasonable. + const size_t kMsgSize[5] = {12, 144, 1728, 20736, 248832}; + const int kMessageCount[5] = {50000, 50000, 50000, 12000, 1000}; + + for (size_t i = 0; i < 5; i++) { + listener.SetTestParams(kMessageCount[i], kMsgSize[i]); // This initial message will kick-start the ping-pong of messages. IPC::Message* message = @@ -247,8 +248,6 @@ TEST_F(IPCChannelPerfTest, Performance) { // Run message loop. base::MessageLoop::current()->Run(); - - msg_size *= kMsgSizeBase; } // Send quit message. @@ -266,11 +265,10 @@ TEST_F(IPCChannelPerfTest, Performance) { MULTIPROCESS_IPC_TEST_CLIENT_MAIN(PerformanceClient) { base::MessageLoopForIO main_message_loop; ChannelReflectorListener listener; - IPC::Channel channel(IPCTestBase::GetChannelName("PerformanceClient"), - IPC::Channel::MODE_CLIENT, - &listener); - listener.Init(&channel); - CHECK(channel.Connect()); + scoped_ptr<IPC::Channel> channel(IPC::Channel::CreateClient( + IPCTestBase::GetChannelName("PerformanceClient"), &listener)); + listener.Init(channel.get()); + CHECK(channel->Connect()); base::MessageLoop::current()->Run(); return 0; diff --git a/chromium/ipc/ipc_platform_file.cc b/chromium/ipc/ipc_platform_file.cc index 4a756ea6788..826d03014ed 100644 --- a/chromium/ipc/ipc_platform_file.cc +++ b/chromium/ipc/ipc_platform_file.cc @@ -45,4 +45,9 @@ PlatformFileForTransit GetFileHandleForProcess(base::PlatformFile handle, return out_handle; } +PlatformFileForTransit TakeFileHandleForProcess(base::File file, + base::ProcessHandle process) { + return GetFileHandleForProcess(file.TakePlatformFile(), process, true); +} + } // namespace IPC diff --git a/chromium/ipc/ipc_platform_file.h b/chromium/ipc/ipc_platform_file.h index 553c78c522f..40bfa0da189 100644 --- a/chromium/ipc/ipc_platform_file.h +++ b/chromium/ipc/ipc_platform_file.h @@ -6,7 +6,7 @@ #define IPC_IPC_PLATFORM_FILE_H_ #include "base/basictypes.h" -#include "base/platform_file.h" +#include "base/files/file.h" #include "base/process/process.h" #include "ipc/ipc_export.h" @@ -24,7 +24,7 @@ typedef base::FileDescriptor PlatformFileForTransit; inline PlatformFileForTransit InvalidPlatformFileForTransit() { #if defined(OS_WIN) - return base::kInvalidPlatformFileValue; + return INVALID_HANDLE_VALUE; #elif defined(OS_POSIX) return base::FileDescriptor(); #endif @@ -39,12 +39,27 @@ inline base::PlatformFile PlatformFileForTransitToPlatformFile( #endif } +inline base::File PlatformFileForTransitToFile( + const PlatformFileForTransit& transit) { +#if defined(OS_WIN) + return base::File(transit); +#elif defined(OS_POSIX) + return base::File(transit.fd); +#endif +} + // Returns a file handle equivalent to |file| that can be used in |process|. IPC_EXPORT PlatformFileForTransit GetFileHandleForProcess( base::PlatformFile file, base::ProcessHandle process, bool close_source_handle); +// Returns a file handle equivalent to |file| that can be used in |process|. +// Note that this function takes ownership of |file|. +IPC_EXPORT PlatformFileForTransit TakeFileHandleForProcess( + base::File file, + base::ProcessHandle process); + } // namespace IPC #endif // IPC_IPC_PLATFORM_FILE_H_ diff --git a/chromium/ipc/ipc_send_fds_test.cc b/chromium/ipc/ipc_send_fds_test.cc index aeec8907a4f..1df6be98643 100644 --- a/chromium/ipc/ipc_send_fds_test.cc +++ b/chromium/ipc/ipc_send_fds_test.cc @@ -134,10 +134,10 @@ int SendFdsClientCommon(const std::string& test_client_name, MyChannelDescriptorListener listener(expected_inode_num); // Set up IPC channel. - IPC::Channel channel(IPCTestBase::GetChannelName(test_client_name), - IPC::Channel::MODE_CLIENT, - &listener); - CHECK(channel.Connect()); + scoped_ptr<IPC::Channel> channel(IPC::Channel::CreateClient( + IPCTestBase::GetChannelName(test_client_name), + &listener)); + CHECK(channel->Connect()); // Run message loop. base::MessageLoop::current()->Run(); @@ -233,14 +233,11 @@ class PipeChannelHelper { void Init() { IPC::ChannelHandle in_handle("IN"); - in.reset(new IPC::Channel(in_handle, - IPC::Channel::MODE_SERVER, - &null_listener_)); - base::FileDescriptor out_fd(in->TakeClientFileDescriptor(), false); + in = IPC::Channel::CreateServer(in_handle, &null_listener_); + base::FileDescriptor out_fd( + in->TakeClientFileDescriptor(), false); IPC::ChannelHandle out_handle("OUT", out_fd); - out.reset(new IPC::Channel(out_handle, - IPC::Channel::MODE_CLIENT, - &cb_listener_)); + out = IPC::Channel::CreateClient(out_handle, &cb_listener_); // PostTask the connect calls to make sure the callbacks happens // on the right threads. in_thread_->message_loop()->PostTask( diff --git a/chromium/ipc/ipc_switches.cc b/chromium/ipc/ipc_switches.cc index bcb12256e54..df245f55f26 100644 --- a/chromium/ipc/ipc_switches.cc +++ b/chromium/ipc/ipc_switches.cc @@ -15,10 +15,5 @@ namespace switches { // IPC channel the browser expects to use to communicate with it. const char kProcessChannelID[] = "channel"; -// Will add kDebugOnStart to every child processes. If a value is passed, it -// will be used as a filter to determine if the child process should have the -// kDebugOnStart flag passed on or not. -const char kDebugChildren[] = "debug-children"; - } // namespace switches diff --git a/chromium/ipc/ipc_switches.h b/chromium/ipc/ipc_switches.h index d88afb5ba9f..c63c2024270 100644 --- a/chromium/ipc/ipc_switches.h +++ b/chromium/ipc/ipc_switches.h @@ -12,7 +12,6 @@ namespace switches { IPC_EXPORT extern const char kProcessChannelID[]; -IPC_EXPORT extern const char kDebugChildren[]; } // namespace switches diff --git a/chromium/ipc/ipc_sync_channel.cc b/chromium/ipc/ipc_sync_channel.cc index 491b72db8a6..a7ed230e53c 100644 --- a/chromium/ipc/ipc_sync_channel.cc +++ b/chromium/ipc/ipc_sync_channel.cc @@ -404,25 +404,37 @@ base::WaitableEventWatcher::EventCallback return base::Bind(&SyncChannel::SyncContext::OnWaitableEventSignaled, this); } -SyncChannel::SyncChannel( +// static +scoped_ptr<SyncChannel> SyncChannel::Create( const IPC::ChannelHandle& channel_handle, Channel::Mode mode, Listener* listener, base::SingleThreadTaskRunner* ipc_task_runner, bool create_pipe_now, - WaitableEvent* shutdown_event) - : ChannelProxy(new SyncContext(listener, ipc_task_runner, shutdown_event)), - sync_messages_with_no_timeout_allowed_(true) { - ChannelProxy::Init(channel_handle, mode, create_pipe_now); - StartWatching(); + base::WaitableEvent* shutdown_event) { + scoped_ptr<SyncChannel> channel = + Create(listener, ipc_task_runner, shutdown_event); + channel->Init(channel_handle, mode, create_pipe_now); + return channel.Pass(); +} + +// static +scoped_ptr<SyncChannel> SyncChannel::Create( + Listener* listener, + base::SingleThreadTaskRunner* ipc_task_runner, + WaitableEvent* shutdown_event) { + return make_scoped_ptr( + new SyncChannel(listener, ipc_task_runner, shutdown_event)); } SyncChannel::SyncChannel( Listener* listener, base::SingleThreadTaskRunner* ipc_task_runner, WaitableEvent* shutdown_event) - : ChannelProxy(new SyncContext(listener, ipc_task_runner, shutdown_event)), - sync_messages_with_no_timeout_allowed_(true) { + : ChannelProxy(new SyncContext(listener, ipc_task_runner, shutdown_event)) { + // The current (listener) thread must be distinct from the IPC thread, or else + // sending synchronous messages will deadlock. + DCHECK_NE(ipc_task_runner, base::ThreadTaskRunnerHandle::Get()); StartWatching(); } @@ -434,18 +446,13 @@ void SyncChannel::SetRestrictDispatchChannelGroup(int group) { } bool SyncChannel::Send(Message* message) { - return SendWithTimeout(message, base::kNoTimeout); -} - -bool SyncChannel::SendWithTimeout(Message* message, int timeout_ms) { #ifdef IPC_MESSAGE_LOG_ENABLED Logging* logger = Logging::GetInstance(); std::string name; logger->GetMessageText(message->type(), &name, message, NULL); - TRACE_EVENT1("task", "SyncChannel::SendWithTimeout", - "name", name); + TRACE_EVENT1("ipc", "SyncChannel::Send", "name", name); #else - TRACE_EVENT2("task", "SyncChannel::SendWithTimeout", + TRACE_EVENT2("ipc", "SyncChannel::Send", "class", IPC_MESSAGE_ID_CLASS(message->type()), "line", IPC_MESSAGE_ID_LINE(message->type())); #endif @@ -462,25 +469,12 @@ bool SyncChannel::SendWithTimeout(Message* message, int timeout_ms) { return false; } - DCHECK(sync_messages_with_no_timeout_allowed_ || - timeout_ms != base::kNoTimeout); SyncMessage* sync_msg = static_cast<SyncMessage*>(message); context->Push(sync_msg); - int message_id = SyncMessage::GetMessageId(*sync_msg); WaitableEvent* pump_messages_event = sync_msg->pump_messages_event(); ChannelProxy::Send(message); - if (timeout_ms != base::kNoTimeout) { - // We use the sync message id so that when a message times out, we don't - // confuse it with another send that is either above/below this Send in - // the call stack. - context->ipc_task_runner()->PostDelayedTask( - FROM_HERE, - base::Bind(&SyncContext::OnSendTimeout, context.get(), message_id), - base::TimeDelta::FromMilliseconds(timeout_ms)); - } - // Wait for reply, or for any other incoming synchronous messages. // *this* might get deleted, so only call static functions at this point. WaitForReply(context.get(), pump_messages_event); diff --git a/chromium/ipc/ipc_sync_channel.h b/chromium/ipc/ipc_sync_channel.h index 46ac910efdc..8984184da29 100644 --- a/chromium/ipc/ipc_sync_channel.h +++ b/chromium/ipc/ipc_sync_channel.h @@ -30,7 +30,7 @@ class SyncMessage; // Overview of how the sync channel works // -------------------------------------- // When the sending thread sends a synchronous message, we create a bunch -// of tracking info (created in SendWithTimeout, stored in the PendingSyncMsg +// of tracking info (created in Send, stored in the PendingSyncMsg // structure) associated with the message that we identify by the unique // "MessageId" on the SyncMessage. Among the things we save is the // "Deserializer" which is provided by the sync message. This object is in @@ -66,29 +66,26 @@ class IPC_EXPORT SyncChannel : public ChannelProxy { // Creates and initializes a sync channel. If create_pipe_now is specified, // the channel will be initialized synchronously. - SyncChannel(const IPC::ChannelHandle& channel_handle, - Channel::Mode mode, - Listener* listener, - base::SingleThreadTaskRunner* ipc_task_runner, - bool create_pipe_now, - base::WaitableEvent* shutdown_event); + // The naming pattern follows IPC::Channel. + static scoped_ptr<SyncChannel> Create( + const IPC::ChannelHandle& channel_handle, + IPC::Channel::Mode mode, + Listener* listener, + base::SingleThreadTaskRunner* ipc_task_runner, + bool create_pipe_now, + base::WaitableEvent* shutdown_event); // Creates an uninitialized sync channel. Call ChannelProxy::Init to // initialize the channel. This two-step setup allows message filters to be // added before any messages are sent or received. - SyncChannel(Listener* listener, - base::SingleThreadTaskRunner* ipc_task_runner, - base::WaitableEvent* shutdown_event); + static scoped_ptr<SyncChannel> Create( + Listener* listener, + base::SingleThreadTaskRunner* ipc_task_runner, + base::WaitableEvent* shutdown_event); virtual ~SyncChannel(); virtual bool Send(Message* message) OVERRIDE; - virtual bool SendWithTimeout(Message* message, int timeout_ms); - - // Whether we allow sending messages with no time-out. - void set_sync_messages_with_no_timeout_allowed(bool value) { - sync_messages_with_no_timeout_allowed_ = value; - } // Sets the dispatch group for this channel, to only allow re-entrant dispatch // of messages to other channels in the same group. @@ -194,6 +191,10 @@ class IPC_EXPORT SyncChannel : public ChannelProxy { }; private: + SyncChannel(Listener* listener, + base::SingleThreadTaskRunner* ipc_task_runner, + base::WaitableEvent* shutdown_event); + void OnWaitableEventSignaled(base::WaitableEvent* arg); SyncContext* sync_context() { @@ -212,8 +213,6 @@ class IPC_EXPORT SyncChannel : public ChannelProxy { // Starts the dispatch watcher. void StartWatching(); - bool sync_messages_with_no_timeout_allowed_; - // Used to signal events between the IPC and listener threads. base::WaitableEventWatcher dispatch_watcher_; base::WaitableEventWatcher::EventCallback dispatch_watcher_callback_; diff --git a/chromium/ipc/ipc_sync_channel_unittest.cc b/chromium/ipc/ipc_sync_channel_unittest.cc index 30f02f7c4c7..05126b975f1 100644 --- a/chromium/ipc/ipc_sync_channel_unittest.cc +++ b/chromium/ipc/ipc_sync_channel_unittest.cc @@ -65,9 +65,6 @@ class Worker : public Listener, public Sender { void AddRef() { } void Release() { } virtual bool Send(Message* msg) OVERRIDE { return channel_->Send(msg); } - bool SendWithTimeout(Message* msg, int timeout_ms) { - return channel_->SendWithTimeout(msg, timeout_ms); - } void WaitForChannelCreation() { channel_created_->Wait(); } void CloseChannel() { DCHECK(base::MessageLoop::current() == ListenerThread()->message_loop()); @@ -96,12 +93,12 @@ class Worker : public Listener, public Sender { DCHECK(overrided_thread_ == NULL); overrided_thread_ = overrided_thread; } - bool SendAnswerToLife(bool pump, int timeout, bool succeed) { + bool SendAnswerToLife(bool pump, bool succeed) { int answer = 0; SyncMessage* msg = new SyncChannelTestMsg_AnswerToLife(&answer); if (pump) msg->EnableMessagePumping(); - bool result = SendWithTimeout(msg, timeout); + bool result = Send(msg); DCHECK_EQ(result, succeed); DCHECK_EQ(answer, (succeed ? 42 : 0)); return result; @@ -154,12 +151,10 @@ class Worker : public Listener, public Sender { } virtual SyncChannel* CreateChannel() { - return new SyncChannel(channel_name_, - mode_, - this, - ipc_thread_.message_loop_proxy().get(), - true, - &shutdown_event_); + scoped_ptr<SyncChannel> channel = SyncChannel::Create( + channel_name_, mode_, this, ipc_thread_.message_loop_proxy().get(), + true, &shutdown_event_); + return channel.release(); } base::Thread* ListenerThread() { @@ -280,7 +275,7 @@ class SimpleServer : public Worker { : Worker(Channel::MODE_SERVER, "simpler_server"), pump_during_send_(pump_during_send) { } virtual void Run() OVERRIDE { - SendAnswerToLife(pump_during_send_, base::kNoTimeout, true); + SendAnswerToLife(pump_during_send_, true); Done(); } @@ -322,14 +317,16 @@ class TwoStepServer : public Worker { create_pipe_now_(create_pipe_now) { } virtual void Run() OVERRIDE { - SendAnswerToLife(false, base::kNoTimeout, true); + SendAnswerToLife(false, true); Done(); } virtual SyncChannel* CreateChannel() OVERRIDE { - SyncChannel* channel = new SyncChannel( - this, ipc_thread().message_loop_proxy().get(), shutdown_event()); - channel->Init(channel_name(), mode(), create_pipe_now_); + SyncChannel* channel = + SyncChannel::Create(channel_name(), mode(), this, + ipc_thread().message_loop_proxy().get(), + create_pipe_now_, + shutdown_event()).release(); return channel; } @@ -348,9 +345,11 @@ class TwoStepClient : public Worker { } virtual SyncChannel* CreateChannel() OVERRIDE { - SyncChannel* channel = new SyncChannel( - this, ipc_thread().message_loop_proxy().get(), shutdown_event()); - channel->Init(channel_name(), mode(), create_pipe_now_); + SyncChannel* channel = + SyncChannel::Create(channel_name(), mode(), this, + ipc_thread().message_loop_proxy().get(), + create_pipe_now_, + shutdown_event()).release(); return channel; } @@ -408,10 +407,10 @@ class NoHangServer : public Worker { got_first_reply_(got_first_reply), pump_during_send_(pump_during_send) { } virtual void Run() OVERRIDE { - SendAnswerToLife(pump_during_send_, base::kNoTimeout, true); + SendAnswerToLife(pump_during_send_, true); got_first_reply_->Signal(); - SendAnswerToLife(pump_during_send_, base::kNoTimeout, false); + SendAnswerToLife(pump_during_send_, false); Done(); } @@ -470,7 +469,7 @@ class UnblockServer : public Worker { msg->EnableMessagePumping(); Send(msg); } else { - SendAnswerToLife(pump_during_send_, base::kNoTimeout, true); + SendAnswerToLife(pump_during_send_, true); } Done(); } @@ -541,7 +540,7 @@ class RecursiveServer : public Worker { virtual void OnDouble(int in, int* out) OVERRIDE { *out = in * 2; - SendAnswerToLife(pump_second_, base::kNoTimeout, expected_send_result_); + SendAnswerToLife(pump_second_, expected_send_result_); } bool expected_send_result_, pump_first_, pump_second_; @@ -679,7 +678,7 @@ class MultipleClient2 : public Worker { virtual void Run() OVERRIDE { client1_msg_received_->Wait(); - SendAnswerToLife(pump_during_send_, base::kNoTimeout, true); + SendAnswerToLife(pump_during_send_, true); client1_can_reply_->Signal(); Done(); } @@ -879,117 +878,10 @@ TEST_F(IPCSyncChannelTest, ChattyServer) { //------------------------------------------------------------------------------ -class TimeoutServer : public Worker { - public: - TimeoutServer(int timeout_ms, - std::vector<bool> timeout_seq, - bool pump_during_send) - : Worker(Channel::MODE_SERVER, "timeout_server"), - timeout_ms_(timeout_ms), - timeout_seq_(timeout_seq), - pump_during_send_(pump_during_send) { - } - - virtual void Run() OVERRIDE { - for (std::vector<bool>::const_iterator iter = timeout_seq_.begin(); - iter != timeout_seq_.end(); ++iter) { - SendAnswerToLife(pump_during_send_, timeout_ms_, !*iter); - } - Done(); - } - - private: - int timeout_ms_; - std::vector<bool> timeout_seq_; - bool pump_during_send_; -}; - -class UnresponsiveClient : public Worker { - public: - explicit UnresponsiveClient(std::vector<bool> timeout_seq) - : Worker(Channel::MODE_CLIENT, "unresponsive_client"), - timeout_seq_(timeout_seq) { - } - - virtual void OnAnswerDelay(Message* reply_msg) OVERRIDE { - DCHECK(!timeout_seq_.empty()); - if (!timeout_seq_[0]) { - SyncChannelTestMsg_AnswerToLife::WriteReplyParams(reply_msg, 42); - Send(reply_msg); - } else { - // Don't reply. - delete reply_msg; - } - timeout_seq_.erase(timeout_seq_.begin()); - if (timeout_seq_.empty()) - Done(); - } - - private: - // Whether we should time-out or respond to the various messages we receive. - std::vector<bool> timeout_seq_; -}; - -void SendWithTimeoutOK(bool pump_during_send) { - std::vector<Worker*> workers; - std::vector<bool> timeout_seq; - timeout_seq.push_back(false); - timeout_seq.push_back(false); - timeout_seq.push_back(false); - workers.push_back(new TimeoutServer(5000, timeout_seq, pump_during_send)); - workers.push_back(new SimpleClient()); - RunTest(workers); -} - -void SendWithTimeoutTimeout(bool pump_during_send) { - std::vector<Worker*> workers; - std::vector<bool> timeout_seq; - timeout_seq.push_back(true); - timeout_seq.push_back(false); - timeout_seq.push_back(false); - workers.push_back(new TimeoutServer(100, timeout_seq, pump_during_send)); - workers.push_back(new UnresponsiveClient(timeout_seq)); - RunTest(workers); -} - -void SendWithTimeoutMixedOKAndTimeout(bool pump_during_send) { - std::vector<Worker*> workers; - std::vector<bool> timeout_seq; - timeout_seq.push_back(true); - timeout_seq.push_back(false); - timeout_seq.push_back(false); - timeout_seq.push_back(true); - timeout_seq.push_back(false); - workers.push_back(new TimeoutServer(100, timeout_seq, pump_during_send)); - workers.push_back(new UnresponsiveClient(timeout_seq)); - RunTest(workers); -} - -// Tests that SendWithTimeout does not time-out if the response comes back fast -// enough. -TEST_F(IPCSyncChannelTest, SendWithTimeoutOK) { - SendWithTimeoutOK(false); - SendWithTimeoutOK(true); -} - -// Tests that SendWithTimeout does time-out. -TEST_F(IPCSyncChannelTest, SendWithTimeoutTimeout) { - SendWithTimeoutTimeout(false); - SendWithTimeoutTimeout(true); -} - -// Sends some message that time-out and some that succeed. -TEST_F(IPCSyncChannelTest, SendWithTimeoutMixedOKAndTimeout) { - SendWithTimeoutMixedOKAndTimeout(false); - SendWithTimeoutMixedOKAndTimeout(true); -} - -//------------------------------------------------------------------------------ - void NestedCallback(Worker* server) { // Sleep a bit so that we wake up after the reply has been received. base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(250)); - server->SendAnswerToLife(true, base::kNoTimeout, true); + server->SendAnswerToLife(true, true); } bool timeout_occurred = false; @@ -1014,7 +906,7 @@ class DoneEventRaceServer : public Worker { // bug, the reply message comes back and is deserialized, however the done // event wasn't set. So we indirectly use the timeout task to notice if a // timeout occurred. - SendAnswerToLife(true, 10000, true); + SendAnswerToLife(true, true); DCHECK(!timeout_occurred); Done(); } @@ -1042,8 +934,8 @@ class TestSyncMessageFilter : public SyncMessageFilter { message_loop_(message_loop) { } - virtual void OnFilterAdded(Channel* channel) OVERRIDE { - SyncMessageFilter::OnFilterAdded(channel); + virtual void OnFilterAdded(Sender* sender) OVERRIDE { + SyncMessageFilter::OnFilterAdded(sender); message_loop_->PostTask( FROM_HERE, base::Bind(&TestSyncMessageFilter::SendMessageOnHelperThread, this)); @@ -1245,13 +1137,13 @@ class RestrictedDispatchClient : public Worker { else LOG(ERROR) << "Send failed to dispatch incoming message on same channel"; - non_restricted_channel_.reset( - new SyncChannel("non_restricted_channel", - Channel::MODE_CLIENT, - this, - ipc_thread().message_loop_proxy().get(), - true, - shutdown_event())); + non_restricted_channel_ = + SyncChannel::Create("non_restricted_channel", + IPC::Channel::MODE_CLIENT, + this, + ipc_thread().message_loop_proxy().get(), + true, + shutdown_event()); server_->ListenerThread()->message_loop()->PostTask( FROM_HERE, base::Bind(&RestrictedDispatchServer::OnDoPing, server_, 2)); @@ -1517,7 +1409,6 @@ class RestrictedDispatchDeadlockClient1 : public Worker { PossiblyDone(); } - base::Thread* ListenerThread() { return Worker::ListenerThread(); } private: virtual bool OnMessageReceived(const Message& message) OVERRIDE { IPC_BEGIN_MESSAGE_MAP(RestrictedDispatchDeadlockClient1, message) @@ -1637,13 +1528,13 @@ class RestrictedDispatchPipeWorker : public Worker { if (is_first()) event1_->Signal(); event2_->Wait(); - other_channel_.reset( - new SyncChannel(other_channel_name_, - Channel::MODE_CLIENT, - this, - ipc_thread().message_loop_proxy().get(), - true, - shutdown_event())); + other_channel_ = + SyncChannel::Create(other_channel_name_, + IPC::Channel::MODE_CLIENT, + this, + ipc_thread().message_loop_proxy().get(), + true, + shutdown_event()); other_channel_->SetRestrictDispatchChannelGroup(group_); if (!is_first()) { event1_->Signal(); @@ -1717,13 +1608,13 @@ class ReentrantReplyServer1 : public Worker { server_ready_(server_ready) { } virtual void Run() OVERRIDE { - server2_channel_.reset( - new SyncChannel("reentrant_reply2", - Channel::MODE_CLIENT, - this, - ipc_thread().message_loop_proxy().get(), - true, - shutdown_event())); + server2_channel_ = + SyncChannel::Create("reentrant_reply2", + IPC::Channel::MODE_CLIENT, + this, + ipc_thread().message_loop_proxy().get(), + true, + shutdown_event()); server_ready_->Signal(); Message* msg = new SyncChannelTestMsg_Reentrant1(); server2_channel_->Send(msg); @@ -1831,7 +1722,7 @@ class VerifiedServer : public Worker { VLOG(1) << __FUNCTION__ << " Sending reply: " << reply_text_; SyncChannelNestedTestMsg_String::WriteReplyParams(reply_msg, reply_text_); Send(reply_msg); - ASSERT_EQ(channel()->peer_pid(), base::GetCurrentProcId()); + ASSERT_EQ(channel()->GetPeerPID(), base::GetCurrentProcId()); Done(); } @@ -1860,7 +1751,7 @@ class VerifiedClient : public Worker { (void)expected_text_; VLOG(1) << __FUNCTION__ << " Received reply: " << response; - ASSERT_EQ(channel()->peer_pid(), base::GetCurrentProcId()); + ASSERT_EQ(channel()->GetPeerPID(), base::GetCurrentProcId()); Done(); } diff --git a/chromium/ipc/ipc_sync_message_filter.cc b/chromium/ipc/ipc_sync_message_filter.cc index a534c445916..e2ea1bfb1ee 100644 --- a/chromium/ipc/ipc_sync_message_filter.cc +++ b/chromium/ipc/ipc_sync_message_filter.cc @@ -9,6 +9,7 @@ #include "base/logging.h" #include "base/message_loop/message_loop_proxy.h" #include "base/synchronization/waitable_event.h" +#include "ipc/ipc_channel.h" #include "ipc/ipc_sync_message.h" using base::MessageLoopProxy; @@ -16,7 +17,7 @@ using base::MessageLoopProxy; namespace IPC { SyncMessageFilter::SyncMessageFilter(base::WaitableEvent* shutdown_event) - : channel_(NULL), + : sender_(NULL), listener_loop_(MessageLoopProxy::current()), shutdown_event_(shutdown_event) { } @@ -66,19 +67,19 @@ bool SyncMessageFilter::Send(Message* message) { return pending_message.send_result; } -void SyncMessageFilter::OnFilterAdded(Channel* channel) { - channel_ = channel; +void SyncMessageFilter::OnFilterAdded(Sender* sender) { + sender_ = sender; base::AutoLock auto_lock(lock_); io_loop_ = MessageLoopProxy::current(); } void SyncMessageFilter::OnChannelError() { - channel_ = NULL; + sender_ = NULL; SignalAllEvents(); } void SyncMessageFilter::OnChannelClosing() { - channel_ = NULL; + sender_ = NULL; SignalAllEvents(); } @@ -103,8 +104,8 @@ SyncMessageFilter::~SyncMessageFilter() { } void SyncMessageFilter::SendOnIOThread(Message* message) { - if (channel_) { - channel_->Send(message); + if (sender_) { + sender_->Send(message); return; } diff --git a/chromium/ipc/ipc_sync_message_filter.h b/chromium/ipc/ipc_sync_message_filter.h index dbc28345ac6..933a95eea0e 100644 --- a/chromium/ipc/ipc_sync_message_filter.h +++ b/chromium/ipc/ipc_sync_message_filter.h @@ -10,8 +10,9 @@ #include "base/basictypes.h" #include "base/memory/ref_counted.h" #include "base/synchronization/lock.h" -#include "ipc/ipc_channel_proxy.h" +#include "ipc/ipc_sender.h" #include "ipc/ipc_sync_message.h" +#include "ipc/message_filter.h" namespace base { class MessageLoopProxy; @@ -25,16 +26,15 @@ namespace IPC { // support fancy features that SyncChannel does, such as handling recursion or // receiving messages while waiting for a response. Note that this object can // be used to send simultaneous synchronous messages from different threads. -class IPC_EXPORT SyncMessageFilter : public ChannelProxy::MessageFilter, - public Sender { +class IPC_EXPORT SyncMessageFilter : public MessageFilter, public Sender { public: explicit SyncMessageFilter(base::WaitableEvent* shutdown_event); // MessageSender implementation. virtual bool Send(Message* message) OVERRIDE; - // ChannelProxy::MessageFilter implementation. - virtual void OnFilterAdded(Channel* channel) OVERRIDE; + // MessageFilter implementation. + virtual void OnFilterAdded(Sender* sender) OVERRIDE; virtual void OnChannelError() OVERRIDE; virtual void OnChannelClosing() OVERRIDE; virtual bool OnMessageReceived(const Message& message) OVERRIDE; @@ -48,7 +48,7 @@ class IPC_EXPORT SyncMessageFilter : public ChannelProxy::MessageFilter, void SignalAllEvents(); // The channel to which this filter was added. - Channel* channel_; + Sender* sender_; // The process's main thread. scoped_refptr<base::MessageLoopProxy> listener_loop_; diff --git a/chromium/ipc/ipc_test_base.cc b/chromium/ipc/ipc_test_base.cc index ef050a7151f..589ee98ca09 100644 --- a/chromium/ipc/ipc_test_base.cc +++ b/chromium/ipc/ipc_test_base.cc @@ -7,12 +7,10 @@ #include "ipc/ipc_test_base.h" #include "base/command_line.h" -#include "base/debug/debug_on_start_win.h" #include "base/process/kill.h" #include "base/threading/thread.h" #include "base/time/time.h" #include "ipc/ipc_descriptors.h" -#include "ipc/ipc_switches.h" #if defined(OS_POSIX) #include "base/posix/global_descriptors.h" @@ -71,9 +69,7 @@ void IPCTestBase::CreateChannelFromChannelHandle( IPC::Listener* listener) { CHECK(!channel_.get()); CHECK(!channel_proxy_.get()); - channel_.reset(new IPC::Channel(channel_handle, - IPC::Channel::MODE_SERVER, - listener)); + channel_ = IPC::Channel::CreateServer(channel_handle, listener); } void IPCTestBase::CreateChannelProxy( @@ -81,10 +77,10 @@ void IPCTestBase::CreateChannelProxy( base::SingleThreadTaskRunner* ipc_task_runner) { CHECK(!channel_.get()); CHECK(!channel_proxy_.get()); - channel_proxy_.reset(new IPC::ChannelProxy(GetChannelName(test_client_name_), + channel_proxy_ = IPC::ChannelProxy::Create(GetChannelName(test_client_name_), IPC::Channel::MODE_SERVER, listener, - ipc_task_runner)); + ipc_task_runner); } void IPCTestBase::DestroyChannelProxy() { @@ -96,22 +92,20 @@ bool IPCTestBase::StartClient() { DCHECK(client_process_ == base::kNullProcessHandle); std::string test_main = test_client_name_ + "TestClientMain"; - bool debug_on_start = - CommandLine::ForCurrentProcess()->HasSwitch(switches::kDebugChildren); #if defined(OS_WIN) - client_process_ = MultiProcessTest::SpawnChild(test_main, debug_on_start); + client_process_ = SpawnChild(test_main); #elif defined(OS_POSIX) base::FileHandleMappingVector fds_to_map; - const int ipcfd = channel_.get() ? channel_->GetClientFileDescriptor() : - channel_proxy_->GetClientFileDescriptor(); + const int ipcfd = channel_.get() + ? channel_->GetClientFileDescriptor() + : channel_proxy_->GetClientFileDescriptor(); if (ipcfd > -1) fds_to_map.push_back(std::pair<int, int>(ipcfd, kPrimaryIPCChannel + base::GlobalDescriptors::kBaseDescriptor)); - - client_process_ = MultiProcessTest::SpawnChild(test_main, - fds_to_map, - debug_on_start); + base::LaunchOptions options; + options.fds_to_remap = &fds_to_map; + client_process_ = SpawnChildWithOptions(test_main, options); #endif return client_process_ != base::kNullProcessHandle; diff --git a/chromium/ipc/ipc_test_sink.cc b/chromium/ipc/ipc_test_sink.cc index 070fe1235d1..9e9d1fd3c24 100644 --- a/chromium/ipc/ipc_test_sink.cc +++ b/chromium/ipc/ipc_test_sink.cc @@ -21,6 +21,21 @@ bool TestSink::Send(Message* message) { return true; } +bool TestSink::Connect() { + NOTIMPLEMENTED(); + return false; +} + +void TestSink::Close() { + NOTIMPLEMENTED(); +} + +base::ProcessId TestSink::GetPeerPID() const { + NOTIMPLEMENTED(); + return base::ProcessId(); +} + + bool TestSink::OnMessageReceived(const Message& msg) { ObserverListBase<Listener>::Iterator it(filter_list_); Listener* observer; @@ -74,4 +89,18 @@ void TestSink::RemoveFilter(Listener* filter) { filter_list_.RemoveObserver(filter); } +#if defined(OS_POSIX) && !defined(OS_NACL) + +int TestSink::GetClientFileDescriptor() const { + NOTREACHED(); + return -1; +} + +int TestSink::TakeClientFileDescriptor() { + NOTREACHED(); + return -1; +} + +#endif // defined(OS_POSIX) && !defined(OS_NACL) + } // namespace IPC diff --git a/chromium/ipc/ipc_test_sink.h b/chromium/ipc/ipc_test_sink.h index 78be9e75269..1a213ee1245 100644 --- a/chromium/ipc/ipc_test_sink.h +++ b/chromium/ipc/ipc_test_sink.h @@ -78,6 +78,14 @@ class TestSink : public Channel { // Interface in IPC::Channel. This copies the message to the sink and then // deletes it. virtual bool Send(IPC::Message* message) OVERRIDE; + virtual bool Connect() OVERRIDE WARN_UNUSED_RESULT; + virtual void Close() OVERRIDE; + virtual base::ProcessId GetPeerPID() const OVERRIDE; + +#if defined(OS_POSIX) && !defined(OS_NACL) + virtual int GetClientFileDescriptor() const OVERRIDE; + virtual int TakeClientFileDescriptor() OVERRIDE; +#endif // defined(OS_POSIX) && !defined(OS_NACL) // Used by the source of the messages to send the message to the sink. This // will make a copy of the message and store it in the list. diff --git a/chromium/ipc/message_filter.cc b/chromium/ipc/message_filter.cc new file mode 100644 index 00000000000..ffde0f0d0f3 --- /dev/null +++ b/chromium/ipc/message_filter.cc @@ -0,0 +1,35 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ipc/message_filter.h" + +#include "base/memory/ref_counted.h" +#include "ipc/ipc_channel.h" + +namespace IPC { + +MessageFilter::MessageFilter() {} + +void MessageFilter::OnFilterAdded(Sender* sender) {} + +void MessageFilter::OnFilterRemoved() {} + +void MessageFilter::OnChannelConnected(int32 peer_pid) {} + +void MessageFilter::OnChannelError() {} + +void MessageFilter::OnChannelClosing() {} + +bool MessageFilter::OnMessageReceived(const Message& message) { + return false; +} + +bool MessageFilter::GetSupportedMessageClasses( + std::vector<uint32>* /*supported_message_classes*/) const { + return false; +} + +MessageFilter::~MessageFilter() {} + +} // namespace IPC diff --git a/chromium/ipc/message_filter.h b/chromium/ipc/message_filter.h new file mode 100644 index 00000000000..c9ba4e84ac2 --- /dev/null +++ b/chromium/ipc/message_filter.h @@ -0,0 +1,67 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef IPC_MESSAGE_FILTER_H_ +#define IPC_MESSAGE_FILTER_H_ + +#include <vector> + +#include "base/memory/ref_counted.h" +#include "ipc/ipc_export.h" + +namespace IPC { + +class Sender; +class Message; + +// A class that receives messages on the thread where the IPC channel is +// running. It can choose to prevent the default action for an IPC message. +class IPC_EXPORT MessageFilter + : public base::RefCountedThreadSafe<MessageFilter> { + public: + MessageFilter(); + + // Called on the background thread to provide the filter with access to the + // channel. Called when the IPC channel is initialized or when AddFilter + // is called if the channel is already initialized. + virtual void OnFilterAdded(Sender* sender); + + // Called on the background thread when the filter has been removed from + // the ChannelProxy and when the Channel is closing. After a filter is + // removed, it will not be called again. + virtual void OnFilterRemoved(); + + // Called to inform the filter that the IPC channel is connected and we + // have received the internal Hello message from the peer. + virtual void OnChannelConnected(int32 peer_pid); + + // Called when there is an error on the channel, typically that the channel + // has been closed. + virtual void OnChannelError(); + + // Called to inform the filter that the IPC channel will be destroyed. + // OnFilterRemoved is called immediately after this. + virtual void OnChannelClosing(); + + // Return true to indicate that the message was handled, or false to let + // the message be handled in the default way. + virtual bool OnMessageReceived(const Message& message); + + // Called to query the Message classes supported by the filter. Return + // false to indicate that all message types should reach the filter, or true + // if the resulting contents of |supported_message_classes| may be used to + // selectively offer messages of a particular class to the filter. + virtual bool GetSupportedMessageClasses( + std::vector<uint32>* supported_message_classes) const; + + protected: + virtual ~MessageFilter(); + + private: + friend class base::RefCountedThreadSafe<MessageFilter>; +}; + +} // namespace IPC + +#endif // IPC_MESSAGE_FILTER_H_ diff --git a/chromium/ipc/message_filter_router.cc b/chromium/ipc/message_filter_router.cc new file mode 100644 index 00000000000..81e40289147 --- /dev/null +++ b/chromium/ipc/message_filter_router.cc @@ -0,0 +1,92 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ipc/message_filter_router.h" + +#include "ipc/ipc_message_macros.h" +#include "ipc/ipc_message_utils.h" +#include "ipc/message_filter.h" + +namespace IPC { + +namespace { + +bool TryFiltersImpl(MessageFilterRouter::MessageFilters& filters, + const IPC::Message& message) { + for (size_t i = 0; i < filters.size(); ++i) { + if (filters[i]->OnMessageReceived(message)) { + return true; + } + } + return false; +} + +bool RemoveFilterImpl(MessageFilterRouter::MessageFilters& filters, + MessageFilter* filter) { + MessageFilterRouter::MessageFilters::iterator it = + std::remove(filters.begin(), filters.end(), filter); + if (it == filters.end()) + return false; + + filters.erase(it, filters.end()); + return true; +} + +bool ValidMessageClass(int message_class) { + return message_class >= 0 && message_class < LastIPCMsgStart; +} + +} // namespace + +MessageFilterRouter::MessageFilterRouter() {} +MessageFilterRouter::~MessageFilterRouter() {} + +void MessageFilterRouter::AddFilter(MessageFilter* filter) { + // Determine if the filter should be applied to all messages, or only + // messages of a certain class. + std::vector<uint32> supported_message_classes; + if (filter->GetSupportedMessageClasses(&supported_message_classes)) { + DCHECK(!supported_message_classes.empty()); + for (size_t i = 0; i < supported_message_classes.size(); ++i) { + const int message_class = supported_message_classes[i]; + DCHECK(ValidMessageClass(message_class)); + // Safely ignore repeated subscriptions to a given message class for the + // current filter being added. + if (!message_class_filters_[message_class].empty() && + message_class_filters_[message_class].back() == filter) { + continue; + } + message_class_filters_[message_class].push_back(filter); + } + } else { + global_filters_.push_back(filter); + } +} + +void MessageFilterRouter::RemoveFilter(MessageFilter* filter) { + if (RemoveFilterImpl(global_filters_, filter)) + return; + + for (size_t i = 0; i < arraysize(message_class_filters_); ++i) + RemoveFilterImpl(message_class_filters_[i], filter); +} + +bool MessageFilterRouter::TryFilters(const Message& message) { + if (TryFiltersImpl(global_filters_, message)) + return true; + + const int message_class = IPC_MESSAGE_CLASS(message); + if (!ValidMessageClass(message_class)) + return false; + + return TryFiltersImpl(message_class_filters_[message_class], message); +} + +void MessageFilterRouter::Clear() { + global_filters_.clear(); + for (size_t i = 0; i < arraysize(message_class_filters_); ++i) + message_class_filters_[i].clear(); +} + +} // namespace IPC diff --git a/chromium/ipc/message_filter_router.h b/chromium/ipc/message_filter_router.h new file mode 100644 index 00000000000..183b8ebc8a9 --- /dev/null +++ b/chromium/ipc/message_filter_router.h @@ -0,0 +1,42 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef IPC_MESSAGE_FILTER_ROUTER_H_ +#define IPC_MESSAGE_FILTER_ROUTER_H_ + +#include <vector> + +#include "ipc/ipc_message_start.h" + +namespace IPC { + +class Message; +class MessageFilter; + +class MessageFilterRouter { + public: + typedef std::vector<MessageFilter*> MessageFilters; + + MessageFilterRouter(); + ~MessageFilterRouter(); + + void AddFilter(MessageFilter* filter); + void RemoveFilter(MessageFilter* filter); + bool TryFilters(const Message& message); + void Clear(); + + private: + // List of global and selective filters; a given filter will exist in either + // |message_global_filters_| OR |message_class_filters_|, but not both. + // Note that |message_global_filters_| will be given first offering of any + // given message. It's the filter implementer and installer's + // responsibility to ensure that a filter is either global or selective to + // ensure proper message filtering order. + MessageFilters global_filters_; + MessageFilters message_class_filters_[LastIPCMsgStart]; +}; + +} // namespace IPC + +#endif // IPC_MESSAGE_FILTER_ROUTER_H_ diff --git a/chromium/ipc/sync_socket_unittest.cc b/chromium/ipc/sync_socket_unittest.cc index 288860713ad..5527abc4b53 100644 --- a/chromium/ipc/sync_socket_unittest.cc +++ b/chromium/ipc/sync_socket_unittest.cc @@ -108,11 +108,11 @@ class SyncSocketServerListener : public IPC::Listener { MULTIPROCESS_IPC_TEST_CLIENT_MAIN(SyncSocketServerClient) { base::MessageLoopForIO main_message_loop; SyncSocketServerListener listener; - IPC::Channel channel(IPCTestBase::GetChannelName("SyncSocketServerClient"), - IPC::Channel::MODE_CLIENT, - &listener); - EXPECT_TRUE(channel.Connect()); - listener.Init(&channel); + scoped_ptr<IPC::Channel> channel(IPC::Channel::CreateClient( + IPCTestBase::GetChannelName("SyncSocketServerClient"), + &listener)); + EXPECT_TRUE(channel->Connect()); + listener.Init(channel.get()); base::MessageLoop::current()->Run(); return 0; } diff --git a/chromium/ipc/unix_domain_socket_util.cc b/chromium/ipc/unix_domain_socket_util.cc index 10df9b21441..c29ce929f92 100644 --- a/chromium/ipc/unix_domain_socket_util.cc +++ b/chromium/ipc/unix_domain_socket_util.cc @@ -13,6 +13,7 @@ #include "base/file_util.h" #include "base/files/file_path.h" +#include "base/files/scoped_file.h" #include "base/logging.h" #include "base/posix/eintr_wrapper.h" @@ -45,15 +46,14 @@ int MakeUnixAddrForPath(const std::string& socket_name, } // Create socket. - int fd = socket(AF_UNIX, SOCK_STREAM, 0); - if (fd < 0) { + base::ScopedFD fd(socket(AF_UNIX, SOCK_STREAM, 0)); + if (!fd.is_valid()) { PLOG(ERROR) << "socket"; return -1; } - file_util::ScopedFD scoped_fd(&fd); // Make socket non-blocking - if (HANDLE_EINTR(fcntl(fd, F_SETFL, O_NONBLOCK)) < 0) { + if (HANDLE_EINTR(fcntl(fd.get(), F_SETFL, O_NONBLOCK)) < 0) { PLOG(ERROR) << "fcntl(O_NONBLOCK)"; return -1; } @@ -64,7 +64,7 @@ int MakeUnixAddrForPath(const std::string& socket_name, strncpy(unix_addr->sun_path, socket_name.c_str(), kMaxSocketNameLength); *unix_addr_len = offsetof(struct sockaddr_un, sun_path) + socket_name.length(); - return *scoped_fd.release(); + return fd.release(); } } // namespace @@ -78,10 +78,10 @@ bool CreateServerUnixDomainSocket(const base::FilePath& socket_path, struct sockaddr_un unix_addr; size_t unix_addr_len; - int fd = MakeUnixAddrForPath(socket_name, &unix_addr, &unix_addr_len); - if (fd < 0) + base::ScopedFD fd( + MakeUnixAddrForPath(socket_name, &unix_addr, &unix_addr_len)); + if (!fd.is_valid()) return false; - file_util::ScopedFD scoped_fd(&fd); // Make sure the path we need exists. if (!base::CreateDirectory(socket_dir)) { @@ -96,20 +96,20 @@ bool CreateServerUnixDomainSocket(const base::FilePath& socket_path, } // Bind the socket. - if (bind(fd, reinterpret_cast<const sockaddr*>(&unix_addr), + if (bind(fd.get(), reinterpret_cast<const sockaddr*>(&unix_addr), unix_addr_len) < 0) { PLOG(ERROR) << "bind " << socket_path.value(); return false; } // Start listening on the socket. - if (listen(fd, SOMAXCONN) < 0) { + if (listen(fd.get(), SOMAXCONN) < 0) { PLOG(ERROR) << "listen " << socket_path.value(); unlink(socket_name.c_str()); return false; } - *server_listen_fd = *scoped_fd.release(); + *server_listen_fd = fd.release(); return true; } @@ -122,18 +122,18 @@ bool CreateClientUnixDomainSocket(const base::FilePath& socket_path, struct sockaddr_un unix_addr; size_t unix_addr_len; - int fd = MakeUnixAddrForPath(socket_name, &unix_addr, &unix_addr_len); - if (fd < 0) + base::ScopedFD fd( + MakeUnixAddrForPath(socket_name, &unix_addr, &unix_addr_len)); + if (!fd.is_valid()) return false; - file_util::ScopedFD scoped_fd(&fd); - if (HANDLE_EINTR(connect(fd, reinterpret_cast<sockaddr*>(&unix_addr), + if (HANDLE_EINTR(connect(fd.get(), reinterpret_cast<sockaddr*>(&unix_addr), unix_addr_len)) < 0) { PLOG(ERROR) << "connect " << socket_path.value(); return false; } - *client_socket = *scoped_fd.release(); + *client_socket = fd.release(); return true; } @@ -184,18 +184,17 @@ bool ServerAcceptConnection(int server_listen_fd, int* server_socket) { DCHECK(server_socket); *server_socket = -1; - int accept_fd = HANDLE_EINTR(accept(server_listen_fd, NULL, 0)); - if (accept_fd < 0) + base::ScopedFD accept_fd(HANDLE_EINTR(accept(server_listen_fd, NULL, 0))); + if (!accept_fd.is_valid()) return IsRecoverableError(errno); - file_util::ScopedFD scoped_fd(&accept_fd); - if (HANDLE_EINTR(fcntl(accept_fd, F_SETFL, O_NONBLOCK)) < 0) { - PLOG(ERROR) << "fcntl(O_NONBLOCK) " << accept_fd; + if (HANDLE_EINTR(fcntl(accept_fd.get(), F_SETFL, O_NONBLOCK)) < 0) { + PLOG(ERROR) << "fcntl(O_NONBLOCK) " << accept_fd.get(); // It's safe to keep listening on |server_listen_fd| even if the attempt to // set O_NONBLOCK failed on the client fd. return true; } - *server_socket = *scoped_fd.release(); + *server_socket = accept_fd.release(); return true; } |