summaryrefslogtreecommitdiffstats
path: root/chrome
diff options
context:
space:
mode:
Diffstat (limited to 'chrome')
-rw-r--r--chrome/chrome.xcodeproj/project.pbxproj4
-rw-r--r--chrome/common/common.scons3
-rw-r--r--chrome/common/file_descriptor_posix.cc82
-rw-r--r--chrome/common/file_descriptor_posix.h115
-rw-r--r--chrome/common/ipc_channel_posix.cc121
-rw-r--r--chrome/common/ipc_channel_posix.h15
-rw-r--r--chrome/common/ipc_message.cc7
-rw-r--r--chrome/common/ipc_message.h20
-rw-r--r--chrome/common/ipc_message_utils.h32
-rw-r--r--chrome/common/ipc_tests.cc92
-rw-r--r--chrome/common/ipc_tests.h1
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
};