From 5fe733dee6afd3ab897cafbfcdcc1450264409b0 Mon Sep 17 00:00:00 2001 From: "agl@chromium.org" Date: Wed, 11 Feb 2009 18:59:20 +0000 Subject: POSIX: Transfer network data using shared memory This patch adds the long planned support for sharing memory on POSIX by transporting file descriptors. It largely builds on the shared memory cleanup work by jrg. We move FileDescriptor out of chrome/common/file_descriptor_posix.h and into base/file_descriptor_posix.h. Since all that's left in the chrome/common verion is the DescriptorSet, those files are renamed to descriptor_set.[h|cc]. The SharedMemoryHandle on POSIX then becomes a typedef to a FileDescriptor and thus can be serialised over IPC. After that, it's mostly a case of cleaning up those snippets of code which considered SharedMemoryHandles to be scaler values. Review URL: http://codereview.chromium.org/21208 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@9580 0039d316-1c4b-4281-b951-d872f2087c98 --- chrome/common/common.scons | 2 +- chrome/common/descriptor_set_posix.cc | 100 ++++++++++++++++++++++++++ chrome/common/descriptor_set_posix.h | 109 +++++++++++++++++++++++++++++ chrome/common/file_descriptor_posix.cc | 90 ------------------------ chrome/common/file_descriptor_posix.h | 124 --------------------------------- chrome/common/ipc_channel_posix.cc | 2 +- chrome/common/ipc_channel_posix.h | 2 +- chrome/common/ipc_message.h | 2 +- chrome/common/ipc_message_utils.h | 12 ++-- chrome/common/ipc_send_fds_test.cc | 9 +-- chrome/common/ipc_tests.cc | 2 +- chrome/common/resource_dispatcher.cc | 3 +- 12 files changed, 228 insertions(+), 229 deletions(-) create mode 100644 chrome/common/descriptor_set_posix.cc create mode 100644 chrome/common/descriptor_set_posix.h delete mode 100644 chrome/common/file_descriptor_posix.cc delete mode 100644 chrome/common/file_descriptor_posix.h (limited to 'chrome/common') diff --git a/chrome/common/common.scons b/chrome/common/common.scons index 7eca809..829da06 100644 --- a/chrome/common/common.scons +++ b/chrome/common/common.scons @@ -219,7 +219,7 @@ if not env.Bit('windows'): # TODO(port): This is temporary so we can link. input_files.Append( 'temp_scaffolding_stubs.cc', - 'file_descriptor_posix.cc', + 'descriptor_set_posix.cc', ) # TODO(port): Port these. diff --git a/chrome/common/descriptor_set_posix.cc b/chrome/common/descriptor_set_posix.cc new file mode 100644 index 0000000..82f2899 --- /dev/null +++ b/chrome/common/descriptor_set_posix.cc @@ -0,0 +1,100 @@ +// 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/descriptor_set_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); + } +} + +bool DescriptorSet::Add(int fd) { + if (descriptors_.size() == MAX_DESCRIPTORS_PER_MESSAGE) + return false; + + struct base::FileDescriptor sd; + sd.fd = fd; + sd.auto_close = false; + descriptors_.push_back(sd); + return true; +} + +bool DescriptorSet::AddAndAutoClose(int fd) { + if (descriptors_.size() == MAX_DESCRIPTORS_PER_MESSAGE) + return false; + + struct base::FileDescriptor sd; + sd.fd = fd; + sd.auto_close = true; + descriptors_.push_back(sd); + DCHECK(descriptors_.size() <= MAX_DESCRIPTORS_PER_MESSAGE); + return true; +} + +int DescriptorSet::NextDescriptor() { + if (next_descriptor_ == descriptors_.size()) + return -1; + + return descriptors_[next_descriptor_++].fd; +} + +void DescriptorSet::GetDescriptors(int* buffer) const { + DCHECK_EQ(next_descriptor_, 0u); + + for (std::vector::const_iterator + i = descriptors_.begin(); i != descriptors_.end(); ++i) { + *(buffer++) = i->fd; + } +} + +void DescriptorSet::CommitAll() { + for (std::vector::iterator + i = descriptors_.begin(); i != descriptors_.end(); ++i) { + if (i->auto_close) + close(i->fd); + } + descriptors_.clear(); + next_descriptor_ = 0; +} + +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 base::FileDescriptor sd; + sd.fd = buffer[i]; + sd.auto_close = true; + descriptors_.push_back(sd); + } +} + +void DescriptorSet::TakeFrom(DescriptorSet* other) { + DCHECK(descriptors_.size() == 0); + + descriptors_.swap(other->descriptors_); + next_descriptor_ = other->next_descriptor_; + other->next_descriptor_ = 0; +} diff --git a/chrome/common/descriptor_set_posix.h b/chrome/common/descriptor_set_posix.h new file mode 100644 index 0000000..36a0433 --- /dev/null +++ b/chrome/common/descriptor_set_posix.h @@ -0,0 +1,109 @@ +// 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_DESCRIPTOR_SET_POSIX_H_ +#define CHROME_COMMON_DESCRIPTOR_SET_POSIX_H_ + +#include + +#include "base/basictypes.h" +#include "base/file_descriptor_posix.h" + +// ----------------------------------------------------------------------------- +// 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. Returns false iff the set is full. + bool Add(int fd); + // Add a descriptor to the end of the set and automatically close it after + // transmission. Returns false iff the set is full. + bool 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); + + // --------------------------------------------------------------------------- + + // --------------------------------------------------------------------------- + // Interfaces for IPC::Message... + + // Take all the FileDescriptors from another set. Just like a copy + // constructor, except that the source is emptied. + void TakeFrom(DescriptorSet* other); + + // --------------------------------------------------------------------------- + + 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 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_; + + DISALLOW_COPY_AND_ASSIGN(DescriptorSet); +}; + +#endif // CHROME_COMMON_FILE_DESCRIPTOR_POSIX_H_ diff --git a/chrome/common/file_descriptor_posix.cc b/chrome/common/file_descriptor_posix.cc deleted file mode 100644 index 7db9514..0000000 --- a/chrome/common/file_descriptor_posix.cc +++ /dev/null @@ -1,90 +0,0 @@ -// 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::const_iterator - i = descriptors_.begin(); i != descriptors_.end(); ++i) { - *(buffer++) = i->fd; - } -} - -void DescriptorSet::CommitAll() { - for (std::vector::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); - } -} - -void DescriptorSet::TakeFrom(DescriptorSet* other) { - DCHECK(descriptors_.size() == 0); - - descriptors_.swap(other->descriptors_); - next_descriptor_ = other->next_descriptor_; - other->next_descriptor_ = 0; -} diff --git a/chrome/common/file_descriptor_posix.h b/chrome/common/file_descriptor_posix.h deleted file mode 100644 index 43590b0..0000000 --- a/chrome/common/file_descriptor_posix.h +++ /dev/null @@ -1,124 +0,0 @@ -// 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 - -// ----------------------------------------------------------------------------- -// 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); - - // --------------------------------------------------------------------------- - - // --------------------------------------------------------------------------- - // Interfaces for IPC::Message... - - // Take all the FileDescriptors from another set. Just like a copy - // constructor, except that the source is emptied. - void TakeFrom(DescriptorSet* other); - - // --------------------------------------------------------------------------- - - 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 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 b688c77..d9f44dd 100644 --- a/chrome/common/ipc_channel_posix.cc +++ b/chrome/common/ipc_channel_posix.cc @@ -28,7 +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/descriptor_set_posix.h" #include "chrome/common/ipc_message_utils.h" namespace IPC { diff --git a/chrome/common/ipc_channel_posix.h b/chrome/common/ipc_channel_posix.h index 555d463..d6ed17d 100644 --- a/chrome/common/ipc_channel_posix.h +++ b/chrome/common/ipc_channel_posix.h @@ -14,7 +14,7 @@ #include #include "base/message_loop.h" -#include "chrome/common/file_descriptor_posix.h" +#include "chrome/common/descriptor_set_posix.h" namespace IPC { diff --git a/chrome/common/ipc_message.h b/chrome/common/ipc_message.h index 0af3b44..8aedbe3 100644 --- a/chrome/common/ipc_message.h +++ b/chrome/common/ipc_message.h @@ -17,7 +17,7 @@ #define IPC_MESSAGE_LOG_ENABLED #endif #elif defined(OS_POSIX) -#include "chrome/common/file_descriptor_posix.h" +#include "chrome/common/descriptor_set_posix.h" #endif namespace IPC { diff --git a/chrome/common/ipc_message_utils.h b/chrome/common/ipc_message_utils.h index bd38df0..e7dd7e3 100644 --- a/chrome/common/ipc_message_utils.h +++ b/chrome/common/ipc_message_utils.h @@ -13,7 +13,7 @@ #include "base/string_util.h" #include "base/tuple.h" #if defined(OS_POSIX) -#include "chrome/common/file_descriptor_posix.h" +#include "chrome/common/descriptor_set_posix.h" #endif #include "chrome/common/ipc_sync_message.h" #include "chrome/common/thumbnail_score.h" @@ -668,13 +668,15 @@ struct ParamTraits { #if defined(OS_POSIX) template<> -struct ParamTraits { - typedef FileDescriptor param_type; +struct ParamTraits { + typedef base::FileDescriptor param_type; static void Write(Message* m, const param_type& p) { if (p.auto_close) { - m->descriptor_set()->AddAndAutoClose(p.fd); + if (!m->descriptor_set()->AddAndAutoClose(p.fd)) + NOTREACHED(); } else { - m->descriptor_set()->Add(p.fd); + if (!m->descriptor_set()->Add(p.fd)) + NOTREACHED(); } } static bool Read(const Message* m, void** iter, param_type* r) { diff --git a/chrome/common/ipc_send_fds_test.cc b/chrome/common/ipc_send_fds_test.cc index ca22bd5..e7cb207 100644 --- a/chrome/common/ipc_send_fds_test.cc +++ b/chrome/common/ipc_send_fds_test.cc @@ -47,10 +47,11 @@ class MyChannelDescriptorListener : public IPC::Channel::Listener { virtual void OnMessageReceived(const IPC::Message& message) { void* iter = NULL; - FileDescriptor descriptor; + base::FileDescriptor descriptor; ASSERT_TRUE( - IPC::ParamTraits::Read(&message, &iter, &descriptor)); + IPC::ParamTraits::Read( + &message, &iter, &descriptor)); VerifyAndCloseDescriptor(descriptor.fd, expected_inode_num_); MessageLoop::current()->Quit(); @@ -67,7 +68,7 @@ void TestDescriptorServer(IPC::Channel &chan, base::ProcessHandle process_handle) { ASSERT_TRUE(process_handle); - FileDescriptor descriptor; + base::FileDescriptor descriptor; const int fd = open(kDevRandomPath, O_RDONLY); ASSERT_GE(fd, 0); descriptor.auto_close = true; @@ -76,7 +77,7 @@ void TestDescriptorServer(IPC::Channel &chan, IPC::Message* message = new IPC::Message(0, // routing_id 3, // message type IPC::Message::PRIORITY_NORMAL); - IPC::ParamTraits::Write(message, descriptor); + IPC::ParamTraits::Write(message, descriptor); chan.Send(message); // Run message loop. diff --git a/chrome/common/ipc_tests.cc b/chrome/common/ipc_tests.cc index 2544e43..4b24406 100644 --- a/chrome/common/ipc_tests.cc +++ b/chrome/common/ipc_tests.cc @@ -27,7 +27,7 @@ #include "base/thread.h" #include "chrome/common/chrome_switches.h" #if defined(OS_POSIX) -#include "chrome/common/file_descriptor_posix.h" +#include "chrome/common/descriptor_set_posix.h" #endif #include "chrome/common/ipc_channel.h" #include "chrome/common/ipc_channel_proxy.h" diff --git a/chrome/common/resource_dispatcher.cc b/chrome/common/resource_dispatcher.cc index 7f64d32..e9db613 100644 --- a/chrome/common/resource_dispatcher.cc +++ b/chrome/common/resource_dispatcher.cc @@ -342,7 +342,8 @@ void ResourceDispatcher::OnReceivedData(int request_id, sender->Send( new ViewHostMsg_DataReceived_ACK(MSG_ROUTING_NONE, request_id)); - DCHECK((shm_handle && data_len > 0) || (!shm_handle && !data_len)); + const bool shm_valid = base::SharedMemory::IsHandleValid(shm_handle); + DCHECK((shm_valid && data_len > 0) || (!shm_valid && !data_len)); base::SharedMemory shared_mem(shm_handle, true); // read only PendingRequestList::iterator it = pending_requests_.find(request_id); -- cgit v1.1