diff options
Diffstat (limited to 'chrome')
-rw-r--r-- | chrome/chrome.xcodeproj/project.pbxproj | 4 | ||||
-rw-r--r-- | chrome/common/common.scons | 3 | ||||
-rw-r--r-- | chrome/common/file_descriptor_posix.cc | 82 | ||||
-rw-r--r-- | chrome/common/file_descriptor_posix.h | 115 | ||||
-rw-r--r-- | chrome/common/ipc_channel_posix.cc | 121 | ||||
-rw-r--r-- | chrome/common/ipc_channel_posix.h | 15 | ||||
-rw-r--r-- | chrome/common/ipc_message.cc | 7 | ||||
-rw-r--r-- | chrome/common/ipc_message.h | 20 | ||||
-rw-r--r-- | chrome/common/ipc_message_utils.h | 32 | ||||
-rw-r--r-- | chrome/common/ipc_tests.cc | 92 | ||||
-rw-r--r-- | chrome/common/ipc_tests.h | 1 |
11 files changed, 484 insertions, 8 deletions
diff --git a/chrome/chrome.xcodeproj/project.pbxproj b/chrome/chrome.xcodeproj/project.pbxproj index fed5650..a104874 100644 --- a/chrome/chrome.xcodeproj/project.pbxproj +++ b/chrome/chrome.xcodeproj/project.pbxproj @@ -266,6 +266,7 @@ 4DCE9E2D0EF0B8C000682526 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4DCE9E2B0EF0B8C000682526 /* MainMenu.xib */; }; 4DDC644B0EAE390800FB5EBE /* libxml.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4D7BFB230E9D4BBF009A6919 /* libxml.a */; }; 4DDC64580EAE394200FB5EBE /* libzlib.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4DDC64550EAE392400FB5EBE /* libzlib.a */; }; + 50886B71DAE5D39B7B066A3E /* file_descriptor_posix.cc in Sources */ = {isa = PBXBuildFile; fileRef = 4BC2B5751BE559198D20DD1E /* file_descriptor_posix.cc */; }; 534E66C40F311BEC0006B2B2 /* temp_scaffolding_stubs.cc in Sources */ = {isa = PBXBuildFile; fileRef = 534E66C30F311BEC0006B2B2 /* temp_scaffolding_stubs.cc */; }; 544FBC49CB83E458B6B7069D /* test_web_contents.cc in Sources */ = {isa = PBXBuildFile; fileRef = 56E1D7DF17D327BFCB0B895D /* test_web_contents.cc */; }; 671555F7DF06E224B646E5D2 /* backing_store_posix.cc in Sources */ = {isa = PBXBuildFile; fileRef = B94B5B0CBF4D7FAC48BB1AE2 /* backing_store_posix.cc */; }; @@ -1729,6 +1730,7 @@ 37521A11B07C479E93A39D52 /* user_script_unittest.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = user_script_unittest.cc; path = common/extensions/user_script_unittest.cc; sourceTree = SOURCE_ROOT; }; 3CCF8AA8A56FF8FE59F0C299 /* template_url.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = template_url.cc; sourceTree = "<group>"; }; 433B6EFB7A1D931A13C9556F /* url_fetcher_protect.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = url_fetcher_protect.h; sourceTree = "<group>"; }; + 4BC2B5751BE559198D20DD1E /* file_descriptor_posix.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = file_descriptor_posix.cc; sourceTree = "<group>"; }; 4D1F59EA0F2A6B590040C1E3 /* image_diff */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = image_diff; sourceTree = BUILT_PRODUCTS_DIR; }; 4D1F59FD0F2A6BBB0040C1E3 /* image_diff.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = image_diff.cc; sourceTree = "<group>"; }; 4D1F5AB10F2A6EE90040C1E3 /* libpng.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = libpng.xcodeproj; path = third_party/libpng/libpng.xcodeproj; sourceTree = "<group>"; }; @@ -3499,6 +3501,7 @@ 4D7BFBA40E9D4C9F009A6919 /* drag_drop_types.h */, 4D7BFBA50E9D4C9F009A6919 /* env_vars.cc */, 4D7BFBA60E9D4C9F009A6919 /* env_vars.h */, + 4BC2B5751BE559198D20DD1E /* file_descriptor_posix.cc */, 4D7BFBA70E9D4C9F009A6919 /* filter_policy.h */, 4D7BFBA80E9D4C9F009A6919 /* gears_api.h */, 4D7BFBAA0E9D4C9F009A6919 /* ipc_channel.h */, @@ -5304,6 +5307,7 @@ 4D7BFCCE0E9D4D7A009A6919 /* cookie_monster_sqlite.cc in Sources */, 4D7BFC2E0E9D4CF5009A6919 /* debug_flags.cc in Sources */, 4D7BFC330E9D4CF9009A6919 /* env_vars.cc in Sources */, + 50886B71DAE5D39B7B066A3E /* file_descriptor_posix.cc in Sources */, B5FDC0580EE488E500BEC6E6 /* ipc_channel_posix.cc in Sources */, B5DBEA900EFC60E200C95176 /* ipc_channel_proxy.cc in Sources */, 4D7BFC380E9D4CFF009A6919 /* ipc_message.cc in Sources */, diff --git a/chrome/common/common.scons b/chrome/common/common.scons index 71f1761..7eca809 100644 --- a/chrome/common/common.scons +++ b/chrome/common/common.scons @@ -218,7 +218,8 @@ input_files = ChromeFileList([ if not env.Bit('windows'): # TODO(port): This is temporary so we can link. input_files.Append( - 'temp_scaffolding_stubs.cc' + 'temp_scaffolding_stubs.cc', + 'file_descriptor_posix.cc', ) # TODO(port): Port these. diff --git a/chrome/common/file_descriptor_posix.cc b/chrome/common/file_descriptor_posix.cc new file mode 100644 index 0000000..b3dc4a0 --- /dev/null +++ b/chrome/common/file_descriptor_posix.cc @@ -0,0 +1,82 @@ +// Copyright (c) 2006-2008 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 "chrome/common/file_descriptor_posix.h" + +#include "base/logging.h" + +DescriptorSet::DescriptorSet() + : next_descriptor_(0) { +} + +DescriptorSet::~DescriptorSet() { + if (next_descriptor_ == descriptors_.size()) + return; + + LOG(WARNING) << "DescriptorSet destroyed with unconsumed descriptors"; + // We close all the descriptors where the close flag is set. If this + // message should have been transmitted, then closing those with close + // flags set mirrors the expected behaviour. + // + // If this message was received with more descriptors than expected + // (which could a DOS against the browser by a rouge renderer) then all + // the descriptors have their close flag set and we free all the extra + // kernel resources. + for (unsigned i = next_descriptor_; i < descriptors_.size(); ++i) { + if (descriptors_[i].auto_close) + close(descriptors_[i].fd); + } +} + +void DescriptorSet::Add(int fd) { + struct FileDescriptor sd; + sd.fd = fd; + sd.auto_close = false; + descriptors_.push_back(sd); + DCHECK(descriptors_.size() <= MAX_DESCRIPTORS_PER_MESSAGE); +} + +void DescriptorSet::AddAndAutoClose(int fd) { + struct FileDescriptor sd; + sd.fd = fd; + sd.auto_close = true; + descriptors_.push_back(sd); + DCHECK(descriptors_.size() <= MAX_DESCRIPTORS_PER_MESSAGE); +} + +int DescriptorSet::NextDescriptor() { + if (next_descriptor_ == descriptors_.size()) + return -1; + + return descriptors_[next_descriptor_++].fd; +} + +void DescriptorSet::GetDescriptors(int* buffer) const { + for (std::vector<FileDescriptor>::const_iterator + i = descriptors_.begin(); i != descriptors_.end(); ++i) { + *(buffer++) = i->fd; + } +} + +void DescriptorSet::CommitAll() { + for (std::vector<FileDescriptor>::iterator + i = descriptors_.begin(); i != descriptors_.end(); ++i) { + if (i->auto_close) + close(i->fd); + } + descriptors_.clear(); +} + +void DescriptorSet::SetDescriptors(const int* buffer, unsigned count) { + DCHECK(count <= MAX_DESCRIPTORS_PER_MESSAGE); + DCHECK(descriptors_.size() == 0); + + descriptors_.reserve(count); + for (unsigned i = 0; i < count; ++i) { + struct FileDescriptor sd; + sd.fd = buffer[i]; + sd.auto_close = true; + descriptors_.push_back(sd); + } +} diff --git a/chrome/common/file_descriptor_posix.h b/chrome/common/file_descriptor_posix.h new file mode 100644 index 0000000..533992e --- /dev/null +++ b/chrome/common/file_descriptor_posix.h @@ -0,0 +1,115 @@ +// Copyright (c) 2006-2009 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 CHROME_COMMON_FILE_DESCRIPTOR_POSIX_H_ +#define CHROME_COMMON_FILE_DESCRIPTOR_POSIX_H_ + +#include <vector> + +// ----------------------------------------------------------------------------- +// A FileDescriptor is a structure for use in IPC messages. It allows one to +// send descriptors over an IPC channel. +// +// In the Windows world, processes can peek and poke the HANDLE table of other +// processes. On POSIX, in order to transmit descriptors we need to include +// them in a control-message (a side-channel on the UNIX domain socket). +// Serialising this type adds descriptors to a vector in the IPC Message, from +// which the IPC channel can package them up for the kernel. +// ----------------------------------------------------------------------------- +struct FileDescriptor { + FileDescriptor() + : fd(-1), + auto_close(false) { } + + int fd; + // If true, close this descriptor after it has been sent. + bool auto_close; +}; + +// ----------------------------------------------------------------------------- +// A DescriptorSet is an ordered set of POSIX file descriptors. These are +// associated with IPC messages so that descriptors can be transmitted over a +// UNIX domain socket. +// ----------------------------------------------------------------------------- +class DescriptorSet { + public: + DescriptorSet(); + ~DescriptorSet(); + + // This is the maximum number of descriptors per message. We need to know this + // because the control message kernel interface has to be given a buffer which + // is large enough to store all the descriptor numbers. Otherwise the kernel + // tells us that it truncated the control data and the extra descriptors are + // lost. + // + // In debugging mode, it's a fatal error to try and add more than this number + // of descriptors to a DescriptorSet. + enum { + MAX_DESCRIPTORS_PER_MESSAGE = 4, + }; + + // --------------------------------------------------------------------------- + // Interfaces for building during message serialisation... + + // Add a descriptor to the end of the set + void Add(int fd); + // Add a descriptor to the end of the set and automatically close it after + // transmission. + void AddAndAutoClose(int fd); + + // --------------------------------------------------------------------------- + + + // --------------------------------------------------------------------------- + // Interfaces for accessing during message deserialisation... + + // Return the number of descriptors remaining + unsigned size() const { return descriptors_.size() - next_descriptor_; } + // Return true if no unconsumed descriptors remain + bool empty() const { return descriptors_.size() == next_descriptor_; } + // Fetch the next descriptor from the beginning of the set. This interface is + // designed for the deserialising code as it doesn't support close flags. + // returns: file descriptor, or -1 on error + int NextDescriptor(); + + // --------------------------------------------------------------------------- + + + // --------------------------------------------------------------------------- + // Interfaces for transmission... + + // Fill an array with file descriptors without 'consuming' them. CommitAll + // must be called after these descriptors have been transmitted. + // buffer: (output) a buffer of, at least, size() integers. + void GetDescriptors(int* buffer) const; + // This must be called after transmitting the descriptors returned by + // GetDescriptors. It marks all the descriptors as consumed and closes those + // which are auto-close. + void CommitAll(); + + // --------------------------------------------------------------------------- + + + // --------------------------------------------------------------------------- + // Interfaces for receiving... + + // Set the contents of the set from the given buffer. This set must be empty + // before calling. The auto-close flag is set on all the descriptors so that + // unconsumed descriptors are closed on destruction. + void SetDescriptors(const int* buffer, unsigned count); + + // --------------------------------------------------------------------------- + + private: + // A vector of descriptors and close flags. If this message is sent, then + // these descriptors are sent as control data. After sending, any descriptors + // with a true flag are closed. If this message has been received, then these + // are the descriptors which were received and all close flags are true. + std::vector<FileDescriptor> descriptors_; + // When deserialising the message, the descriptors will be extracted + // one-by-one. This contains the index of the next unused descriptor. + unsigned next_descriptor_; +}; + +#endif // CHROME_COMMON_FILE_DESCRIPTOR_POSIX_H_ diff --git a/chrome/common/ipc_channel_posix.cc b/chrome/common/ipc_channel_posix.cc index a7bbb56..bc77552 100644 --- a/chrome/common/ipc_channel_posix.cc +++ b/chrome/common/ipc_channel_posix.cc @@ -28,6 +28,7 @@ #include "base/singleton.h" #include "chrome/common/chrome_counters.h" #include "chrome/common/chrome_switches.h" +#include "chrome/common/file_descriptor_posix.h" #include "chrome/common/ipc_message_utils.h" namespace IPC { @@ -357,16 +358,24 @@ bool Channel::ChannelImpl::Connect() { bool Channel::ChannelImpl::ProcessIncomingMessages() { ssize_t bytes_read = 0; + struct msghdr msg = {0}; + struct iovec iov = {input_buf_, Channel::kReadBufferSize}; + + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = input_cmsg_buf_; + msg.msg_controllen = sizeof(input_cmsg_buf_); + for (;;) { if (bytes_read == 0) { if (pipe_ == -1) return false; // Read from pipe. - // recv() returns 0 if the connection has closed or EAGAIN if no data is - // waiting on the pipe. + // recvmsg() returns 0 if the connection has closed or EAGAIN if no data + // is waiting on the pipe. do { - bytes_read = read(pipe_, input_buf_, Channel::kReadBufferSize); + bytes_read = recvmsg(pipe_, &msg, MSG_DONTWAIT); } while (bytes_read == -1 && errno == EINTR); if (bytes_read < 0) { @@ -390,6 +399,33 @@ bool Channel::ChannelImpl::ProcessIncomingMessages() { client_pipe_ = -1; } + // a pointer to an array of |num_wire_fds| file descriptors from the read + const int* wire_fds; + unsigned num_wire_fds = 0; + + // walk the list of control messages and, if we find an array of file + // descriptors, save a pointer to the array + for (struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); cmsg; + cmsg = CMSG_NXTHDR(&msg, cmsg)) { + if (cmsg->cmsg_level == SOL_SOCKET && + cmsg->cmsg_type == SCM_RIGHTS) { + const unsigned payload_len = cmsg->cmsg_len - CMSG_LEN(0); + DCHECK(payload_len % sizeof(int) == 0); + wire_fds = reinterpret_cast<int*>(CMSG_DATA(cmsg)); + num_wire_fds = payload_len / 4; + + if (msg.msg_flags & MSG_CTRUNC) { + LOG(ERROR) << "SCM_RIGHTS message was truncated" + << " cmsg_len:" << cmsg->cmsg_len + << " fd:" << pipe_; + for (unsigned i = 0; i < num_wire_fds; ++i) + close(wire_fds[i]); + return false; + } + break; + } + } + // Process messages from input buffer. const char *p; const char *end; @@ -408,11 +444,50 @@ bool Channel::ChannelImpl::ProcessIncomingMessages() { end = p + input_overflow_buf_.size(); } + // A pointer to an array of |num_fds| file descriptors which includes any + // fds that have spilled over from a previous read. + const int* fds; + unsigned num_fds; + unsigned fds_i = 0; // the index of the first unused descriptor + + if (input_overflow_fds_.empty()) { + fds = wire_fds; + num_fds = num_wire_fds; + } else { + const size_t prev_size = input_overflow_fds_.size(); + input_overflow_fds_.resize(prev_size + num_wire_fds); + memcpy(&input_overflow_fds_[prev_size], wire_fds, + num_wire_fds * sizeof(int)); + fds = &input_overflow_fds_[0]; + num_fds = input_overflow_fds_.size(); + } + while (p < end) { const char* message_tail = Message::FindNext(p, end); if (message_tail) { int len = static_cast<int>(message_tail - p); const Message m(p, len); + if (m.header()->num_fds) { + // the message has file descriptors + if (m.header()->num_fds > num_fds - fds_i) { + // the message has been completely received, but we didn't get + // enough file descriptors. + LOG(WARNING) << "Message needs unreceived descriptors" + << " channel:" << this + << " message-type:" << m.type() + << " header()->num_fds:" << m.header()->num_fds + << " num_fds:" << num_fds + << " fds_i:" << fds_i; + // close the existing file descriptors so that we don't leak them + for (unsigned i = fds_i; i < num_fds; ++i) + close(fds[i]); + input_overflow_fds_.clear(); + return false; + } + + m.descriptor_set()->SetDescriptors(&fds[fds_i], m.header()->num_fds); + fds_i += m.header()->num_fds; + } #ifdef IPC_MESSAGE_DEBUG_EXTRA DLOG(INFO) << "received message on channel @" << this << " with type " << m.type(); @@ -431,6 +506,7 @@ bool Channel::ChannelImpl::ProcessIncomingMessages() { } } input_overflow_buf_.assign(p, end - p); + input_overflow_fds_ = std::vector<int>(&fds[fds_i], &fds[num_fds]); bytes_read = 0; // Get more data. } @@ -460,7 +536,37 @@ bool Channel::ChannelImpl::ProcessOutgoingMessages() { message_send_bytes_written_; ssize_t bytes_written = -1; do { - bytes_written = write(pipe_, out_bytes, amt_to_write); + struct msghdr msgh = {0}; + struct iovec iov = {const_cast<char*>(out_bytes), amt_to_write}; + msgh.msg_iov = &iov; + msgh.msg_iovlen = 1; + char buf[CMSG_SPACE( + sizeof(int[DescriptorSet::MAX_DESCRIPTORS_PER_MESSAGE]))]; + + if (message_send_bytes_written_ == 0 && + !msg->descriptor_set()->empty()) { + // This is the first chunk of a message which has descriptors to send + struct cmsghdr *cmsg; + const unsigned num_fds = msg->descriptor_set()->size(); + + DCHECK_LE(num_fds, DescriptorSet::MAX_DESCRIPTORS_PER_MESSAGE); + + msgh.msg_control = buf; + msgh.msg_controllen = CMSG_SPACE(sizeof(int) * num_fds); + cmsg = CMSG_FIRSTHDR(&msgh); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + cmsg->cmsg_len = CMSG_LEN(sizeof(int) * num_fds); + msg->descriptor_set()->GetDescriptors( + reinterpret_cast<int*>(CMSG_DATA(cmsg))); + msgh.msg_controllen = cmsg->cmsg_len; + + msg->header()->num_fds = num_fds; + } + + bytes_written = sendmsg(pipe_, &msgh, MSG_DONTWAIT); + if (bytes_written > 0) + msg->descriptor_set()->CommitAll(); } while (bytes_written == -1 && errno == EINTR); if (bytes_written < 0 && errno != EAGAIN) { @@ -621,6 +727,13 @@ void Channel::ChannelImpl::Close() { output_queue_.pop(); delete m; } + + // Close any outstanding, received file descriptors + for (std::vector<int>::iterator + i = input_overflow_fds_.begin(); i != input_overflow_fds_.end(); ++i) { + close(*i); + } + input_overflow_fds_.clear(); } //------------------------------------------------------------------------------ diff --git a/chrome/common/ipc_channel_posix.h b/chrome/common/ipc_channel_posix.h index cdc0db2..4f4e0ef 100644 --- a/chrome/common/ipc_channel_posix.h +++ b/chrome/common/ipc_channel_posix.h @@ -7,10 +7,14 @@ #include "chrome/common/ipc_channel.h" +#include <sys/socket.h> // for CMSG macros + #include <queue> #include <string> +#include <vector> #include "base/message_loop.h" +#include "chrome/common/file_descriptor_posix.h" namespace IPC { @@ -68,9 +72,20 @@ class Channel::ChannelImpl : public MessageLoopForIO::Watcher { // We read from the pipe into this buffer char input_buf_[Channel::kReadBufferSize]; + enum { + // We assume a worst case: kReadBufferSize bytes of messages, where each + // message has no payload and a full complement of descriptors. + MAX_READ_FDS = (Channel::kReadBufferSize / sizeof(IPC::Message::Header)) * + DescriptorSet::MAX_DESCRIPTORS_PER_MESSAGE, + }; + + // This is a control message buffer large enough to hold kMaxReadFDs + char input_cmsg_buf_[CMSG_SPACE(sizeof(int) * MAX_READ_FDS)]; + // Large messages that span multiple pipe buffers, get built-up using // this buffer. std::string input_overflow_buf_; + std::vector<int> input_overflow_fds_; // In server-mode, we have to wait for the client to connect before we // can begin reading. We make use of the input_state_ when performing diff --git a/chrome/common/ipc_message.cc b/chrome/common/ipc_message.cc index fe61423..67dfad8 100644 --- a/chrome/common/ipc_message.cc +++ b/chrome/common/ipc_message.cc @@ -5,6 +5,7 @@ #include "chrome/common/ipc_message.h" #include "base/logging.h" +#include "build/build_config.h" namespace IPC { @@ -16,6 +17,9 @@ Message::~Message() { Message::Message() : Pickle(sizeof(Header)) { header()->routing = header()->type = header()->flags = 0; +#if defined(OS_POSIX) + header()->num_fds = 0; +#endif InitLoggingVariables(); } @@ -24,6 +28,9 @@ Message::Message(int32 routing_id, uint16 type, PriorityValue priority) header()->routing = routing_id; header()->type = type; header()->flags = priority; +#if defined(OS_POSIX) + header()->num_fds = 0; +#endif InitLoggingVariables(); } diff --git a/chrome/common/ipc_message.h b/chrome/common/ipc_message.h index 040a346..0af3b44 100644 --- a/chrome/common/ipc_message.h +++ b/chrome/common/ipc_message.h @@ -16,6 +16,8 @@ #ifndef NDEBUG #define IPC_MESSAGE_LOG_ENABLED #endif +#elif defined(OS_POSIX) +#include "chrome/common/file_descriptor_posix.h" #endif namespace IPC { @@ -159,6 +161,10 @@ class Message : public Pickle { return Pickle::FindNext(sizeof(Header), range_start, range_end); } +#if defined(OS_POSIX) + DescriptorSet* descriptor_set() const { return &descriptor_set_; } +#endif + #ifdef IPC_MESSAGE_LOG_ENABLED // Adds the outgoing time from Time::Now() at the end of the message and sets // a bit to indicate that it's been added. @@ -201,9 +207,12 @@ class Message : public Pickle { #pragma pack(push, 2) struct Header : Pickle::Header { - int32 routing; // ID of the view that this message is destined for - uint16 type; // specifies the user-defined message type - uint16 flags; // specifies control flags for the message + int32 routing; // ID of the view that this message is destined for + uint16 type; // specifies the user-defined message type + uint16 flags; // specifies control flags for the message +#if defined(OS_POSIX) + uint32 num_fds; // the number of descriptors included with this message +#endif }; #pragma pack(pop) @@ -216,6 +225,11 @@ class Message : public Pickle { void InitLoggingVariables(); +#if defined(OS_POSIX) + // The set of file descriptors associated with this message. + mutable DescriptorSet descriptor_set_; +#endif + #ifdef IPC_MESSAGE_LOG_ENABLED // Used for logging. mutable int64 received_time_; diff --git a/chrome/common/ipc_message_utils.h b/chrome/common/ipc_message_utils.h index 64f5653..7887dcc 100644 --- a/chrome/common/ipc_message_utils.h +++ b/chrome/common/ipc_message_utils.h @@ -12,6 +12,9 @@ #include "base/file_path.h" #include "base/string_util.h" #include "base/tuple.h" +#if defined(OS_POSIX) +#include "chrome/common/file_descriptor_posix.h" +#endif #include "chrome/common/ipc_sync_message.h" #include "chrome/common/thumbnail_score.h" #include "webkit/glue/cache_manager.h" @@ -662,6 +665,35 @@ struct ParamTraits<gfx::Size> { static void Log(const param_type& p, std::wstring* l); }; +#if defined(OS_POSIX) + +template<> +struct ParamTraits<FileDescriptor> { + typedef FileDescriptor param_type; + static void Write(Message* m, const param_type& p) { + if (p.auto_close) { + m->descriptor_set()->AddAndAutoClose(p.fd); + } else { + m->descriptor_set()->Add(p.fd); + } + } + static bool Read(const Message* m, void** iter, param_type* r) { + r->auto_close = false; + r->fd = m->descriptor_set()->NextDescriptor(); + + return r->fd >= 0; + } + static void Log(const param_type& p, std::wstring* l) { + if (p.auto_close) { + l->append(StringPrintf(L"FD(%d auto-close)", p.fd)); + } else { + l->append(StringPrintf(L"FD(%d)", p.fd)); + } + } +}; + +#endif // defined(OS_POSIX) + template<> struct ParamTraits<ThumbnailScore> { typedef ThumbnailScore param_type; diff --git a/chrome/common/ipc_tests.cc b/chrome/common/ipc_tests.cc index 695f0b4..7dd5f0af 100644 --- a/chrome/common/ipc_tests.cc +++ b/chrome/common/ipc_tests.cc @@ -20,6 +20,9 @@ #include "base/test_suite.h" #include "base/thread.h" #include "chrome/common/chrome_switches.h" +#if defined(OS_POSIX) +#include "chrome/common/file_descriptor_posix.h" +#endif #include "chrome/common/ipc_channel.h" #include "chrome/common/ipc_channel_proxy.h" #include "chrome/common/ipc_message_utils.h" @@ -93,6 +96,12 @@ base::ProcessHandle IPCChannelTest::SpawnChild(ChildType child_type, debug_on_start); channel->OnClientConnected(); break; + case TEST_DESCRIPTOR_CLIENT: + ret = MultiProcessTest::SpawnChild(L"RunTestDescriptorClient", + fds_to_map, + debug_on_start); + channel->OnClientConnected(); + break; case TEST_REFLECTOR: ret = MultiProcessTest::SpawnChild(L"RunReflector", fds_to_map, @@ -217,6 +226,88 @@ TEST_F(IPCChannelTest, ChannelTest) { EXPECT_TRUE(base::WaitForSingleProcess(process_handle, 5000)); } +#if defined(OS_POSIX) + +class MyChannelDescriptorListener : public IPC::Channel::Listener { + public: + virtual void OnMessageReceived(const IPC::Message& message) { + void* iter = NULL; + + FileDescriptor descriptor; + + ASSERT_TRUE( + IPC::ParamTraits<FileDescriptor>::Read(&message, &iter, &descriptor)); + VerifyDescriptor(&descriptor); + MessageLoop::current()->Quit(); + } + + virtual void OnChannelError() { + MessageLoop::current()->Quit(); + } + +private: + static void VerifyDescriptor(FileDescriptor* descriptor) { + const int fd = open("/dev/null", O_RDONLY); + struct stat st1, st2; + fstat(fd, &st1); + close(fd); + fstat(descriptor->fd, &st2); + close(descriptor->fd); + ASSERT_EQ(st1.st_ino, st2.st_ino); + } +}; + +TEST_F(IPCChannelTest, DescriptorTest) { + // Setup IPC channel. + MyChannelDescriptorListener listener; + + IPC::Channel chan(kTestClientChannel, IPC::Channel::MODE_SERVER, + &listener); + chan.Connect(); + + base::ProcessHandle process_handle = SpawnChild(TEST_DESCRIPTOR_CLIENT, + &chan); + ASSERT_TRUE(process_handle); + + FileDescriptor descriptor; + const int fd = open("/dev/null", O_RDONLY); + ASSERT_GE(fd, 0); + descriptor.auto_close = true; + descriptor.fd = fd; + + IPC::Message* message = new IPC::Message(0, // routing_id + 3, // message type + IPC::Message::PRIORITY_NORMAL); + IPC::ParamTraits<FileDescriptor>::Write(message, descriptor); + chan.Send(message); + + // Run message loop. + MessageLoop::current()->Run(); + + // Close Channel so client gets its OnChannelError() callback fired. + chan.Close(); + + // Cleanup child process. + EXPECT_TRUE(base::WaitForSingleProcess(process_handle, 5000)); +} + +MULTIPROCESS_TEST_MAIN(RunTestDescriptorClient) { + MessageLoopForIO main_message_loop; + MyChannelDescriptorListener listener; + + // setup IPC channel + IPC::Channel chan(kTestClientChannel, IPC::Channel::MODE_CLIENT, + &listener); + chan.Connect(); + + // run message loop + MessageLoop::current()->Run(); + // return true; + return NULL; +} + +#endif // defined(OS_POSIX) + TEST_F(IPCChannelTest, ChannelProxyTest) { // The thread needs to out-live the ChannelProxy. base::Thread thread("ChannelProxyTestServer"); @@ -277,6 +368,7 @@ MULTIPROCESS_TEST_MAIN(RunTestClient) { // return true; return NULL; } + #endif // !PERFORMANCE_TEST #ifdef PERFORMANCE_TEST diff --git a/chrome/common/ipc_tests.h b/chrome/common/ipc_tests.h index 09f3d12..609ebc6 100644 --- a/chrome/common/ipc_tests.h +++ b/chrome/common/ipc_tests.h @@ -12,6 +12,7 @@ // a client reflector and a IPC server used for fuzzing tests. enum ChildType { TEST_CLIENT, + TEST_DESCRIPTOR_CLIENT, TEST_REFLECTOR, FUZZER_SERVER }; |