// 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 #include #include #include #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> AttachmentSet; bool SendAttachmentToProcess( const scoped_refptr& attachment, base::ProcessId destination_process) override { return false; } bool OnMessageReceived(const Message& message) override { return false; } void AddAttachment(scoped_refptr 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(buffer_len), data_.size()); memcpy(buffer, data_.data(), read_len); *bytes_read = static_cast(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(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 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) { scoped_ptr message_loop(new base::MessageLoopForIO()); MockAttachmentBroker broker; MockChannelReader reader; reader.set_broker(&broker); scoped_refptr 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(&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(&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(-1); EXPECT_FALSE(reader.TranslateInputData( reinterpret_cast(&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::max(); EXPECT_FALSE(reader.TranslateInputData( reinterpret_cast(&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