summaryrefslogtreecommitdiffstats
path: root/chrome
diff options
context:
space:
mode:
authoragl@chromium.org <agl@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-02-07 00:39:26 +0000
committeragl@chromium.org <agl@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-02-07 00:39:26 +0000
commit526776cf6ba5d6370ef28c9b483141fb0e4b1ae6 (patch)
tree5dd943dee4f2c74661c97f09a04b2358b16e3185 /chrome
parent95284326ea69903454907a200ad43ec41d158105 (diff)
downloadchromium_src-526776cf6ba5d6370ef28c9b483141fb0e4b1ae6.zip
chromium_src-526776cf6ba5d6370ef28c9b483141fb0e4b1ae6.tar.gz
chromium_src-526776cf6ba5d6370ef28c9b483141fb0e4b1ae6.tar.bz2
FileDescriptor: passing fds over IPC
This patch introduces a FileDescriptor object which can be included in IPC messages and will perform the magic needed to pass file descriptors over IPC. After some consideration, Windows will continue to do the current DuplicateHandle tricks. Review URL: http://codereview.chromium.org/20027 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@9369 0039d316-1c4b-4281-b951-d872f2087c98
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
};