diff options
-rw-r--r-- | base/pickle.h | 13 | ||||
-rw-r--r-- | ipc/ipc.gyp | 1 | ||||
-rw-r--r-- | ipc/ipc_message.cc | 13 | ||||
-rw-r--r-- | ipc/ipc_message.h | 10 | ||||
-rw-r--r-- | ipc/ipc_message_utils.cc | 41 | ||||
-rw-r--r-- | ipc/ipc_message_utils_unittest.cc | 53 |
6 files changed, 120 insertions, 11 deletions
diff --git a/base/pickle.h b/base/pickle.h index 2420f75..0eea490 100644 --- a/base/pickle.h +++ b/base/pickle.h @@ -168,9 +168,17 @@ class BASE_EXPORT Pickle { bool ReadString16(PickleIterator* iter, string16* result) const { return iter->ReadString16(result); } + // A pointer to the data will be placed in *data, and the length will be + // placed in *length. This buffer will be into the message's buffer so will + // be scoped to the lifetime of the message (or until the message data is + // mutated). bool ReadData(PickleIterator* iter, const char** data, int* length) const { return iter->ReadData(data, length); } + // A pointer to the data will be placed in *data. The caller specifies the + // number of bytes to read, and ReadBytes will validate this length. The + // returned buffer will be into the message's buffer so will be scoped to the + // lifetime of the message (or until the message data is mutated). bool ReadBytes(PickleIterator* iter, const char** data, int length) const { return iter->ReadBytes(data, length); } @@ -214,7 +222,12 @@ class BASE_EXPORT Pickle { bool WriteString(const std::string& value); bool WriteWString(const std::wstring& value); bool WriteString16(const string16& value); + // "Data" is a blob with a length. When you read it out you will be given the + // length. See also WriteBytes. bool WriteData(const char* data, int length); + // "Bytes" is a blob with no length. The caller must specify the lenght both + // when reading and writing. It is normally used to serialize PoD types of a + // known size. See also WriteData. bool WriteBytes(const void* data, int data_len); // Same as WriteData, but allows the caller to write directly into the diff --git a/ipc/ipc.gyp b/ipc/ipc.gyp index 5f1762e..3c62aaa 100644 --- a/ipc/ipc.gyp +++ b/ipc/ipc.gyp @@ -48,6 +48,7 @@ 'ipc_channel_posix_unittest.cc', 'ipc_fuzzing_tests.cc', 'ipc_message_unittest.cc', + 'ipc_message_utils_unittest.cc', 'ipc_send_fds_test.cc', 'ipc_sync_channel_unittest.cc', 'ipc_sync_message_unittest.cc', diff --git a/ipc/ipc_message.cc b/ipc/ipc_message.cc index feec91a..9908bc7 100644 --- a/ipc/ipc_message.cc +++ b/ipc/ipc_message.cc @@ -67,6 +67,15 @@ Message& Message::operator=(const Message& other) { return *this; } +void Message::SetHeaderValues(int32 routing, uint32 type, uint32 flags) { + // This should only be called when the message is already empty. + DCHECK(payload_size() == 0); + + header()->routing = routing; + header()->type = type; + header()->flags = flags; +} + #ifdef IPC_MESSAGE_LOG_ENABLED void Message::set_sent_time(int64 time) { DCHECK((header()->flags & HAS_SENT_TIME_BIT) == 0); @@ -116,6 +125,10 @@ bool Message::ReadFileDescriptor(PickleIterator* iter, return descriptor->fd >= 0; } +bool Message::HasFileDescriptors() const { + return file_descriptor_set_.get() && !file_descriptor_set_->empty(); +} + void Message::EnsureFileDescriptorSet() { if (file_descriptor_set_.get() == NULL) file_descriptor_set_ = new FileDescriptorSet; diff --git a/ipc/ipc_message.h b/ipc/ipc_message.h index c3d4296..098a010 100644 --- a/ipc/ipc_message.h +++ b/ipc/ipc_message.h @@ -149,6 +149,10 @@ class IPC_EXPORT Message : public Pickle { return header()->flags; } + // Sets all the given header values. The message should be empty at this + // call. + void SetHeaderValues(int32 routing, uint32 type, uint32 flags); + template<class T, class S> static bool Dispatch(const Message* msg, T* obj, S* sender, void (T::*func)()) { @@ -191,12 +195,16 @@ class IPC_EXPORT Message : public Pickle { // On POSIX, a message supports reading / writing FileDescriptor objects. // This is used to pass a file descriptor to the peer of an IPC channel. - // Add a descriptor to the end of the set. Returns false iff the set is full. + // Add a descriptor to the end of the set. Returns false if the set is full. bool WriteFileDescriptor(const base::FileDescriptor& descriptor); + // Get a file descriptor from the message. Returns false on error. // iter: a Pickle iterator to the current location in the message. bool ReadFileDescriptor(PickleIterator* iter, base::FileDescriptor* descriptor) const; + + // Returns true if there are any file descriptors in this message. + bool HasFileDescriptors() const; #endif #ifdef IPC_MESSAGE_LOG_ENABLED diff --git a/ipc/ipc_message_utils.cc b/ipc/ipc_message_utils.cc index a8392cf..66a6877 100644 --- a/ipc/ipc_message_utils.cc +++ b/ipc/ipc_message_utils.cc @@ -730,22 +730,43 @@ void ParamTraits<LogData>::Log(const param_type& p, std::string* l) { } void ParamTraits<Message>::Write(Message* m, const Message& p) { - DCHECK(p.size() <= INT_MAX); - int message_size = static_cast<int>(p.size()); - m->WriteInt(message_size); - m->WriteData(reinterpret_cast<const char*>(p.data()), message_size); +#if defined(OS_POSIX) + // We don't serialize the file descriptors in the nested message, so there + // better not be any. + DCHECK(!p.HasFileDescriptors()); +#endif + + // Don't just write out the message. This is used to send messages between + // NaCl (Posix environment) and the browser (could be on Windows). The message + // header formats differ between these systems (so does handle sharing, but + // we already asserted we don't have any handles). So just write out the + // parts of the header we use. + // + // Be careful also to use only explicitly-sized types. The NaCl environment + // could be 64-bit and the host browser could be 32-bits. The nested message + // may or may not be safe to send between 32-bit and 64-bit systems, but we + // leave that up to the code sending the message to ensure. + m->WriteUInt32(static_cast<uint32>(p.routing_id())); + m->WriteUInt32(p.type()); + m->WriteUInt32(p.flags()); + m->WriteData(p.payload(), static_cast<uint32>(p.payload_size())); } bool ParamTraits<Message>::Read(const Message* m, PickleIterator* iter, Message* r) { - int size; - if (!m->ReadInt(iter, &size)) + uint32 routing_id, type, flags; + if (!m->ReadUInt32(iter, &routing_id) || + !m->ReadUInt32(iter, &type) || + !m->ReadUInt32(iter, &flags)) return false; - const char* data; - if (!m->ReadData(iter, &data, &size)) + + int payload_size; + const char* payload; + if (!m->ReadData(iter, &payload, &payload_size)) return false; - *r = Message(data, size); - return true; + + r->SetHeaderValues(static_cast<int32>(routing_id), type, flags); + return r->WriteBytes(payload, payload_size); } void ParamTraits<Message>::Log(const Message& p, std::string* l) { diff --git a/ipc/ipc_message_utils_unittest.cc b/ipc/ipc_message_utils_unittest.cc new file mode 100644 index 0000000..19726a0 --- /dev/null +++ b/ipc/ipc_message_utils_unittest.cc @@ -0,0 +1,53 @@ +// Copyright (c) 2012 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 "ipc/ipc_message.h" +#include "ipc/ipc_message_utils.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace IPC { + +// Tests nesting of messages as parameters to other messages. +TEST(IPCMessageUtilsTest, NestedMessages) { + int32 nested_routing = 12; + uint32 nested_type = 78; + int nested_content = 456789; + Message::PriorityValue nested_priority = Message::PRIORITY_HIGH; + Message nested_msg(nested_routing, nested_type, nested_priority); + nested_msg.set_sync(); + ParamTraits<int>::Write(&nested_msg, nested_content); + + // Outer message contains the nested one as its parameter. + int32 outer_routing = 91; + uint32 outer_type = 88; + Message::PriorityValue outer_priority = Message::PRIORITY_NORMAL; + Message outer_msg(outer_routing, outer_type, outer_priority); + ParamTraits<Message>::Write(&outer_msg, nested_msg); + + // Read back the nested message. + PickleIterator iter(outer_msg); + IPC::Message result_msg; + ASSERT_TRUE(ParamTraits<Message>::Read(&outer_msg, &iter, &result_msg)); + + // Verify nested message headers. + EXPECT_EQ(nested_msg.routing_id(), result_msg.routing_id()); + EXPECT_EQ(nested_msg.type(), result_msg.type()); + EXPECT_EQ(nested_msg.priority(), result_msg.priority()); + EXPECT_EQ(nested_msg.flags(), result_msg.flags()); + + // Verify nested message content + PickleIterator nested_iter(nested_msg); + int result_content = 0; + ASSERT_TRUE(ParamTraits<int>::Read(&nested_msg, &nested_iter, + &result_content)); + EXPECT_EQ(nested_content, result_content); + + // Try reading past the ends for both messages and make sure it fails. + IPC::Message dummy; + ASSERT_FALSE(ParamTraits<Message>::Read(&outer_msg, &iter, &dummy)); + ASSERT_FALSE(ParamTraits<int>::Read(&nested_msg, &nested_iter, + &result_content)); +} + +} // namespace IPC |