|  | // Copyright 2015 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 "build/build_config.h" | 
|  |  | 
|  | #include <stddef.h> | 
|  | #include <stdint.h> | 
|  |  | 
|  | #include <limits> | 
|  | #include <memory> | 
|  | #include <set> | 
|  |  | 
|  | #include "base/run_loop.h" | 
|  | #include "ipc/attachment_broker.h" | 
|  | #include "ipc/brokerable_attachment.h" | 
|  | #include "ipc/ipc_channel_reader.h" | 
|  | #include "ipc/placeholder_brokerable_attachment.h" | 
|  | #include "testing/gtest/include/gtest/gtest.h" | 
|  |  | 
|  | // Whether IPC::Message::FindNext() can determine message size for | 
|  | // partial messages. The condition is from FindNext() implementation. | 
|  | #if USE_ATTACHMENT_BROKER | 
|  | #define MESSAGE_FINDNEXT_PARTIAL 0 | 
|  | #else | 
|  | #define MESSAGE_FINDNEXT_PARTIAL 1 | 
|  | #endif | 
|  |  | 
|  | namespace IPC { | 
|  | namespace internal { | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | #if USE_ATTACHMENT_BROKER | 
|  |  | 
|  | class MockAttachment : public BrokerableAttachment { | 
|  | public: | 
|  | MockAttachment() {} | 
|  | MockAttachment(BrokerableAttachment::AttachmentId id) | 
|  | : BrokerableAttachment(id) {} | 
|  |  | 
|  | #if defined(OS_POSIX) | 
|  | base::PlatformFile TakePlatformFile() override { | 
|  | return base::PlatformFile(); | 
|  | } | 
|  | #endif  // OS_POSIX | 
|  |  | 
|  | BrokerableType GetBrokerableType() const override { return WIN_HANDLE; } | 
|  |  | 
|  | private: | 
|  | ~MockAttachment() override {} | 
|  | }; | 
|  |  | 
|  | class MockAttachmentBroker : public AttachmentBroker { | 
|  | public: | 
|  | typedef std::set<scoped_refptr<BrokerableAttachment>> AttachmentSet; | 
|  |  | 
|  | bool SendAttachmentToProcess( | 
|  | const scoped_refptr<BrokerableAttachment>& attachment, | 
|  | base::ProcessId destination_process) override { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | bool OnMessageReceived(const Message& message) override { return false; } | 
|  |  | 
|  | void AddAttachment(scoped_refptr<BrokerableAttachment> attachment) { | 
|  | get_attachments()->push_back(attachment); | 
|  | NotifyObservers(attachment->GetIdentifier()); | 
|  | } | 
|  | }; | 
|  |  | 
|  | #endif  // USE_ATTACHMENT_BROKER | 
|  |  | 
|  | class MockChannelReader : public ChannelReader { | 
|  | public: | 
|  | MockChannelReader() | 
|  | : ChannelReader(nullptr), last_dispatched_message_(nullptr) {} | 
|  |  | 
|  | ReadState ReadData(char* buffer, int buffer_len, int* bytes_read) override { | 
|  | if (data_.empty()) | 
|  | return READ_PENDING; | 
|  |  | 
|  | size_t read_len = std::min(static_cast<size_t>(buffer_len), data_.size()); | 
|  | memcpy(buffer, data_.data(), read_len); | 
|  | *bytes_read = static_cast<int>(read_len); | 
|  | data_.erase(0, read_len); | 
|  | return READ_SUCCEEDED; | 
|  | } | 
|  |  | 
|  | bool ShouldDispatchInputMessage(Message* msg) override { return true; } | 
|  |  | 
|  | bool GetNonBrokeredAttachments(Message* msg) override { return true; } | 
|  |  | 
|  | bool DidEmptyInputBuffers() override { return true; } | 
|  |  | 
|  | void HandleInternalMessage(const Message& msg) override {} | 
|  |  | 
|  | void DispatchMessage(Message* m) override { last_dispatched_message_ = m; } | 
|  |  | 
|  | base::ProcessId GetSenderPID() override { return base::kNullProcessId; } | 
|  |  | 
|  | bool IsAttachmentBrokerEndpoint() override { return false; } | 
|  |  | 
|  | AttachmentBroker* GetAttachmentBroker() override { return broker_; } | 
|  |  | 
|  | // This instance takes ownership of |m|. | 
|  | void AddMessageForDispatch(Message* m) { | 
|  | get_queued_messages()->push_back(m); | 
|  | } | 
|  |  | 
|  | Message* get_last_dispatched_message() { return last_dispatched_message_; } | 
|  |  | 
|  | void set_broker(AttachmentBroker* broker) { broker_ = broker; } | 
|  |  | 
|  | void AppendData(const void* data, size_t size) { | 
|  | data_.append(static_cast<const char*>(data), size); | 
|  | } | 
|  |  | 
|  | void AppendMessageData(const Message& message) { | 
|  | AppendData(message.data(), message.size()); | 
|  | } | 
|  |  | 
|  | private: | 
|  | Message* last_dispatched_message_; | 
|  | AttachmentBroker* broker_; | 
|  | std::string data_; | 
|  | }; | 
|  |  | 
|  | class ExposedMessage: public Message { | 
|  | public: | 
|  | using Message::Header; | 
|  | using Message::header; | 
|  | }; | 
|  |  | 
|  | // Payload that makes messages large | 
|  | const size_t LargePayloadSize = Channel::kMaximumReadBufferSize * 3 / 2; | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | #if USE_ATTACHMENT_BROKER | 
|  |  | 
|  | TEST(ChannelReaderTest, AttachmentAlreadyBrokered) { | 
|  | MockAttachmentBroker broker; | 
|  | MockChannelReader reader; | 
|  | reader.set_broker(&broker); | 
|  | scoped_refptr<MockAttachment> attachment(new MockAttachment); | 
|  | broker.AddAttachment(attachment); | 
|  |  | 
|  | Message* m = new Message; | 
|  | PlaceholderBrokerableAttachment* needs_brokering_attachment = | 
|  | new PlaceholderBrokerableAttachment(attachment->GetIdentifier()); | 
|  | EXPECT_TRUE(m->WriteAttachment(needs_brokering_attachment)); | 
|  | reader.AddMessageForDispatch(m); | 
|  | EXPECT_EQ(ChannelReader::DISPATCH_FINISHED, reader.DispatchMessages()); | 
|  | EXPECT_EQ(m, reader.get_last_dispatched_message()); | 
|  | } | 
|  |  | 
|  | TEST(ChannelReaderTest, AttachmentNotYetBrokered) { | 
|  | std::unique_ptr<base::MessageLoop> message_loop(new base::MessageLoopForIO()); | 
|  |  | 
|  | MockAttachmentBroker broker; | 
|  | MockChannelReader reader; | 
|  | reader.set_broker(&broker); | 
|  | scoped_refptr<MockAttachment> attachment(new MockAttachment); | 
|  |  | 
|  | Message* m = new Message; | 
|  | PlaceholderBrokerableAttachment* needs_brokering_attachment = | 
|  | new PlaceholderBrokerableAttachment(attachment->GetIdentifier()); | 
|  | EXPECT_TRUE(m->WriteAttachment(needs_brokering_attachment)); | 
|  | reader.AddMessageForDispatch(m); | 
|  | EXPECT_EQ(ChannelReader::DISPATCH_WAITING_ON_BROKER, | 
|  | reader.DispatchMessages()); | 
|  | EXPECT_EQ(nullptr, reader.get_last_dispatched_message()); | 
|  |  | 
|  | broker.AddAttachment(attachment); | 
|  | base::RunLoop run_loop; | 
|  | run_loop.RunUntilIdle(); | 
|  |  | 
|  | EXPECT_EQ(m, reader.get_last_dispatched_message()); | 
|  | } | 
|  |  | 
|  | #endif  // USE_ATTACHMENT_BROKER | 
|  |  | 
|  | #if !USE_ATTACHMENT_BROKER | 
|  |  | 
|  | // We can determine message size from its header (and hence resize the buffer) | 
|  | // only when attachment broker is not used, see IPC::Message::FindNext(). | 
|  |  | 
|  | TEST(ChannelReaderTest, ResizeOverflowBuffer) { | 
|  | MockChannelReader reader; | 
|  |  | 
|  | ExposedMessage::Header header = {}; | 
|  |  | 
|  | header.payload_size = 128 * 1024; | 
|  | EXPECT_LT(reader.input_overflow_buf_.capacity(), header.payload_size); | 
|  | EXPECT_TRUE(reader.TranslateInputData( | 
|  | reinterpret_cast<const char*>(&header), sizeof(header))); | 
|  |  | 
|  | // Once message header is available we resize overflow buffer to | 
|  | // fit the entire message. | 
|  | EXPECT_GE(reader.input_overflow_buf_.capacity(), header.payload_size); | 
|  | } | 
|  |  | 
|  | TEST(ChannelReaderTest, InvalidMessageSize) { | 
|  | MockChannelReader reader; | 
|  |  | 
|  | ExposedMessage::Header header = {}; | 
|  |  | 
|  | size_t capacity_before = reader.input_overflow_buf_.capacity(); | 
|  |  | 
|  | // Message is slightly larger than maximum allowed size | 
|  | header.payload_size = Channel::kMaximumMessageSize + 1; | 
|  | EXPECT_FALSE(reader.TranslateInputData( | 
|  | reinterpret_cast<const char*>(&header), sizeof(header))); | 
|  | EXPECT_LE(reader.input_overflow_buf_.capacity(), capacity_before); | 
|  |  | 
|  | // Payload size is negative, overflow is detected by Pickle::PeekNext() | 
|  | header.payload_size = static_cast<uint32_t>(-1); | 
|  | EXPECT_FALSE(reader.TranslateInputData( | 
|  | reinterpret_cast<const char*>(&header), sizeof(header))); | 
|  | EXPECT_LE(reader.input_overflow_buf_.capacity(), capacity_before); | 
|  |  | 
|  | // Payload size is maximum int32_t value | 
|  | header.payload_size = std::numeric_limits<int32_t>::max(); | 
|  | EXPECT_FALSE(reader.TranslateInputData( | 
|  | reinterpret_cast<const char*>(&header), sizeof(header))); | 
|  | EXPECT_LE(reader.input_overflow_buf_.capacity(), capacity_before); | 
|  | } | 
|  |  | 
|  | #endif  // !USE_ATTACHMENT_BROKER | 
|  |  | 
|  | TEST(ChannelReaderTest, TrimBuffer) { | 
|  | // ChannelReader uses std::string as a buffer, and calls reserve() | 
|  | // to trim it to kMaximumReadBufferSize. However, an implementation | 
|  | // is free to actually reserve a larger amount. | 
|  | size_t trimmed_buffer_size; | 
|  | { | 
|  | std::string buf; | 
|  | buf.reserve(Channel::kMaximumReadBufferSize); | 
|  | trimmed_buffer_size = buf.capacity(); | 
|  | } | 
|  |  | 
|  | // Buffer is trimmed after message is processed. | 
|  | { | 
|  | MockChannelReader reader; | 
|  |  | 
|  | Message message; | 
|  | message.WriteString(std::string(LargePayloadSize, 'X')); | 
|  |  | 
|  | // Sanity check | 
|  | EXPECT_TRUE(message.size() > trimmed_buffer_size); | 
|  |  | 
|  | // Initially buffer is small | 
|  | EXPECT_LE(reader.input_overflow_buf_.capacity(), trimmed_buffer_size); | 
|  |  | 
|  | // Write and process large message | 
|  | reader.AppendMessageData(message); | 
|  | EXPECT_EQ(ChannelReader::DISPATCH_FINISHED, | 
|  | reader.ProcessIncomingMessages()); | 
|  |  | 
|  | // After processing large message buffer is trimmed | 
|  | EXPECT_EQ(reader.input_overflow_buf_.capacity(), trimmed_buffer_size); | 
|  | } | 
|  |  | 
|  | // Buffer is trimmed only after entire message is processed. | 
|  | { | 
|  | MockChannelReader reader; | 
|  |  | 
|  | ExposedMessage message; | 
|  | message.WriteString(std::string(LargePayloadSize, 'X')); | 
|  |  | 
|  | // Write and process message header | 
|  | reader.AppendData(message.header(), sizeof(ExposedMessage::Header)); | 
|  | EXPECT_EQ(ChannelReader::DISPATCH_FINISHED, | 
|  | reader.ProcessIncomingMessages()); | 
|  |  | 
|  | #if MESSAGE_FINDNEXT_PARTIAL | 
|  | // We determined message size for the message from its header, so | 
|  | // we resized the buffer to fit. | 
|  | EXPECT_GE(reader.input_overflow_buf_.capacity(), message.size()); | 
|  | #else | 
|  | // We couldn't determine message size, so we didn't resize the buffer. | 
|  | #endif | 
|  |  | 
|  | // Write and process payload | 
|  | reader.AppendData(message.payload(), message.payload_size()); | 
|  | EXPECT_EQ(ChannelReader::DISPATCH_FINISHED, | 
|  | reader.ProcessIncomingMessages()); | 
|  |  | 
|  | // But once we process the message, we trim the buffer | 
|  | EXPECT_EQ(reader.input_overflow_buf_.capacity(), trimmed_buffer_size); | 
|  | } | 
|  |  | 
|  | // Buffer is not trimmed if the next message is also large. | 
|  | { | 
|  | MockChannelReader reader; | 
|  |  | 
|  | // Write large message | 
|  | Message message1; | 
|  | message1.WriteString(std::string(LargePayloadSize * 2, 'X')); | 
|  | reader.AppendMessageData(message1); | 
|  |  | 
|  | // Write header for the next large message | 
|  | ExposedMessage message2; | 
|  | message2.WriteString(std::string(LargePayloadSize, 'Y')); | 
|  | reader.AppendData(message2.header(), sizeof(ExposedMessage::Header)); | 
|  |  | 
|  | // Process messages | 
|  | EXPECT_EQ(ChannelReader::DISPATCH_FINISHED, | 
|  | reader.ProcessIncomingMessages()); | 
|  |  | 
|  | #if MESSAGE_FINDNEXT_PARTIAL | 
|  | // We determined message size for the second (partial) message, so | 
|  | // we resized the buffer to fit. | 
|  | EXPECT_GE(reader.input_overflow_buf_.capacity(), message1.size()); | 
|  | #else | 
|  | // We couldn't determine message size for the second (partial) message, | 
|  | // so we trimmed the buffer. | 
|  | EXPECT_EQ(reader.input_overflow_buf_.capacity(), trimmed_buffer_size); | 
|  | #endif | 
|  | } | 
|  |  | 
|  | // Buffer resized appropriately if next message is larger than the first. | 
|  | // (Similar to the test above except for the order of messages.) | 
|  | { | 
|  | MockChannelReader reader; | 
|  |  | 
|  | // Write large message | 
|  | Message message1; | 
|  | message1.WriteString(std::string(LargePayloadSize, 'Y')); | 
|  | reader.AppendMessageData(message1); | 
|  |  | 
|  | // Write header for the next even larger message | 
|  | ExposedMessage message2; | 
|  | message2.WriteString(std::string(LargePayloadSize * 2, 'X')); | 
|  | reader.AppendData(message2.header(), sizeof(ExposedMessage::Header)); | 
|  |  | 
|  | // Process messages | 
|  | EXPECT_EQ(ChannelReader::DISPATCH_FINISHED, | 
|  | reader.ProcessIncomingMessages()); | 
|  |  | 
|  | #if MESSAGE_FINDNEXT_PARTIAL | 
|  | // We determined message size for the second (partial) message, and | 
|  | // resized the buffer to fit it. | 
|  | EXPECT_GE(reader.input_overflow_buf_.capacity(), message2.size()); | 
|  | #else | 
|  | // We couldn't determine message size for the second (partial) message, | 
|  | // so we trimmed the buffer. | 
|  | EXPECT_EQ(reader.input_overflow_buf_.capacity(), trimmed_buffer_size); | 
|  | #endif | 
|  | } | 
|  |  | 
|  | // Buffer is not trimmed if we've just resized it to accommodate large | 
|  | // incoming message. | 
|  | { | 
|  | MockChannelReader reader; | 
|  |  | 
|  | // Write small message | 
|  | Message message1; | 
|  | message1.WriteString(std::string(11, 'X')); | 
|  | reader.AppendMessageData(message1); | 
|  |  | 
|  | // Write header for the next large message | 
|  | ExposedMessage message2; | 
|  | message2.WriteString(std::string(LargePayloadSize, 'Y')); | 
|  | reader.AppendData(message2.header(), sizeof(ExposedMessage::Header)); | 
|  |  | 
|  | EXPECT_EQ(ChannelReader::DISPATCH_FINISHED, | 
|  | reader.ProcessIncomingMessages()); | 
|  |  | 
|  | #if MESSAGE_FINDNEXT_PARTIAL | 
|  | // We determined message size for the second (partial) message, so | 
|  | // we resized the buffer to fit. | 
|  | EXPECT_GE(reader.input_overflow_buf_.capacity(), message2.size()); | 
|  | #else | 
|  | // We couldn't determine size for the second (partial) message, and | 
|  | // first message was small, so we did nothing. | 
|  | #endif | 
|  | } | 
|  | } | 
|  |  | 
|  | }  // namespace internal | 
|  | }  // namespace IPC |