diff options
author | viettrungluu@chromium.org <viettrungluu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-05-03 03:40:21 +0000 |
---|---|---|
committer | viettrungluu@chromium.org <viettrungluu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-05-03 03:40:21 +0000 |
commit | 68d3f6bbacbde53e968ae36ae3e4231ea1a8be4b (patch) | |
tree | c7dae7d4373144356c410354ff3d2dfa93bff915 /mojo/system | |
parent | 7e6d4055cddfa4a06800cffcbf978c342a608639 (diff) | |
download | chromium_src-68d3f6bbacbde53e968ae36ae3e4231ea1a8be4b.zip chromium_src-68d3f6bbacbde53e968ae36ae3e4231ea1a8be4b.tar.gz chromium_src-68d3f6bbacbde53e968ae36ae3e4231ea1a8be4b.tar.bz2 |
Mojo: Factor MessageInTransit secondary buffer stuff out into a separate class.
Also write a big comment explaining what I'm going to do.
R=darin@chromium.org
Review URL: https://codereview.chromium.org/265753006
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@268032 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'mojo/system')
-rw-r--r-- | mojo/system/channel.cc | 16 | ||||
-rw-r--r-- | mojo/system/dispatcher.cc | 6 | ||||
-rw-r--r-- | mojo/system/dispatcher.h | 21 | ||||
-rw-r--r-- | mojo/system/message_in_transit.cc | 268 | ||||
-rw-r--r-- | mojo/system/message_in_transit.h | 92 | ||||
-rw-r--r-- | mojo/system/message_pipe.cc | 9 | ||||
-rw-r--r-- | mojo/system/raw_channel.cc | 29 | ||||
-rw-r--r-- | mojo/system/raw_channel_unittest.cc | 2 | ||||
-rw-r--r-- | mojo/system/transport_data.cc | 255 | ||||
-rw-r--r-- | mojo/system/transport_data.h | 163 |
10 files changed, 500 insertions, 361 deletions
diff --git a/mojo/system/channel.cc b/mojo/system/channel.cc index ca65284..7446cb9 100644 --- a/mojo/system/channel.cc +++ b/mojo/system/channel.cc @@ -12,6 +12,7 @@ #include "base/logging.h" #include "base/strings/stringprintf.h" #include "mojo/system/message_pipe_endpoint.h" +#include "mojo/system/transport_data.h" namespace mojo { namespace system { @@ -347,10 +348,17 @@ void Channel::OnReadMessageForDownstream( return; } - // We need to duplicate the message, because |EnqueueMessage()| will take - // ownership of it. + // We need to duplicate the message (data), because |EnqueueMessage()| will + // take ownership of it. scoped_ptr<MessageInTransit> message(new MessageInTransit(message_view)); - message->DeserializeDispatchers(this); + if (message_view.transport_data_buffer_size() > 0) { + DCHECK(message_view.transport_data_buffer()); + message->SetDispatchers( + TransportData::DeserializeDispatchersFromBuffer( + message_view.transport_data_buffer(), + message_view.transport_data_buffer_size(), + this)); + } MojoResult result = endpoint_info.message_pipe->EnqueueMessage( MessagePipe::GetPeerPort(endpoint_info.port), message.Pass()); if (result != MOJO_RESULT_OK) { @@ -458,7 +466,7 @@ bool Channel::SendControlMessage(MessageInTransit::Subtype subtype, DVLOG(2) << "Sending channel control message: subtype " << subtype << ", local ID " << local_id << ", remote ID " << remote_id; scoped_ptr<MessageInTransit> message(new MessageInTransit( - MessageInTransit::kTypeChannel, subtype, 0, 0, NULL)); + MessageInTransit::kTypeChannel, subtype, 0, NULL)); message->set_source_id(local_id); message->set_destination_id(remote_id); return WriteMessage(message.Pass()); diff --git a/mojo/system/dispatcher.cc b/mojo/system/dispatcher.cc index 8b0d71f..1107cf3 100644 --- a/mojo/system/dispatcher.cc +++ b/mojo/system/dispatcher.cc @@ -40,7 +40,7 @@ DispatcherTransport Dispatcher::HandleTableAccess::TryStartTransport( } // static -void Dispatcher::MessageInTransitAccess::StartSerialize( +void Dispatcher::TransportDataAccess::StartSerialize( Dispatcher* dispatcher, Channel* channel, size_t* max_size, @@ -50,7 +50,7 @@ void Dispatcher::MessageInTransitAccess::StartSerialize( } // static -bool Dispatcher::MessageInTransitAccess::EndSerializeAndClose( +bool Dispatcher::TransportDataAccess::EndSerializeAndClose( Dispatcher* dispatcher, Channel* channel, void* destination, @@ -62,7 +62,7 @@ bool Dispatcher::MessageInTransitAccess::EndSerializeAndClose( } // static -scoped_refptr<Dispatcher> Dispatcher::MessageInTransitAccess::Deserialize( +scoped_refptr<Dispatcher> Dispatcher::TransportDataAccess::Deserialize( Channel* channel, int32_t type, const void* source, diff --git a/mojo/system/dispatcher.h b/mojo/system/dispatcher.h index 504e33a..25c02e8 100644 --- a/mojo/system/dispatcher.h +++ b/mojo/system/dispatcher.h @@ -27,9 +27,9 @@ class Dispatcher; class DispatcherTransport; class HandleTable; class LocalMessagePipeEndpoint; -class MessageInTransit; class ProxyMessagePipeEndpoint; class RawSharedBufferMapping; +class TransportData; class Waiter; namespace test { @@ -146,18 +146,19 @@ class MOJO_SYSTEM_IMPL_EXPORT Dispatcher : static DispatcherTransport TryStartTransport(Dispatcher* dispatcher); }; - // A |MessageInTransit| may serialize dispatchers that are attached to it to a - // given |Channel| and then (probably in a different process) deserialize. + // A |TransportData| may serialize dispatchers that are given to it (and which + // were previously attached to the |MessageInTransit| that is creating it) to + // a given |Channel| and then (probably in a different process) deserialize. // Note that the |MessageInTransit| "owns" (i.e., has the only ref to) these // dispatchers, so there are no locking issues. (There's no lock ordering // issue, and in fact no need to take dispatcher locks at all.) // TODO(vtl): Consider making another wrapper similar to |DispatcherTransport| // (but with an owning, unique reference), and having // |CreateEquivalentDispatcherAndCloseImplNoLock()| return that wrapper (and - // |MessageInTransit| only holding on to such wrappers). - class MessageInTransitAccess { + // |MessageInTransit|, etc. only holding on to such wrappers). + class TransportDataAccess { private: - friend class MessageInTransit; + friend class TransportData; // Serialization API. These functions may only be called on such // dispatchers. (|channel| is the |Channel| to which the dispatcher is to be @@ -286,16 +287,16 @@ class MOJO_SYSTEM_IMPL_EXPORT Dispatcher : scoped_refptr<Dispatcher> CreateEquivalentDispatcherAndCloseNoLock(); // API to serialize dispatchers to a |Channel|, exposed to only - // |MessageInTransit| (via |MessageInTransitAccess|). They may only be called - // on a dispatcher attached to a |MessageInTransit| (and in particular not in + // |TransportData| (via |TransportData|). They may only be called on a + // dispatcher attached to a |MessageInTransit| (and in particular not in // |CoreImpl|'s handle table). // // Starts the serialization. Returns (via the two "out" parameters) the // maximum amount of space that may be needed to serialize this dispatcher to // the given |Channel| (no more than - // |MessageInTransit::kMaxSerializedDispatcherSize|) and the maximum number of + // |TransportData::kMaxSerializedDispatcherSize|) and the maximum number of // |PlatformHandle|s that may need to be attached (no more than - // |MessageInTransit::kMaxSerializedDispatcherPlatformHandles|). If this + // |TransportData::kMaxSerializedDispatcherPlatformHandles|). If this // dispatcher cannot be serialized to the given |Channel|, |*max_size| and // |*max_platform_handles| should be set to zero. A call to this method will // ALWAYS be followed by a call to |EndSerializeAndClose()| (even if this diff --git a/mojo/system/message_in_transit.cc b/mojo/system/message_in_transit.cc index a38ac1c..79f1184 100644 --- a/mojo/system/message_in_transit.cc +++ b/mojo/system/message_in_transit.cc @@ -6,11 +6,10 @@ #include <string.h> -#include <new> - #include "base/compiler_specific.h" #include "base/logging.h" #include "mojo/system/constants.h" +#include "mojo/system/transport_data.h" namespace mojo { namespace system { @@ -32,10 +31,6 @@ STATIC_CONST_MEMBER_DEFINITION const MessageInTransit::Subtype STATIC_CONST_MEMBER_DEFINITION const MessageInTransit::EndpointId MessageInTransit::kInvalidEndpointId; STATIC_CONST_MEMBER_DEFINITION const size_t MessageInTransit::kMessageAlignment; -STATIC_CONST_MEMBER_DEFINITION const size_t - MessageInTransit::kMaxSerializedDispatcherSize; -STATIC_CONST_MEMBER_DEFINITION const size_t - MessageInTransit::kMaxSerializedDispatcherPlatformHandles; struct MessageInTransit::PrivateStructForCompileAsserts { // The size of |Header| must be a multiple of the alignment. @@ -51,26 +46,8 @@ struct MessageInTransit::PrivateStructForCompileAsserts { // size is a multiple of the alignment. COMPILE_ASSERT(kMaxMessageNumBytes % kMessageAlignment == 0, kMessageAlignment_not_a_multiple_of_alignment); - - // The maximum serialized dispatcher size must be a multiple of the alignment. - COMPILE_ASSERT(kMaxSerializedDispatcherSize % kMessageAlignment == 0, - kMaxSerializedDispatcherSize_not_a_multiple_of_alignment); - - // The size of |HandleTableEntry| must be a multiple of the alignment. - COMPILE_ASSERT(sizeof(HandleTableEntry) % kMessageAlignment == 0, - sizeof_MessageInTransit_HandleTableEntry_invalid); }; -// For each attached (Mojo) handle, there'll be a handle table entry and -// serialized dispatcher data. -// static -const size_t MessageInTransit::kMaxSecondaryBufferSize = kMaxMessageNumHandles * - (sizeof(HandleTableEntry) + kMaxSerializedDispatcherSize); - -// static -const size_t MessageInTransit::kMaxPlatformHandles = - kMaxMessageNumHandles * kMaxSerializedDispatcherPlatformHandles; - MessageInTransit::View::View(size_t message_size, const void* buffer) : buffer_(buffer) { size_t next_message_size = 0; @@ -89,11 +66,13 @@ bool MessageInTransit::View::IsValid(const char** error_message) const { return false; } - if (const char* secondary_buffer_error_message = - ValidateSecondaryBuffer(num_handles(), secondary_buffer(), - secondary_buffer_size())) { - *error_message = secondary_buffer_error_message; - return false; + if (transport_data_buffer_size() > 0) { + const char* e = TransportData::ValidateBuffer(transport_data_buffer(), + transport_data_buffer_size()); + if (e) { + *error_message = e; + return false; + } } return true; @@ -102,14 +81,11 @@ bool MessageInTransit::View::IsValid(const char** error_message) const { MessageInTransit::MessageInTransit(Type type, Subtype subtype, uint32_t num_bytes, - uint32_t num_handles, const void* bytes) : main_buffer_size_(RoundUpMessageAlignment(sizeof(Header) + num_bytes)), main_buffer_(static_cast<char*>(base::AlignedAlloc(main_buffer_size_, - kMessageAlignment))), - secondary_buffer_size_(0) { + kMessageAlignment))) { DCHECK_LE(num_bytes, kMaxMessageNumBytes); - DCHECK_LE(num_handles, kMaxMessageNumHandles); // |total_size| is updated below, from the other values. header()->type = type; @@ -117,9 +93,8 @@ MessageInTransit::MessageInTransit(Type type, header()->source_id = kInvalidEndpointId; header()->destination_id = kInvalidEndpointId; header()->num_bytes = num_bytes; - header()->num_handles = num_handles; - // Note: If dispatchers are subsequently attached (in particular, if - // |num_handles| is nonzero), then |total_size| will have to be adjusted. + // Note: If dispatchers are subsequently attached, then |total_size| will have + // to be adjusted. UpdateTotalSize(); if (bytes) { @@ -131,24 +106,14 @@ MessageInTransit::MessageInTransit(Type type, } } -// TODO(vtl): Do I really want/need to copy the secondary buffer here, or should -// I just create (deserialize) the dispatchers right away? MessageInTransit::MessageInTransit(const View& message_view) : main_buffer_size_(message_view.main_buffer_size()), main_buffer_(static_cast<char*>(base::AlignedAlloc(main_buffer_size_, - kMessageAlignment))), - secondary_buffer_size_(message_view.secondary_buffer_size()), - secondary_buffer_(secondary_buffer_size_ ? - static_cast<char*>( - base::AlignedAlloc(secondary_buffer_size_, - kMessageAlignment)) : NULL) { + kMessageAlignment))) { DCHECK_GE(main_buffer_size_, sizeof(Header)); DCHECK_EQ(main_buffer_size_ % kMessageAlignment, 0u); memcpy(main_buffer_.get(), message_view.main_buffer(), main_buffer_size_); - memcpy(secondary_buffer_.get(), message_view.secondary_buffer(), - secondary_buffer_size_); - DCHECK_EQ(main_buffer_size_, RoundUpMessageAlignment(sizeof(Header) + num_bytes())); } @@ -163,17 +128,6 @@ MessageInTransit::~MessageInTransit() { (*dispatchers_)[i]->Close(); } } - - if (platform_handles_) { - for (size_t i = 0; i < platform_handles_->size(); i++) - (*platform_handles_)[i].CloseIfNecessary(); - } - -#ifndef NDEBUG - secondary_buffer_size_ = 0; - dispatchers_.reset(); - platform_handles_.reset(); -#endif } // static @@ -210,206 +164,24 @@ void MessageInTransit::SetDispatchers( void MessageInTransit::SerializeAndCloseDispatchers(Channel* channel) { DCHECK(channel); - DCHECK(!secondary_buffer_); + DCHECK(!transport_data_); - const size_t num_handles = dispatchers_ ? dispatchers_->size() : 0; - if (!num_handles) + if (!dispatchers_ || !dispatchers_->size()) return; - // The offset to the start of the (Mojo) handle table. - // TODO(vtl): Add a header to the secondary buffer. - const size_t handle_table_start_offset = 0; - // The offset to the start of the serialized dispatcher data. - const size_t serialized_dispatcher_start_offset = - handle_table_start_offset + num_handles * sizeof(HandleTableEntry); - // The estimated size of the secondary buffer. We compute this estimate below. - // It must be at least as big as the (eventual) actual size. - size_t estimated_size = serialized_dispatcher_start_offset; - size_t num_platform_handles = 0; -#if DCHECK_IS_ON - std::vector<size_t> all_max_sizes(num_handles); - std::vector<size_t> all_max_platform_handles(num_handles); -#endif - for (size_t i = 0; i < num_handles; i++) { - if (Dispatcher* dispatcher = (*dispatchers_)[i].get()) { - size_t max_size = 0; - size_t max_platform_handles = 0; - Dispatcher::MessageInTransitAccess::StartSerialize( - dispatcher, channel, &max_size, &max_platform_handles); - - DCHECK_LE(max_size, kMaxSerializedDispatcherSize); - estimated_size += RoundUpMessageAlignment(max_size); - DCHECK_LE(estimated_size, kMaxSecondaryBufferSize); - - DCHECK_LE(max_platform_handles, - kMaxSerializedDispatcherPlatformHandles); - num_platform_handles += max_platform_handles; - DCHECK_LE(num_platform_handles, kMaxPlatformHandles); - -#if DCHECK_IS_ON - all_max_sizes[i] = max_size; - all_max_platform_handles[i] = max_platform_handles; -#endif - } - } - - secondary_buffer_.reset(static_cast<char*>( - base::AlignedAlloc(estimated_size, kMessageAlignment))); - // Entirely clear out the secondary buffer, since then we won't have to worry - // about clearing padding or unused space (e.g., if a dispatcher fails to - // serialize). - memset(secondary_buffer_.get(), 0, estimated_size); - - if (num_platform_handles > 0) { - DCHECK(!platform_handles_); - platform_handles_.reset(new std::vector<embedder::PlatformHandle>()); - } - - HandleTableEntry* handle_table = reinterpret_cast<HandleTableEntry*>( - secondary_buffer_.get() + handle_table_start_offset); - size_t current_offset = serialized_dispatcher_start_offset; - for (size_t i = 0; i < num_handles; i++) { - Dispatcher* dispatcher = (*dispatchers_)[i].get(); - if (!dispatcher) { - COMPILE_ASSERT(Dispatcher::kTypeUnknown == 0, - value_of_Dispatcher_kTypeUnknown_must_be_zero); - continue; - } - -#if DCHECK_IS_ON - size_t old_platform_handles_size = - platform_handles_ ? platform_handles_->size() : 0; -#endif - - void* destination = secondary_buffer_.get() + current_offset; - size_t actual_size = 0; - if (Dispatcher::MessageInTransitAccess::EndSerializeAndClose( - dispatcher, channel, destination, &actual_size, - platform_handles_.get())) { - handle_table[i].type = static_cast<int32_t>(dispatcher->GetType()); - handle_table[i].offset = static_cast<uint32_t>(current_offset); - handle_table[i].size = static_cast<uint32_t>(actual_size); - -#if DCHECK_IS_ON - DCHECK_LE(actual_size, all_max_sizes[i]); - DCHECK_LE(platform_handles_ ? (platform_handles_->size() - - old_platform_handles_size) : 0, - all_max_platform_handles[i]); -#endif - } else { - // Nothing to do on failure, since |secondary_buffer_| was cleared, and - // |kTypeUnknown| is zero. The handle was simply closed. - LOG(ERROR) << "Failed to serialize handle to remote message pipe"; - } - - current_offset += RoundUpMessageAlignment(actual_size); - DCHECK_LE(current_offset, estimated_size); - DCHECK_LE(platform_handles_ ? platform_handles_->size() : 0, - num_platform_handles); - } - - // There's no aligned realloc, so it's no good way to release unused space (if - // we overshot our estimated space requirements). - secondary_buffer_size_ = current_offset; - - // The dispatchers (which we "owned") were closed. We can dispose of them now. - dispatchers_.reset(); + transport_data_.reset(new TransportData(dispatchers_.Pass(), channel)); // Update the sizes in the message header. UpdateTotalSize(); } -// Note: The message's secondary buffer should have been checked by calling -// |View::IsValid()| (on the |View|) first. -void MessageInTransit::DeserializeDispatchers(Channel* channel) { - DCHECK(!dispatchers_); - - // Already checked by |View::IsValid()|: - DCHECK_LE(num_handles(), kMaxMessageNumHandles); - - if (!num_handles()) - return; - - dispatchers_.reset( - new std::vector<scoped_refptr<Dispatcher> >(num_handles())); - - size_t handle_table_size = num_handles() * sizeof(HandleTableEntry); - // Already checked by |View::IsValid()|: - DCHECK_LE(handle_table_size, secondary_buffer_size_); - - const HandleTableEntry* handle_table = - reinterpret_cast<const HandleTableEntry*>(secondary_buffer_.get()); - for (size_t i = 0; i < num_handles(); i++) { - size_t offset = handle_table[i].offset; - size_t size = handle_table[i].size; - // Already checked by |View::IsValid()|: - DCHECK_EQ(offset % kMessageAlignment, 0u); - DCHECK_LE(offset, secondary_buffer_size_); - DCHECK_LE(offset + size, secondary_buffer_size_); - - const void* source = secondary_buffer_.get() + offset; - (*dispatchers_)[i] = Dispatcher::MessageInTransitAccess::Deserialize( - channel, handle_table[i].type, source, size); - } -} - -// Validates the secondary buffer. Returns null on success, or a human-readable -// error message on error. -// static -const char* MessageInTransit::ValidateSecondaryBuffer( - size_t num_handles, - const void* secondary_buffer, - size_t secondary_buffer_size) { - // Always make sure that the secondary buffer size is sane (even if we have no - // handles); if it's not, someone's messing with us. - if (secondary_buffer_size > kMaxSecondaryBufferSize) - return "Message secondary buffer too large"; - - // Fast-path for the common case (no handles => no secondary buffer). - if (num_handles == 0) { - // We shouldn't have a secondary buffer in this case. - if (secondary_buffer_size > 0) - return "Message has no handles attached, but secondary buffer present"; - return NULL; - } - - // Sanity-check |num_handles| (before multiplying it against anything). - if (num_handles > kMaxMessageNumHandles) - return "Message handle payload too large"; - - if (secondary_buffer_size < num_handles * sizeof(HandleTableEntry)) - return "Message secondary buffer too small"; - - DCHECK(secondary_buffer); - const HandleTableEntry* handle_table = - static_cast<const HandleTableEntry*>(secondary_buffer); - - static const char kInvalidSerializedDispatcher[] = - "Message contains invalid serialized dispatcher"; - for (size_t i = 0; i < num_handles; i++) { - size_t offset = handle_table[i].offset; - if (offset % kMessageAlignment != 0) - return kInvalidSerializedDispatcher; - - size_t size = handle_table[i].size; - if (size > kMaxSerializedDispatcherSize || size > secondary_buffer_size) - return kInvalidSerializedDispatcher; - - // Note: This is an overflow-safe check for |offset + size > - // secondary_buffer_size()| (we know that |size <= secondary_buffer_size()| - // from the previous check). - if (offset > secondary_buffer_size - size) - return kInvalidSerializedDispatcher; - } - - return NULL; -} - void MessageInTransit::UpdateTotalSize() { DCHECK_EQ(main_buffer_size_ % kMessageAlignment, 0u); - DCHECK_EQ(secondary_buffer_size_ % kMessageAlignment, 0u); - header()->total_size = - static_cast<uint32_t>(main_buffer_size_ + secondary_buffer_size_); + header()->total_size = static_cast<uint32_t>(main_buffer_size_); + if (transport_data_) { + header()->total_size += + static_cast<uint32_t>(transport_data_->buffer_size()); + } } } // namespace system diff --git a/mojo/system/message_in_transit.h b/mojo/system/message_in_transit.h index 1fc021e..103ac3e 100644 --- a/mojo/system/message_in_transit.h +++ b/mojo/system/message_in_transit.h @@ -5,6 +5,7 @@ #ifndef MOJO_SYSTEM_MESSAGE_IN_TRANSIT_H_ #define MOJO_SYSTEM_MESSAGE_IN_TRANSIT_H_ +#include <stddef.h> #include <stdint.h> #include <vector> @@ -12,7 +13,6 @@ #include "base/macros.h" #include "base/memory/aligned_memory.h" #include "base/memory/scoped_ptr.h" -#include "mojo/embedder/platform_handle.h" #include "mojo/system/dispatcher.h" #include "mojo/system/system_impl_export.h" @@ -20,13 +20,14 @@ namespace mojo { namespace system { class Channel; +class TransportData; // This class is used to represent data in transit. It is thread-unsafe. // // |MessageInTransit| buffers: // // A |MessageInTransit| can be serialized by writing the main buffer and then, -// if it has one, the secondary buffer. Both buffers are +// if it has one, the transport data buffer. Both buffers are // |kMessageAlignment|-byte aligned and a multiple of |kMessageAlignment| bytes // in size. // @@ -36,11 +37,8 @@ class Channel; // |kMessageAlignment|-byte aligned), and then any padding needed to make the // main buffer a multiple of |kMessageAlignment| bytes in size. // -// The secondary buffer consists first of a table of |HandleTableEntry|s (each -// of which is already a multiple of |kMessageAlignment| in size), followed by -// data needed for the |HandleTableEntry|s: A |HandleTableEntry| consists of an -// offset (in bytes, relative to the start of the secondary buffer; we guarantee -// that it's a multiple of |kMessageAlignment|), and a size (in bytes). +// See |TransportData| for a description of the (serialized) transport data +// buffer. class MOJO_SYSTEM_IMPL_EXPORT MessageInTransit { public: typedef uint16_t Type; @@ -69,14 +67,6 @@ class MOJO_SYSTEM_IMPL_EXPORT MessageInTransit { // quantity (which must be a power of 2). static const size_t kMessageAlignment = 8; - // The maximum size of a single serialized dispatcher. This must be a multiple - // of |kMessageAlignment|. - static const size_t kMaxSerializedDispatcherSize = 10000; - - // The maximum number of platform handles to attach for a single serialized - // dispatcher. - static const size_t kMaxSerializedDispatcherPlatformHandles = 2; - // Forward-declare |Header| so that |View| can use it: private: struct Header; @@ -90,12 +80,10 @@ class MOJO_SYSTEM_IMPL_EXPORT MessageInTransit { // |buffer| should be |kMessageAlignment|-byte aligned. View(size_t message_size, const void* buffer); - // Checks the following things versus pre-determined limits: - // - |num_bytes()| and |main_buffer_size()|, - // - |num_handles()| and |secondary_buffer_size()|, - // - the entries in the handle entry table (that the sizes and offsets are - // valid). - // Note: It does not check the serialized dispatcher data itself. + // Checks that the given |View| appears to be for a valid message, within + // predetermined limits (e.g., |num_bytes()| and |main_buffer_size()|, that + // |transport_data_buffer()|/|transport_data_buffer_size()| is for valid + // transport data -- see |TransportData::ValidateBuffer()|). // // It returns true (and leaves |error_message| alone) if this object appears // to be a valid message (according to the above) and false, pointing @@ -108,11 +96,11 @@ class MOJO_SYSTEM_IMPL_EXPORT MessageInTransit { size_t main_buffer_size() const { return RoundUpMessageAlignment(sizeof(Header) + header()->num_bytes); } - const void* secondary_buffer() const { + const void* transport_data_buffer() const { return (total_size() > main_buffer_size()) ? static_cast<const char*>(buffer_) + main_buffer_size() : NULL; } - size_t secondary_buffer_size() const { + size_t transport_data_buffer_size() const { return total_size() - main_buffer_size(); } size_t total_size() const { return header()->total_size; } @@ -120,7 +108,6 @@ class MOJO_SYSTEM_IMPL_EXPORT MessageInTransit { const void* bytes() const { return static_cast<const char*>(buffer_) + sizeof(Header); } - uint32_t num_handles() const { return header()->num_handles; } Type type() const { return header()->type; } Subtype subtype() const { return header()->subtype; } EndpointId source_id() const { return header()->source_id; } @@ -141,7 +128,6 @@ class MOJO_SYSTEM_IMPL_EXPORT MessageInTransit { MessageInTransit(Type type, Subtype subtype, uint32_t num_bytes, - uint32_t num_handles, const void* bytes); // Constructs a |MessageInTransit| from a |View|. explicit MessageInTransit(const View& message_view); @@ -172,19 +158,12 @@ class MOJO_SYSTEM_IMPL_EXPORT MessageInTransit { // stays alive through the call. void SerializeAndCloseDispatchers(Channel* channel); - // Deserializes any dispatchers from the secondary buffer. This message must - // not have any dispatchers attached. - // TODO(vtl): Having to copy the secondary buffer (in the constructor from a - // |View|) is suboptimal. Maybe this should just be done in the constructor? - void DeserializeDispatchers(Channel* channel); - // Gets the main buffer and its size (in number of bytes), respectively. const void* main_buffer() const { return main_buffer_.get(); } size_t main_buffer_size() const { return main_buffer_size_; } - // Gets the secondary buffer and its size (in number of bytes), respectively. - const void* secondary_buffer() const { return secondary_buffer_.get(); } - size_t secondary_buffer_size() const { return secondary_buffer_size_; } + // Gets the transport data buffer (if any). + const TransportData* transport_data() const { return transport_data_.get(); } // Gets the total size of the message (see comment in |Header|, below). size_t total_size() const { return header()->total_size; } @@ -196,8 +175,6 @@ class MOJO_SYSTEM_IMPL_EXPORT MessageInTransit { const void* bytes() const { return main_buffer_.get() + sizeof(Header); } void* bytes() { return main_buffer_.get() + sizeof(Header); } - uint32_t num_handles() const { return header()->num_handles; } - Type type() const { return header()->type; } Subtype subtype() const { return header()->subtype; } EndpointId source_id() const { return header()->source_id; } @@ -220,25 +197,13 @@ class MOJO_SYSTEM_IMPL_EXPORT MessageInTransit { return dispatchers_ && !dispatchers_->empty(); } - // Gets the platform-specific handles attached to this message; this may - // return null if there are none. Note that the caller may mutate the set of - // platform-specific handles. - std::vector<embedder::PlatformHandle>* platform_handles() { - return platform_handles_.get(); - } - - // Returns true if this message has platform-specific handles attached. - bool has_platform_handles() const { - return platform_handles_ && !platform_handles_->empty(); - } - // Rounds |n| up to a multiple of |kMessageAlignment|. static inline size_t RoundUpMessageAlignment(size_t n) { return (n + kMessageAlignment - 1) & ~(kMessageAlignment - 1); } private: - // To allow us to make assertions about |Header| in the .cc file. + // To allow us to make compile-assertions about |Header| in the .cc file. struct PrivateStructForCompileAsserts; // Header for the data (main buffer). Must be a multiple of @@ -256,29 +221,9 @@ class MOJO_SYSTEM_IMPL_EXPORT MessageInTransit { EndpointId destination_id; // 4 bytes. // Size of actual message data. uint32_t num_bytes; - // Number of handles "attached". - uint32_t num_handles; - }; - - struct HandleTableEntry { - int32_t type; // From |Dispatcher::Type| (|kTypeUnknown| for "invalid"). - uint32_t offset; // Relative to the start of the secondary buffer. - uint32_t size; // (Not including any padding.) uint32_t unused; }; - // The maximum possible size of a valid secondary buffer. - static const size_t kMaxSecondaryBufferSize; - - // The maximum total number of platform handles that may be attached. - static const size_t kMaxPlatformHandles; - - // Validates the secondary buffer. Returns null on success, or a - // human-readable error message (meant for logging/debugging) on error. - static const char* ValidateSecondaryBuffer(size_t num_handles, - const void* secondary_buffer, - size_t secondary_buffer_size); - const Header* header() const { return reinterpret_cast<const Header*>(main_buffer_.get()); } @@ -289,8 +234,7 @@ class MOJO_SYSTEM_IMPL_EXPORT MessageInTransit { const size_t main_buffer_size_; const scoped_ptr<char, base::AlignedFreeDeleter> main_buffer_; // Never null. - size_t secondary_buffer_size_; - scoped_ptr<char, base::AlignedFreeDeleter> secondary_buffer_; // May be null. + scoped_ptr<TransportData> transport_data_; // May be null. // Any dispatchers that may be attached to this message. These dispatchers // should be "owned" by this message, i.e., have a ref count of exactly 1. (We @@ -298,12 +242,6 @@ class MOJO_SYSTEM_IMPL_EXPORT MessageInTransit { // some reason.) scoped_ptr<std::vector<scoped_refptr<Dispatcher> > > dispatchers_; - // Any platform-specific handles attached to this message (for inter-process - // transport). The vector (if any) owns the handles that it contains (and is - // responsible for closing them). - // TODO(vtl): With C++11, change it to a vector of |ScopedPlatformHandles|. - scoped_ptr<std::vector<embedder::PlatformHandle> > platform_handles_; - DISALLOW_COPY_AND_ASSIGN(MessageInTransit); }; diff --git a/mojo/system/message_pipe.cc b/mojo/system/message_pipe.cc index e154064..e25ac1c 100644 --- a/mojo/system/message_pipe.cc +++ b/mojo/system/message_pipe.cc @@ -72,14 +72,13 @@ MojoResult MessagePipe::WriteMessage( std::vector<DispatcherTransport>* transports, MojoWriteMessageFlags flags) { DCHECK(port == 0 || port == 1); - uint32_t num_handles = - transports ? static_cast<uint32_t>(transports->size()) : 0; return EnqueueMessageInternal( GetPeerPort(port), make_scoped_ptr(new MessageInTransit( MessageInTransit::kTypeMessagePipeEndpoint, MessageInTransit::kSubtypeMessagePipeEndpointData, - num_bytes, num_handles, bytes)), + num_bytes, + bytes)), transports); } @@ -226,9 +225,6 @@ MojoResult MessagePipe::EnqueueMessageInternal( return result; } - if (message->has_dispatchers()) - DCHECK_EQ(message->dispatchers()->size(), message->num_handles()); - // The endpoint's |EnqueueMessage()| may not report failure. endpoints_[port]->EnqueueMessage(message.Pass()); return MOJO_RESULT_OK; @@ -239,7 +235,6 @@ MojoResult MessagePipe::AttachTransportsNoLock( MessageInTransit* message, std::vector<DispatcherTransport>* transports) { DCHECK(!message->has_dispatchers()); - DCHECK_EQ(transports->size(), message->num_handles()); // You're not allowed to send either handle to a message pipe over the message // pipe, so check for this. (The case of trying to write a handle to itself is diff --git a/mojo/system/raw_channel.cc b/mojo/system/raw_channel.cc index 92b9a3d..9780baf 100644 --- a/mojo/system/raw_channel.cc +++ b/mojo/system/raw_channel.cc @@ -14,6 +14,7 @@ #include "base/message_loop/message_loop.h" #include "base/stl_util.h" #include "mojo/system/message_in_transit.h" +#include "mojo/system/transport_data.h" namespace mojo { namespace system { @@ -45,7 +46,10 @@ void RawChannel::WriteBuffer::GetBuffers(std::vector<Buffer>* buffers) const { return; MessageInTransit* message = message_queue_.front(); - if (!message->secondary_buffer_size()) { + size_t transport_data_buffer_size = message->transport_data() ? + message->transport_data()->buffer_size() : 0; + + if (!transport_data_buffer_size) { // Only write from the main buffer. DCHECK_LT(offset_, message->main_buffer_size()); DCHECK_LE(bytes_to_write, message->main_buffer_size()); @@ -57,12 +61,12 @@ void RawChannel::WriteBuffer::GetBuffers(std::vector<Buffer>* buffers) const { } if (offset_ >= message->main_buffer_size()) { - // Only write from the secondary buffer. + // Only write from the transport data buffer. DCHECK_LT(offset_ - message->main_buffer_size(), - message->secondary_buffer_size()); - DCHECK_LE(bytes_to_write, message->secondary_buffer_size()); + transport_data_buffer_size); + DCHECK_LE(bytes_to_write, transport_data_buffer_size); Buffer buffer = { - static_cast<const char*>(message->secondary_buffer()) + + static_cast<const char*>(message->transport_data()->buffer()) + (offset_ - message->main_buffer_size()), bytes_to_write}; buffers->push_back(buffer); @@ -71,14 +75,16 @@ void RawChannel::WriteBuffer::GetBuffers(std::vector<Buffer>* buffers) const { // Write from both buffers. DCHECK_EQ(bytes_to_write, message->main_buffer_size() - offset_ + - message->secondary_buffer_size()); + transport_data_buffer_size); Buffer buffer1 = { - static_cast<const char*>(message->main_buffer()) + offset_, - message->main_buffer_size() - offset_}; + static_cast<const char*>(message->main_buffer()) + offset_, + message->main_buffer_size() - offset_ + }; buffers->push_back(buffer1); Buffer buffer2 = { - static_cast<const char*>(message->secondary_buffer()), - message->secondary_buffer_size()}; + static_cast<const char*>(message->transport_data()->buffer()), + transport_data_buffer_size + }; buffers->push_back(buffer2); } @@ -159,7 +165,8 @@ bool RawChannel::WriteMessage(scoped_ptr<MessageInTransit> message) { DCHECK(message); // TODO(vtl) - if (message->has_platform_handles()) { + if (message->transport_data() && + message->transport_data()->has_platform_handles()) { NOTIMPLEMENTED(); return false; } diff --git a/mojo/system/raw_channel_unittest.cc b/mojo/system/raw_channel_unittest.cc index 22c52e7..a5b8cb4 100644 --- a/mojo/system/raw_channel_unittest.cc +++ b/mojo/system/raw_channel_unittest.cc @@ -40,7 +40,7 @@ scoped_ptr<MessageInTransit> MakeTestMessage(uint32_t num_bytes) { return make_scoped_ptr( new MessageInTransit(MessageInTransit::kTypeMessagePipeEndpoint, MessageInTransit::kSubtypeMessagePipeEndpointData, - num_bytes, 0, bytes.empty() ? NULL : &bytes[0])); + num_bytes, bytes.empty() ? NULL : &bytes[0])); } bool CheckMessageData(const void* bytes, uint32_t num_bytes) { diff --git a/mojo/system/transport_data.cc b/mojo/system/transport_data.cc new file mode 100644 index 0000000..b3c4845 --- /dev/null +++ b/mojo/system/transport_data.cc @@ -0,0 +1,255 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "mojo/system/transport_data.h" + +#include <string.h> + +#include "base/compiler_specific.h" +#include "base/logging.h" +#include "mojo/system/constants.h" +#include "mojo/system/message_in_transit.h" + +namespace mojo { +namespace system { + +STATIC_CONST_MEMBER_DEFINITION const size_t + TransportData::kMaxSerializedDispatcherSize; +STATIC_CONST_MEMBER_DEFINITION const size_t + TransportData::kMaxSerializedDispatcherPlatformHandles; + +// In additional to the header, for each attached (Mojo) handle there'll be a +// handle table entry and serialized dispatcher data. +// static +const size_t TransportData::kMaxBufferSize = + sizeof(Header) + kMaxMessageNumHandles * (sizeof(HandleTableEntry) + + kMaxSerializedDispatcherSize); + +// static +const size_t TransportData::kMaxPlatformHandles = + kMaxMessageNumHandles * kMaxSerializedDispatcherPlatformHandles; + +struct TransportData::PrivateStructForCompileAsserts { + // The size of |Header| must be a multiple of the alignment. + COMPILE_ASSERT(sizeof(Header) % MessageInTransit::kMessageAlignment == 0, + sizeof_MessageInTransit_Header_invalid); + + // The maximum serialized dispatcher size must be a multiple of the alignment. + COMPILE_ASSERT(kMaxSerializedDispatcherSize % + MessageInTransit::kMessageAlignment == 0, + kMaxSerializedDispatcherSize_not_a_multiple_of_alignment); + + // The size of |HandleTableEntry| must be a multiple of the alignment. + COMPILE_ASSERT(sizeof(HandleTableEntry) % + MessageInTransit::kMessageAlignment == 0, + sizeof_MessageInTransit_HandleTableEntry_invalid); +}; + +TransportData::TransportData( + scoped_ptr<std::vector<scoped_refptr<Dispatcher> > > dispatchers, + Channel* channel) + : buffer_size_(0) { + DCHECK(dispatchers); + DCHECK(channel); + + const size_t num_handles = dispatchers->size(); + DCHECK_GT(num_handles, 0u); + + // The offset to the start of the (Mojo) handle table. + const size_t handle_table_start_offset = sizeof(Header); + // The offset to the start of the serialized dispatcher data. + const size_t serialized_dispatcher_start_offset = + handle_table_start_offset + num_handles * sizeof(HandleTableEntry); + // The estimated size of the secondary buffer. We compute this estimate below. + // It must be at least as big as the (eventual) actual size. + size_t estimated_size = serialized_dispatcher_start_offset; + size_t num_platform_handles = 0; +#if DCHECK_IS_ON + std::vector<size_t> all_max_sizes(num_handles); + std::vector<size_t> all_max_platform_handles(num_handles); +#endif + for (size_t i = 0; i < num_handles; i++) { + if (Dispatcher* dispatcher = (*dispatchers)[i].get()) { + size_t max_size = 0; + size_t max_platform_handles = 0; + Dispatcher::TransportDataAccess::StartSerialize( + dispatcher, channel, &max_size, &max_platform_handles); + + DCHECK_LE(max_size, kMaxSerializedDispatcherSize); + estimated_size += MessageInTransit::RoundUpMessageAlignment(max_size); + DCHECK_LE(estimated_size, kMaxBufferSize); + + DCHECK_LE(max_platform_handles, + kMaxSerializedDispatcherPlatformHandles); + num_platform_handles += max_platform_handles; + DCHECK_LE(num_platform_handles, kMaxPlatformHandles); + +#if DCHECK_IS_ON + all_max_sizes[i] = max_size; + all_max_platform_handles[i] = max_platform_handles; +#endif + } + } + + buffer_.reset(static_cast<char*>( + base::AlignedAlloc(estimated_size, MessageInTransit::kMessageAlignment))); + // Entirely clear out the secondary buffer, since then we won't have to worry + // about clearing padding or unused space (e.g., if a dispatcher fails to + // serialize). + memset(buffer_.get(), 0, estimated_size); + + if (num_platform_handles > 0) { + DCHECK(!platform_handles_); + platform_handles_.reset(new std::vector<embedder::PlatformHandle>()); + } + + Header* header = reinterpret_cast<Header*>(buffer_.get()); + header->num_handles = static_cast<uint32_t>(num_handles); + // TODO(vtl): platform_handle_table_offset and num_platform_handles + + HandleTableEntry* handle_table = reinterpret_cast<HandleTableEntry*>( + buffer_.get() + handle_table_start_offset); + size_t current_offset = serialized_dispatcher_start_offset; + for (size_t i = 0; i < num_handles; i++) { + Dispatcher* dispatcher = (*dispatchers)[i].get(); + if (!dispatcher) { + COMPILE_ASSERT(Dispatcher::kTypeUnknown == 0, + value_of_Dispatcher_kTypeUnknown_must_be_zero); + continue; + } + +#if DCHECK_IS_ON + size_t old_platform_handles_size = + platform_handles_ ? platform_handles_->size() : 0; +#endif + + void* destination = buffer_.get() + current_offset; + size_t actual_size = 0; + if (Dispatcher::TransportDataAccess::EndSerializeAndClose( + dispatcher, channel, destination, &actual_size, + platform_handles_.get())) { + handle_table[i].type = static_cast<int32_t>(dispatcher->GetType()); + handle_table[i].offset = static_cast<uint32_t>(current_offset); + handle_table[i].size = static_cast<uint32_t>(actual_size); + +#if DCHECK_IS_ON + DCHECK_LE(actual_size, all_max_sizes[i]); + DCHECK_LE(platform_handles_ ? (platform_handles_->size() - + old_platform_handles_size) : 0, + all_max_platform_handles[i]); +#endif + } else { + // Nothing to do on failure, since |buffer_| was cleared, and + // |kTypeUnknown| is zero. The handle was simply closed. + LOG(ERROR) << "Failed to serialize handle to remote message pipe"; + } + + current_offset += MessageInTransit::RoundUpMessageAlignment(actual_size); + DCHECK_LE(current_offset, estimated_size); + DCHECK_LE(platform_handles_ ? platform_handles_->size() : 0, + num_platform_handles); + } + + // There's no aligned realloc, so it's no good way to release unused space (if + // we overshot our estimated space requirements). + buffer_size_ = current_offset; + + // |dispatchers_| will be destroyed as it goes out of scope. +} + +TransportData::~TransportData() { + if (platform_handles_) { + for (size_t i = 0; i < platform_handles_->size(); i++) + (*platform_handles_)[i].CloseIfNecessary(); + } +} + +// static +const char* TransportData::ValidateBuffer(const void* buffer, + size_t buffer_size) { + DCHECK(buffer); + DCHECK_GT(buffer_size, 0u); + + // Always make sure that the buffer size is sane; if it's not, someone's + // messing with us. + if (buffer_size < sizeof(Header) || buffer_size > kMaxBufferSize || + buffer_size % MessageInTransit::kMessageAlignment != 0) + return "Invalid message secondary buffer size"; + + const Header* header = static_cast<const Header*>(buffer); + const size_t num_handles = header->num_handles; + if (num_handles == 0) + return "Message has no handles attached, but secondary buffer present"; + + // Sanity-check |num_handles| (before multiplying it against anything). + if (num_handles > kMaxMessageNumHandles) + return "Message handle payload too large"; + + if (buffer_size < sizeof(Header) + num_handles * sizeof(HandleTableEntry)) + return "Message secondary buffer too small"; + + // TODO(vtl): Check |platform_handle_table_offset| and |num_platform_handles| + // once they're used. + if (header->platform_handle_table_offset != 0 || + header->num_platform_handles != 0) + return "Bad message secondary buffer header values"; + + const HandleTableEntry* handle_table = + reinterpret_cast<const HandleTableEntry*>( + static_cast<const char*>(buffer) + sizeof(Header)); + static const char kInvalidSerializedDispatcher[] = + "Message contains invalid serialized dispatcher"; + for (size_t i = 0; i < num_handles; i++) { + size_t offset = handle_table[i].offset; + if (offset % MessageInTransit::kMessageAlignment != 0) + return kInvalidSerializedDispatcher; + + size_t size = handle_table[i].size; + if (size > kMaxSerializedDispatcherSize || size > buffer_size) + return kInvalidSerializedDispatcher; + + // Note: This is an overflow-safe check for |offset + size > buffer_size| + // (we know that |size <= buffer_size| from the previous check). + if (offset > buffer_size - size) + return kInvalidSerializedDispatcher; + } + + return NULL; +} + +// static +scoped_ptr<std::vector<scoped_refptr<Dispatcher> > > + TransportData::DeserializeDispatchersFromBuffer(const void* buffer, + size_t buffer_size, + Channel* channel) { + DCHECK(buffer); + DCHECK_GT(buffer_size, 0u); + DCHECK(channel); + + const Header* header = static_cast<const Header*>(buffer); + const size_t num_handles = header->num_handles; + scoped_ptr<std::vector<scoped_refptr<Dispatcher> > > dispatchers( + new std::vector<scoped_refptr<Dispatcher> >(num_handles)); + + const HandleTableEntry* handle_table = + reinterpret_cast<const HandleTableEntry*>( + static_cast<const char*>(buffer) + sizeof(Header)); + for (size_t i = 0; i < num_handles; i++) { + size_t offset = handle_table[i].offset; + size_t size = handle_table[i].size; + // Should already have been checked by |ValidateBuffer()|: + DCHECK_EQ(offset % MessageInTransit::kMessageAlignment, 0u); + DCHECK_LE(offset, buffer_size); + DCHECK_LE(offset + size, buffer_size); + + const void* source = static_cast<const char*>(buffer) + offset; + (*dispatchers)[i] = Dispatcher::TransportDataAccess::Deserialize( + channel, handle_table[i].type, source, size); + } + + return dispatchers.Pass(); +} + +} // namespace system +} // namespace mojo diff --git a/mojo/system/transport_data.h b/mojo/system/transport_data.h new file mode 100644 index 0000000..d4d9528 --- /dev/null +++ b/mojo/system/transport_data.h @@ -0,0 +1,163 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MOJO_SYSTEM_TRANSPORT_DATA_H_ +#define MOJO_SYSTEM_TRANSPORT_DATA_H_ + +#include <stdint.h> + +#include <vector> + +#include "base/macros.h" +#include "base/memory/aligned_memory.h" +#include "base/memory/scoped_ptr.h" +#include "mojo/embedder/platform_handle.h" +#include "mojo/system/dispatcher.h" +#include "mojo/system/system_impl_export.h" + +namespace mojo { +namespace system { + +// This class is used by |MessageInTransit| to represent handles (|Dispatcher|s) +// in various stages of serialization. +// +// The stages are: +// - Before reaching |TransportData|: Turn |DispatcherTransport|s into +// |Dispatcher|s that are "owned" by (and attached to) a |MessageInTransit|. +// This invalidates the handles in the space of the sending application +// (and, e.g., if another thread is waiting on such a handle, it'll be +// notified of this invalidation). +// - Serialize these dispatchers into the |TransportData|: First, for each +// attached dispatcher, there's an entry in the |TransportData|'s "handle +// table", which points to a segment of (dispatcher-type-dependent) data. +// - During the serialization of the dispatchers, |PlatformHandle|s may be +// detached from the dispatchers and attached to the |TransportData|. +// - Before sending the |MessageInTransit|, including its main buffer and the +// |TransportData|'s buffer, the |Channel| sends any |PlatformHandle|s (in a +// platform-, and possibly sandobx-situation-, specific way) first. In doing +// so, it appends a "platform handle table" to the |TransportData| +// containing information about how to deserialize these |PlatformHandle|s. +// - Finally, at this point, to send the |MessageInTransit|, there only +// remains "inert" data: the |MessageInTransit|'s main buffer and data from +// the |TransportData|, consisting of the "handle table" (one entry for each +// attached dispatcher), dispatcher-type-specific data (one segment for each +// entry in the "handle table"), and the "platform handle table" (one entry +// for each attached |PlatformHandle|). +// +// To receive a message (|MessageInTransit|), the "reverse" happens: +// - On POSIX, receive and buffer |PlatformHandle|s (i.e., FDs), which were +// sent before the "inert" data. +// - Receive the "inert" data from the |MessageInTransit|. Examine its +// "platform handle table". On POSIX, match its entries with the buffered +// |PlatformHandle|s, which were previously received. On Windows, do what's +// necessary to obtain |PlatformHandle|s (e.g.: i. if the sender is fully +// trusted and able to duplicate handle into the receiver, then just pick +// out the |HANDLE| value; ii. if the receiver is fully trusted and able to +// duplicate handles from the receiver, do the |DuplicateHandle()|; iii. +// otherwise, talk to a broker to get handles). Reattach all the +// |PlatformHandle|s to the |MessageInTransit|. +// - For each entry in the "handle table", use serialized dispatcher data to +// reconstitute a dispatcher, taking ownership of associated +// |PlatformHandle|s (and detaching them). Attach these dispatchers to the +// |MessageInTransit|. +// - At this point, the |MessageInTransit| consists of its main buffer +// (primarily the data payload) and the attached dispatchers; the +// |TransportData| can be discarded. +// - When |MojoReadMessage()| is to give data to the application, attach the +// dispatchers to the (global, "core") handle table, getting handles; give +// the application the data payload and these handles. +// +// TODO(vtl): Everything above involving |PlatformHandle|s. +class MOJO_SYSTEM_IMPL_EXPORT TransportData { + public: + // The maximum size of a single serialized dispatcher. This must be a multiple + // of |kMessageAlignment|. + static const size_t kMaxSerializedDispatcherSize = 10000; + + // The maximum number of platform handles to attach for a single serialized + // dispatcher. + static const size_t kMaxSerializedDispatcherPlatformHandles = 2; + + TransportData( + scoped_ptr<std::vector<scoped_refptr<Dispatcher> > > dispatchers, + Channel* channel); + ~TransportData(); + + const void* buffer() const { return buffer_.get(); } + size_t buffer_size() const { return buffer_size_; } + + // Gets attached platform-specific handles; this may return null if there are + // none. Note that the caller may mutate the set of platform-specific handles. + std::vector<embedder::PlatformHandle>* platform_handles() { + return platform_handles_.get(); + } + + // Returns true if there are platform-specific handles attached. + bool has_platform_handles() const { + return platform_handles_ && !platform_handles_->empty(); + } + + // Receive-side functions: + + // Checks if the given buffer (from the "wire") looks like a valid + // |TransportData| buffer. (Should only be called if |buffer_size| is + // nonzero.) Returns null if valid, and a pointer to a human-readable error + // message (for debug/logging purposes) on error. Note: This checks the + // validity of the handle table entries (i.e., does range checking), but does + // not check that the validity of the actual serialized dispatcher + // information. + static const char* ValidateBuffer(const void* buffer, size_t buffer_size); + + // Deserializes dispatchers from the given (serialized) transport data buffer + // (typically from a |MessageInTransit::View|). |buffer| should be non-null + // and |buffer_size| should be nonzero. + static scoped_ptr<std::vector<scoped_refptr<Dispatcher> > > + DeserializeDispatchersFromBuffer(const void* buffer, + size_t buffer_size, + Channel* channel); + + private: + // To allow us to make compile-assertions about |Header|, etc. in the .cc + // file. + struct PrivateStructForCompileAsserts; + + // Header for the "secondary buffer"/"transport data". Must be a multiple of + // |MessageInTransit::kMessageAlignment| in size. Must be POD. + struct Header { + uint32_t num_handles; + // TODO(vtl): Not used yet: + uint32_t platform_handle_table_offset; + uint32_t num_platform_handles; + uint32_t unused; + }; + + struct HandleTableEntry { + int32_t type; // From |Dispatcher::Type| (|kTypeUnknown| for "invalid"). + uint32_t offset; // Relative to the start of the "secondary buffer". + uint32_t size; // (Not including any padding.) + uint32_t unused; + }; + + // The maximum possible size of a valid transport data buffer. + static const size_t kMaxBufferSize; + + // The maximum total number of platform handles that may be attached. + static const size_t kMaxPlatformHandles; + + size_t buffer_size_; + scoped_ptr<char, base::AlignedFreeDeleter> buffer_; // Never null. + + // Any platform-specific handles attached to this message (for inter-process + // transport). The vector (if any) owns the handles that it contains (and is + // responsible for closing them). + // TODO(vtl): With C++11, change it to a vector of |ScopedPlatformHandles|. + scoped_ptr<std::vector<embedder::PlatformHandle> > platform_handles_; + + DISALLOW_COPY_AND_ASSIGN(TransportData); +}; + +} // namespace system +} // namespace mojo + +#endif // MOJO_SYSTEM_TRANSPORT_DATA_H_ |