// Copyright 2017 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 <algorithm> #include <memory> #include <utility> #include <vector> #include "base/memory/ptr_util.h" #include "base/numerics/safe_math.h" #include "base/rand_util.h" #include "build/build_config.h" #include "mojo/core/test/mojo_test_base.h" #include "mojo/core/user_message_impl.h" #include "mojo/public/cpp/platform/platform_channel.h" #include "mojo/public/cpp/system/buffer.h" #include "mojo/public/cpp/system/message_pipe.h" #include "mojo/public/cpp/system/platform_handle.h" namespace mojo { namespace core { namespace { using MessageTest = test::MojoTestBase; // Helper class which provides a base implementation for an unserialized user // message context and helpers to go between these objects and opaque message // handles. class TestMessageBase { public: virtual ~TestMessageBase() {} static MojoMessageHandle MakeMessageHandle( std::unique_ptr<TestMessageBase> message) { MojoMessageHandle handle; MojoResult rv = MojoCreateMessage(nullptr, &handle); DCHECK_EQ(MOJO_RESULT_OK, rv); rv = MojoSetMessageContext( handle, reinterpret_cast<uintptr_t>(message.release()), &TestMessageBase::SerializeMessageContext, &TestMessageBase::DestroyMessageContext, nullptr); DCHECK_EQ(MOJO_RESULT_OK, rv); return handle; } template <typename T> static std::unique_ptr<T> UnwrapMessageHandle( MojoMessageHandle* message_handle) { MojoMessageHandle handle = MOJO_HANDLE_INVALID; std::swap(handle, *message_handle); uintptr_t context; MojoResult rv = MojoGetMessageContext(handle, nullptr, &context); DCHECK_EQ(MOJO_RESULT_OK, rv); rv = MojoSetMessageContext(handle, 0, nullptr, nullptr, nullptr); DCHECK_EQ(MOJO_RESULT_OK, rv); MojoDestroyMessage(handle); return base::WrapUnique(reinterpret_cast<T*>(context)); } protected: virtual void GetSerializedSize(size_t* num_bytes, size_t* num_handles) = 0; virtual void SerializeHandles(MojoHandle* handles) = 0; virtual void SerializePayload(void* buffer) = 0; private: static void SerializeMessageContext(MojoMessageHandle message_handle, uintptr_t context) { auto* message = reinterpret_cast<TestMessageBase*>(context); size_t num_bytes = 0; size_t num_handles = 0; message->GetSerializedSize(&num_bytes, &num_handles); std::vector<MojoHandle> handles(num_handles); if (num_handles) message->SerializeHandles(handles.data()); MojoAppendMessageDataOptions options; options.struct_size = sizeof(options); options.flags = MOJO_APPEND_MESSAGE_DATA_FLAG_COMMIT_SIZE; void* buffer; uint32_t buffer_size; MojoResult rv = MojoAppendMessageData( message_handle, base::checked_cast<uint32_t>(num_bytes), handles.data(), base::checked_cast<uint32_t>(num_handles), &options, &buffer, &buffer_size); DCHECK_EQ(MOJO_RESULT_OK, rv); DCHECK_GE(buffer_size, base::checked_cast<uint32_t>(num_bytes)); if (num_bytes) message->SerializePayload(buffer); } static void DestroyMessageContext(uintptr_t context) { delete reinterpret_cast<TestMessageBase*>(context); } }; class NeverSerializedMessage : public TestMessageBase { public: NeverSerializedMessage( const base::Closure& destruction_callback = base::Closure()) : destruction_callback_(destruction_callback) {} ~NeverSerializedMessage() override { if (destruction_callback_) destruction_callback_.Run(); } private: // TestMessageBase: void GetSerializedSize(size_t* num_bytes, size_t* num_handles) override { NOTREACHED(); } void SerializeHandles(MojoHandle* handles) override { NOTREACHED(); } void SerializePayload(void* buffer) override { NOTREACHED(); } const base::Closure destruction_callback_; DISALLOW_COPY_AND_ASSIGN(NeverSerializedMessage); }; class SimpleMessage : public TestMessageBase { public: SimpleMessage(const std::string& contents, const base::Closure& destruction_callback = base::Closure()) : contents_(contents), destruction_callback_(destruction_callback) {} ~SimpleMessage() override { if (destruction_callback_) destruction_callback_.Run(); } void AddMessagePipe(mojo::ScopedMessagePipeHandle handle) { handles_.emplace_back(std::move(handle)); } std::vector<mojo::ScopedMessagePipeHandle>& handles() { return handles_; } private: // TestMessageBase: void GetSerializedSize(size_t* num_bytes, size_t* num_handles) override { *num_bytes = contents_.size(); *num_handles = handles_.size(); } void SerializeHandles(MojoHandle* handles) override { ASSERT_TRUE(!handles_.empty()); for (size_t i = 0; i < handles_.size(); ++i) handles[i] = handles_[i].release().value(); handles_.clear(); } void SerializePayload(void* buffer) override { std::copy(contents_.begin(), contents_.end(), static_cast<char*>(buffer)); } const std::string contents_; const base::Closure destruction_callback_; std::vector<mojo::ScopedMessagePipeHandle> handles_; DISALLOW_COPY_AND_ASSIGN(SimpleMessage); }; TEST_F(MessageTest, InvalidMessageObjects) { ASSERT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoDestroyMessage(MOJO_MESSAGE_HANDLE_INVALID)); ASSERT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoAppendMessageData(MOJO_MESSAGE_HANDLE_INVALID, 0, nullptr, 0, nullptr, nullptr, nullptr)); ASSERT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoGetMessageData(MOJO_MESSAGE_HANDLE_INVALID, nullptr, nullptr, nullptr, nullptr, nullptr)); ASSERT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoSerializeMessage(MOJO_MESSAGE_HANDLE_INVALID, nullptr)); MojoMessageHandle message_handle; ASSERT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoCreateMessage(nullptr, nullptr)); ASSERT_EQ(MOJO_RESULT_OK, MojoCreateMessage(nullptr, &message_handle)); ASSERT_EQ(MOJO_RESULT_OK, MojoSetMessageContext(message_handle, 0, nullptr, nullptr, nullptr)); ASSERT_EQ(MOJO_RESULT_OK, MojoDestroyMessage(message_handle)); } TEST_F(MessageTest, SendLocalMessageWithContext) { // Simple write+read of a message with context. Verifies that such messages // are passed through a local pipe without serialization. auto message = std::make_unique<NeverSerializedMessage>(); auto* original_message = message.get(); MojoHandle a, b; CreateMessagePipe(&a, &b); EXPECT_EQ( MOJO_RESULT_OK, MojoWriteMessage( a, TestMessageBase::MakeMessageHandle(std::move(message)), nullptr)); EXPECT_EQ(MOJO_RESULT_OK, WaitForSignals(b, MOJO_HANDLE_SIGNAL_READABLE)); MojoMessageHandle read_message_handle; EXPECT_EQ(MOJO_RESULT_OK, MojoReadMessage(b, nullptr, &read_message_handle)); message = TestMessageBase::UnwrapMessageHandle<NeverSerializedMessage>( &read_message_handle); EXPECT_EQ(original_message, message.get()); MojoClose(a); MojoClose(b); } TEST_F(MessageTest, DestroyMessageWithContext) { // Tests that |MojoDestroyMessage()| destroys any attached context. bool was_deleted = false; auto message = std::make_unique<NeverSerializedMessage>( base::Bind([](bool* was_deleted) { *was_deleted = true; }, &was_deleted)); MojoMessageHandle handle = TestMessageBase::MakeMessageHandle(std::move(message)); EXPECT_FALSE(was_deleted); EXPECT_EQ(MOJO_RESULT_OK, MojoDestroyMessage(handle)); EXPECT_TRUE(was_deleted); } const char kTestMessageWithContext1[] = "hello laziness"; #if !defined(OS_IOS) const char kTestMessageWithContext2[] = "my old friend"; const char kTestMessageWithContext3[] = "something something"; const char kTestMessageWithContext4[] = "do moar ipc"; DEFINE_TEST_CLIENT_TEST_WITH_PIPE(ReceiveMessageNoHandles, MessageTest, h) { MojoTestBase::WaitForSignals(h, MOJO_HANDLE_SIGNAL_READABLE); auto m = MojoTestBase::ReadMessage(h); EXPECT_EQ(kTestMessageWithContext1, m); } TEST_F(MessageTest, SerializeSimpleMessageNoHandlesWithContext) { RunTestClient("ReceiveMessageNoHandles", [&](MojoHandle h) { auto message = std::make_unique<SimpleMessage>(kTestMessageWithContext1); MojoWriteMessage(h, TestMessageBase::MakeMessageHandle(std::move(message)), nullptr); }); } TEST_F(MessageTest, SerializeDynamicallySizedMessage) { RunTestClient("ReceiveMessageNoHandles", [&](MojoHandle h) { MojoMessageHandle message; EXPECT_EQ(MOJO_RESULT_OK, MojoCreateMessage(nullptr, &message)); void* buffer; uint32_t buffer_size; EXPECT_EQ(MOJO_RESULT_OK, MojoAppendMessageData(message, 0, nullptr, 0, nullptr, &buffer, &buffer_size)); MojoAppendMessageDataOptions options; options.struct_size = sizeof(options); options.flags = MOJO_APPEND_MESSAGE_DATA_FLAG_COMMIT_SIZE; EXPECT_EQ(MOJO_RESULT_OK, MojoAppendMessageData( message, sizeof(kTestMessageWithContext1) - 1, nullptr, 0, &options, &buffer, &buffer_size)); memcpy(buffer, kTestMessageWithContext1, sizeof(kTestMessageWithContext1) - 1); MojoWriteMessage(h, message, nullptr); }); } DEFINE_TEST_CLIENT_TEST_WITH_PIPE(ReceiveMessageOneHandle, MessageTest, h) { MojoTestBase::WaitForSignals(h, MOJO_HANDLE_SIGNAL_READABLE); MojoHandle h1; auto m = MojoTestBase::ReadMessageWithHandles(h, &h1, 1); EXPECT_EQ(kTestMessageWithContext1, m); MojoTestBase::WriteMessage(h1, kTestMessageWithContext2); } TEST_F(MessageTest, SerializeSimpleMessageOneHandleWithContext) { RunTestClient("ReceiveMessageOneHandle", [&](MojoHandle h) { auto message = std::make_unique<SimpleMessage>(kTestMessageWithContext1); mojo::MessagePipe pipe; message->AddMessagePipe(std::move(pipe.handle0)); MojoWriteMessage(h, TestMessageBase::MakeMessageHandle(std::move(message)), nullptr); EXPECT_EQ(kTestMessageWithContext2, MojoTestBase::ReadMessage(pipe.handle1.get().value())); }); } DEFINE_TEST_CLIENT_TEST_WITH_PIPE(ReceiveMessageWithHandles, MessageTest, h) { MojoTestBase::WaitForSignals(h, MOJO_HANDLE_SIGNAL_READABLE); MojoHandle handles[4]; auto m = MojoTestBase::ReadMessageWithHandles(h, handles, 4); EXPECT_EQ(kTestMessageWithContext1, m); MojoTestBase::WriteMessage(handles[0], kTestMessageWithContext1); MojoTestBase::WriteMessage(handles[1], kTestMessageWithContext2); MojoTestBase::WriteMessage(handles[2], kTestMessageWithContext3); MojoTestBase::WriteMessage(handles[3], kTestMessageWithContext4); } TEST_F(MessageTest, SerializeSimpleMessageWithHandlesWithContext) { RunTestClient("ReceiveMessageWithHandles", [&](MojoHandle h) { auto message = std::make_unique<SimpleMessage>(kTestMessageWithContext1); mojo::MessagePipe pipes[4]; message->AddMessagePipe(std::move(pipes[0].handle0)); message->AddMessagePipe(std::move(pipes[1].handle0)); message->AddMessagePipe(std::move(pipes[2].handle0)); message->AddMessagePipe(std::move(pipes[3].handle0)); MojoWriteMessage(h, TestMessageBase::MakeMessageHandle(std::move(message)), nullptr); EXPECT_EQ(kTestMessageWithContext1, MojoTestBase::ReadMessage(pipes[0].handle1.get().value())); EXPECT_EQ(kTestMessageWithContext2, MojoTestBase::ReadMessage(pipes[1].handle1.get().value())); EXPECT_EQ(kTestMessageWithContext3, MojoTestBase::ReadMessage(pipes[2].handle1.get().value())); EXPECT_EQ(kTestMessageWithContext4, MojoTestBase::ReadMessage(pipes[3].handle1.get().value())); }); } #endif // !defined(OS_IOS) TEST_F(MessageTest, SendLocalSimpleMessageWithHandlesWithContext) { auto message = std::make_unique<SimpleMessage>(kTestMessageWithContext1); auto* original_message = message.get(); mojo::MessagePipe pipes[4]; MojoHandle original_handles[4] = { pipes[0].handle0.get().value(), pipes[1].handle0.get().value(), pipes[2].handle0.get().value(), pipes[3].handle0.get().value(), }; message->AddMessagePipe(std::move(pipes[0].handle0)); message->AddMessagePipe(std::move(pipes[1].handle0)); message->AddMessagePipe(std::move(pipes[2].handle0)); message->AddMessagePipe(std::move(pipes[3].handle0)); MojoHandle a, b; CreateMessagePipe(&a, &b); EXPECT_EQ( MOJO_RESULT_OK, MojoWriteMessage( a, TestMessageBase::MakeMessageHandle(std::move(message)), nullptr)); EXPECT_EQ(MOJO_RESULT_OK, WaitForSignals(b, MOJO_HANDLE_SIGNAL_READABLE)); MojoMessageHandle read_message_handle; EXPECT_EQ(MOJO_RESULT_OK, MojoReadMessage(b, nullptr, &read_message_handle)); message = TestMessageBase::UnwrapMessageHandle<SimpleMessage>(&read_message_handle); EXPECT_EQ(original_message, message.get()); ASSERT_EQ(4u, message->handles().size()); EXPECT_EQ(original_handles[0], message->handles()[0].get().value()); EXPECT_EQ(original_handles[1], message->handles()[1].get().value()); EXPECT_EQ(original_handles[2], message->handles()[2].get().value()); EXPECT_EQ(original_handles[3], message->handles()[3].get().value()); MojoClose(a); MojoClose(b); } TEST_F(MessageTest, DropUnreadLocalMessageWithContext) { // Verifies that if a message is sent with context over a pipe and the // receiver closes without reading the message, the context is properly // cleaned up. bool message_was_destroyed = false; auto message = std::make_unique<SimpleMessage>( kTestMessageWithContext1, base::Bind([](bool* was_destroyed) { *was_destroyed = true; }, &message_was_destroyed)); mojo::MessagePipe pipe; message->AddMessagePipe(std::move(pipe.handle0)); MojoHandle a, b; CreateMessagePipe(&a, &b); EXPECT_EQ( MOJO_RESULT_OK, MojoWriteMessage( a, TestMessageBase::MakeMessageHandle(std::move(message)), nullptr)); MojoClose(a); MojoClose(b); EXPECT_TRUE(message_was_destroyed); EXPECT_EQ(MOJO_RESULT_OK, WaitForSignals(pipe.handle1.get().value(), MOJO_HANDLE_SIGNAL_PEER_CLOSED)); } TEST_F(MessageTest, GetMessageDataWithHandles) { MojoHandle h[2]; CreateMessagePipe(&h[0], &h[1]); MojoMessageHandle message_handle; EXPECT_EQ(MOJO_RESULT_OK, MojoCreateMessage(nullptr, &message_handle)); MojoAppendMessageDataOptions append_data_options; append_data_options.struct_size = sizeof(append_data_options); append_data_options.flags = MOJO_APPEND_MESSAGE_DATA_FLAG_COMMIT_SIZE; const std::string kTestMessage = "hello"; void* buffer; uint32_t buffer_size; ASSERT_EQ(MOJO_RESULT_OK, MojoAppendMessageData( message_handle, static_cast<uint32_t>(kTestMessage.size()), h, 2, &append_data_options, &buffer, &buffer_size)); memcpy(buffer, kTestMessage.data(), kTestMessage.size()); // Ignore handles the first time around. This should mean a subsequent call is // allowed to grab the handles. MojoGetMessageDataOptions get_data_options; get_data_options.struct_size = sizeof(get_data_options); get_data_options.flags = MOJO_GET_MESSAGE_DATA_FLAG_IGNORE_HANDLES; EXPECT_EQ(MOJO_RESULT_OK, MojoGetMessageData(message_handle, &get_data_options, &buffer, &buffer_size, nullptr, nullptr)); // Now grab the handles. uint32_t num_handles = 2; EXPECT_EQ(MOJO_RESULT_OK, MojoGetMessageData(message_handle, nullptr, &buffer, &buffer_size, h, &num_handles)); EXPECT_EQ(2u, num_handles); // Should still be callable as long as we ignore handles. EXPECT_EQ(MOJO_RESULT_OK, MojoGetMessageData(message_handle, &get_data_options, &buffer, &buffer_size, nullptr, nullptr)); // But not if we don't. EXPECT_EQ(MOJO_RESULT_NOT_FOUND, MojoGetMessageData(message_handle, nullptr, &buffer, &buffer_size, h, &num_handles)); EXPECT_EQ(MOJO_RESULT_OK, MojoDestroyMessage(message_handle)); } TEST_F(MessageTest, ReadMessageWithContextAsSerializedMessage) { bool message_was_destroyed = false; std::unique_ptr<TestMessageBase> message = std::make_unique<NeverSerializedMessage>( base::Bind([](bool* was_destroyed) { *was_destroyed = true; }, &message_was_destroyed)); MojoHandle a, b; CreateMessagePipe(&a, &b); EXPECT_EQ( MOJO_RESULT_OK, MojoWriteMessage( a, TestMessageBase::MakeMessageHandle(std::move(message)), nullptr)); EXPECT_EQ(MOJO_RESULT_OK, WaitForSignals(b, MOJO_HANDLE_SIGNAL_READABLE)); MojoMessageHandle message_handle; EXPECT_EQ(MOJO_RESULT_OK, MojoReadMessage(b, nullptr, &message_handle)); EXPECT_FALSE(message_was_destroyed); // Not a serialized message, so we can't get serialized contents. uint32_t num_bytes = 0; void* buffer; uint32_t num_handles = 0; EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, MojoGetMessageData(message_handle, nullptr, &buffer, &num_bytes, nullptr, &num_handles)); EXPECT_FALSE(message_was_destroyed); EXPECT_EQ(MOJO_RESULT_OK, MojoDestroyMessage(message_handle)); EXPECT_TRUE(message_was_destroyed); MojoClose(a); MojoClose(b); } TEST_F(MessageTest, ReadSerializedMessageAsMessageWithContext) { MojoHandle a, b; CreateMessagePipe(&a, &b); MojoTestBase::WriteMessage(a, "hello there"); EXPECT_EQ(MOJO_RESULT_OK, WaitForSignals(b, MOJO_HANDLE_SIGNAL_READABLE)); MojoMessageHandle message_handle; EXPECT_EQ(MOJO_RESULT_OK, MojoReadMessage(b, nullptr, &message_handle)); uintptr_t context; EXPECT_EQ(MOJO_RESULT_NOT_FOUND, MojoGetMessageContext(message_handle, nullptr, &context)); MojoClose(a); MojoClose(b); EXPECT_EQ(MOJO_RESULT_OK, MojoDestroyMessage(message_handle)); } TEST_F(MessageTest, ForceSerializeMessageWithContext) { // Basic test - we can serialize a simple message. bool message_was_destroyed = false; auto message = std::make_unique<SimpleMessage>( kTestMessageWithContext1, base::Bind([](bool* was_destroyed) { *was_destroyed = true; }, &message_was_destroyed)); auto message_handle = TestMessageBase::MakeMessageHandle(std::move(message)); EXPECT_EQ(MOJO_RESULT_OK, MojoSerializeMessage(message_handle, nullptr)); EXPECT_TRUE(message_was_destroyed); EXPECT_EQ(MOJO_RESULT_OK, MojoDestroyMessage(message_handle)); // Serialize a message with a single handle. Freeing the message should close // the handle. message_was_destroyed = false; message = std::make_unique<SimpleMessage>( kTestMessageWithContext1, base::Bind([](bool* was_destroyed) { *was_destroyed = true; }, &message_was_destroyed)); MessagePipe pipe1; message->AddMessagePipe(std::move(pipe1.handle0)); message_handle = TestMessageBase::MakeMessageHandle(std::move(message)); EXPECT_EQ(MOJO_RESULT_OK, MojoSerializeMessage(message_handle, nullptr)); EXPECT_TRUE(message_was_destroyed); EXPECT_EQ(MOJO_RESULT_OK, MojoDestroyMessage(message_handle)); EXPECT_EQ(MOJO_RESULT_OK, WaitForSignals(pipe1.handle1.get().value(), MOJO_HANDLE_SIGNAL_PEER_CLOSED)); // Serialize a message with a handle and extract its serialized contents. message_was_destroyed = false; message = std::make_unique<SimpleMessage>( kTestMessageWithContext1, base::Bind([](bool* was_destroyed) { *was_destroyed = true; }, &message_was_destroyed)); MessagePipe pipe2; message->AddMessagePipe(std::move(pipe2.handle0)); message_handle = TestMessageBase::MakeMessageHandle(std::move(message)); EXPECT_EQ(MOJO_RESULT_OK, MojoSerializeMessage(message_handle, nullptr)); EXPECT_TRUE(message_was_destroyed); uint32_t num_bytes = 0; void* buffer = nullptr; uint32_t num_handles = 0; MojoHandle extracted_handle; EXPECT_EQ(MOJO_RESULT_RESOURCE_EXHAUSTED, MojoGetMessageData(message_handle, nullptr, &buffer, &num_bytes, nullptr, &num_handles)); EXPECT_EQ(MOJO_RESULT_OK, MojoGetMessageData(message_handle, nullptr, &buffer, &num_bytes, &extracted_handle, &num_handles)); EXPECT_EQ(std::string(kTestMessageWithContext1).size(), num_bytes); EXPECT_EQ(std::string(kTestMessageWithContext1), base::StringPiece(static_cast<char*>(buffer), num_bytes)); // Confirm that the handle we extracted from the serialized message is still // connected to the same peer, despite the fact that its handle value may have // changed. const char kTestMessage[] = "hey you"; MojoTestBase::WriteMessage(pipe2.handle1.get().value(), kTestMessage); EXPECT_EQ(MOJO_RESULT_OK, WaitForSignals(extracted_handle, MOJO_HANDLE_SIGNAL_READABLE)); EXPECT_EQ(kTestMessage, MojoTestBase::ReadMessage(extracted_handle)); EXPECT_EQ(MOJO_RESULT_OK, MojoDestroyMessage(message_handle)); } TEST_F(MessageTest, DoubleSerialize) { bool message_was_destroyed = false; auto message = std::make_unique<SimpleMessage>( kTestMessageWithContext1, base::Bind([](bool* was_destroyed) { *was_destroyed = true; }, &message_was_destroyed)); auto message_handle = TestMessageBase::MakeMessageHandle(std::move(message)); // Ensure we can safely call |MojoSerializeMessage()| twice on the same // message handle. EXPECT_EQ(MOJO_RESULT_OK, MojoSerializeMessage(message_handle, nullptr)); EXPECT_TRUE(message_was_destroyed); EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, MojoSerializeMessage(message_handle, nullptr)); // And also check that we can call it again after we've written and read the // message object from a pipe. MessagePipe pipe; EXPECT_EQ(MOJO_RESULT_OK, MojoWriteMessage(pipe.handle0->value(), message_handle, nullptr)); EXPECT_EQ(MOJO_RESULT_OK, WaitForSignals(pipe.handle1->value(), MOJO_HANDLE_SIGNAL_READABLE)); EXPECT_EQ(MOJO_RESULT_OK, MojoReadMessage(pipe.handle1->value(), nullptr, &message_handle)); EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, MojoSerializeMessage(message_handle, nullptr)); EXPECT_EQ(MOJO_RESULT_OK, MojoDestroyMessage(message_handle)); } TEST_F(MessageTest, ExtendMessagePayload) { MojoMessageHandle message; EXPECT_EQ(MOJO_RESULT_OK, MojoCreateMessage(nullptr, &message)); const std::string kTestMessagePart1("hello i am message."); void* buffer; uint32_t buffer_size; EXPECT_EQ(MOJO_RESULT_OK, MojoAppendMessageData( message, static_cast<uint32_t>(kTestMessagePart1.size()), nullptr, 0, nullptr, &buffer, &buffer_size)); ASSERT_GE(buffer_size, static_cast<uint32_t>(kTestMessagePart1.size())); memcpy(buffer, kTestMessagePart1.data(), kTestMessagePart1.size()); const std::string kTestMessagePart2 = " in ur computer."; const std::string kTestMessageCombined1 = kTestMessagePart1 + kTestMessagePart2; EXPECT_EQ(MOJO_RESULT_OK, MojoAppendMessageData( message, static_cast<uint32_t>(kTestMessagePart2.size()), nullptr, 0, nullptr, &buffer, &buffer_size)); memcpy(static_cast<uint8_t*>(buffer) + kTestMessagePart1.size(), kTestMessagePart2.data(), kTestMessagePart2.size()); const std::string kTestMessagePart3 = kTestMessagePart2 + " carry ur bits."; const std::string kTestMessageCombined2 = kTestMessageCombined1 + kTestMessagePart3; EXPECT_EQ(MOJO_RESULT_OK, MojoAppendMessageData( message, static_cast<uint32_t>(kTestMessagePart3.size()), nullptr, 0, nullptr, &buffer, &buffer_size)); memcpy(static_cast<uint8_t*>(buffer) + kTestMessageCombined1.size(), kTestMessagePart3.data(), kTestMessagePart3.size()); void* payload; uint32_t payload_size; EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, MojoGetMessageData(message, nullptr, &payload, &payload_size, nullptr, nullptr)); MojoAppendMessageDataOptions options; options.struct_size = sizeof(options); options.flags = MOJO_APPEND_MESSAGE_DATA_FLAG_COMMIT_SIZE; EXPECT_EQ(MOJO_RESULT_OK, MojoAppendMessageData(message, 0, nullptr, 0, &options, nullptr, nullptr)); EXPECT_EQ(MOJO_RESULT_OK, MojoGetMessageData(message, nullptr, &payload, &payload_size, nullptr, nullptr)); EXPECT_EQ(kTestMessageCombined2.size(), payload_size); EXPECT_EQ(0, memcmp(payload, kTestMessageCombined2.data(), kTestMessageCombined2.size())); EXPECT_EQ(MOJO_RESULT_OK, MojoDestroyMessage(message)); } TEST_F(MessageTest, ExtendMessageWithHandlesPayload) { MojoMessageHandle message; EXPECT_EQ(MOJO_RESULT_OK, MojoCreateMessage(nullptr, &message)); MojoHandle handles[2]; CreateMessagePipe(&handles[0], &handles[1]); const std::string kTestMessagePart1("hello i am message."); void* buffer; uint32_t buffer_size; EXPECT_EQ(MOJO_RESULT_OK, MojoAppendMessageData( message, static_cast<uint32_t>(kTestMessagePart1.size()), handles, 2, nullptr, &buffer, &buffer_size)); ASSERT_GE(buffer_size, static_cast<uint32_t>(kTestMessagePart1.size())); memcpy(buffer, kTestMessagePart1.data(), kTestMessagePart1.size()); const std::string kTestMessagePart2 = " in ur computer."; const std::string kTestMessageCombined1 = kTestMessagePart1 + kTestMessagePart2; MojoAppendMessageDataOptions options; options.struct_size = sizeof(options); options.flags = MOJO_APPEND_MESSAGE_DATA_FLAG_COMMIT_SIZE; EXPECT_EQ(MOJO_RESULT_OK, MojoAppendMessageData( message, static_cast<uint32_t>(kTestMessagePart2.size()), nullptr, 0, &options, &buffer, &buffer_size)); memcpy(static_cast<uint8_t*>(buffer) + kTestMessagePart1.size(), kTestMessagePart2.data(), kTestMessagePart2.size()); void* payload; uint32_t payload_size; uint32_t num_handles = 2; EXPECT_EQ(MOJO_RESULT_OK, MojoGetMessageData(message, nullptr, &payload, &payload_size, handles, &num_handles)); EXPECT_EQ(2u, num_handles); EXPECT_EQ(kTestMessageCombined1.size(), payload_size); EXPECT_EQ(0, memcmp(payload, kTestMessageCombined1.data(), kTestMessageCombined1.size())); EXPECT_EQ(MOJO_RESULT_OK, MojoClose(handles[0])); EXPECT_EQ(MOJO_RESULT_OK, MojoClose(handles[1])); EXPECT_EQ(MOJO_RESULT_OK, MojoDestroyMessage(message)); } TEST_F(MessageTest, ExtendMessagePayloadLarge) { // We progressively extend a message payload from small to large using various // chunk sizes to test potentially interesting boundary conditions. constexpr size_t kTestChunkSizes[] = {1, 2, 3, 64, 509, 4096, 16384, 65535}; for (const size_t kChunkSize : kTestChunkSizes) { MojoMessageHandle message; EXPECT_EQ(MOJO_RESULT_OK, MojoCreateMessage(nullptr, &message)); MojoHandle handles[2]; CreateMessagePipe(&handles[0], &handles[1]); const std::string kTestMessageHeader("hey pretend i'm a header"); void* buffer; uint32_t buffer_size; EXPECT_EQ(MOJO_RESULT_OK, MojoAppendMessageData( message, static_cast<uint32_t>(kTestMessageHeader.size()), handles, 2, nullptr, &buffer, &buffer_size)); ASSERT_GE(buffer_size, static_cast<uint32_t>(kTestMessageHeader.size())); memcpy(buffer, kTestMessageHeader.data(), kTestMessageHeader.size()); // 512 kB should be well beyond any reasonable default buffer size for the // system implementation to choose, meaning that this test should guarantee // several reallocations of the serialized message buffer as we // progressively extend the payload to this size. constexpr size_t kTestMessagePayloadSize = 512 * 1024; std::vector<uint8_t> test_payload(kTestMessagePayloadSize); base::RandBytes(test_payload.data(), kTestMessagePayloadSize); size_t current_payload_size = 0; while (current_payload_size < kTestMessagePayloadSize) { const size_t previous_payload_size = current_payload_size; current_payload_size = std::min(current_payload_size + kChunkSize, kTestMessagePayloadSize); const size_t current_chunk_size = current_payload_size - previous_payload_size; const size_t previous_total_size = kTestMessageHeader.size() + previous_payload_size; const size_t current_total_size = kTestMessageHeader.size() + current_payload_size; EXPECT_EQ(MOJO_RESULT_OK, MojoAppendMessageData( message, static_cast<uint32_t>(current_chunk_size), nullptr, 0, nullptr, &buffer, &buffer_size)); EXPECT_GE(buffer_size, static_cast<uint32_t>(current_total_size)); memcpy(static_cast<uint8_t*>(buffer) + previous_total_size, &test_payload[previous_payload_size], current_chunk_size); } MojoAppendMessageDataOptions options; options.struct_size = sizeof(options); options.flags = MOJO_APPEND_MESSAGE_DATA_FLAG_COMMIT_SIZE; EXPECT_EQ(MOJO_RESULT_OK, MojoAppendMessageData(message, 0, nullptr, 0, &options, nullptr, nullptr)); void* payload; uint32_t payload_size; uint32_t num_handles = 2; EXPECT_EQ(MOJO_RESULT_OK, MojoGetMessageData(message, nullptr, &payload, &payload_size, handles, &num_handles)); EXPECT_EQ(static_cast<uint32_t>(kTestMessageHeader.size() + kTestMessagePayloadSize), payload_size); EXPECT_EQ(0, memcmp(payload, kTestMessageHeader.data(), kTestMessageHeader.size())); EXPECT_EQ(0, memcmp(static_cast<uint8_t*>(payload) + kTestMessageHeader.size(), test_payload.data(), kTestMessagePayloadSize)); EXPECT_EQ(MOJO_RESULT_OK, MojoClose(handles[0])); EXPECT_EQ(MOJO_RESULT_OK, MojoClose(handles[1])); EXPECT_EQ(MOJO_RESULT_OK, MojoDestroyMessage(message)); } } TEST_F(MessageTest, CorrectPayloadBufferBoundaries) { // Exercises writes to the full extent of a message's payload under various // circumstances in an effort to catch any potential bugs in internal // allocations or reported size from Mojo APIs. MojoMessageHandle message; void* buffer = nullptr; uint32_t buffer_size = 0; EXPECT_EQ(MOJO_RESULT_OK, MojoCreateMessage(nullptr, &message)); EXPECT_EQ(MOJO_RESULT_OK, MojoAppendMessageData(message, 0, nullptr, 0, nullptr, &buffer, &buffer_size)); // Fill the buffer end-to-end. memset(buffer, 'x', buffer_size); // Continuously grow and fill the message buffer several more times. Should // not crash. constexpr uint32_t kChunkSize = 4096; constexpr size_t kNumIterations = 1000; for (size_t i = 0; i < kNumIterations; ++i) { EXPECT_EQ(MOJO_RESULT_OK, MojoAppendMessageData(message, kChunkSize, nullptr, 0, nullptr, &buffer, &buffer_size)); memset(buffer, 'x', buffer_size); } EXPECT_EQ(MOJO_RESULT_OK, MojoDestroyMessage(message)); } TEST_F(MessageTest, CommitInvalidMessageContents) { // Regression test for https://crbug.com/755127. Ensures that we don't crash // if we attempt to commit the contents of an unserialized message. MojoMessageHandle message; EXPECT_EQ(MOJO_RESULT_OK, MojoCreateMessage(nullptr, &message)); EXPECT_EQ(MOJO_RESULT_OK, MojoAppendMessageData(message, 0, nullptr, 0, nullptr, nullptr, nullptr)); MojoHandle a, b; CreateMessagePipe(&a, &b); EXPECT_EQ(MOJO_RESULT_OK, MojoAppendMessageData(message, 0, &a, 1, nullptr, nullptr, nullptr)); UserMessageImpl::FailHandleSerializationForTesting(true); MojoAppendMessageDataOptions options; options.struct_size = sizeof(options); options.flags = MOJO_APPEND_MESSAGE_DATA_FLAG_COMMIT_SIZE; EXPECT_EQ(MOJO_RESULT_OK, MojoAppendMessageData(message, 0, nullptr, 0, nullptr, nullptr, nullptr)); UserMessageImpl::FailHandleSerializationForTesting(false); EXPECT_EQ(MOJO_RESULT_OK, MojoDestroyMessage(message)); } #if !defined(OS_IOS) TEST_F(MessageTest, ExtendPayloadWithHandlesAttached) { // Regression test for https://crbug.com/748996. Verifies that internal // message objects do not retain invalid payload pointers across buffer // relocations. MojoHandle handles[5]; CreateMessagePipe(&handles[0], &handles[1]); PlatformChannel channel; handles[2] = WrapPlatformHandle(channel.TakeLocalEndpoint().TakePlatformHandle()) .release() .value(); handles[3] = WrapPlatformHandle(channel.TakeRemoteEndpoint().TakePlatformHandle()) .release() .value(); handles[4] = SharedBufferHandle::Create(64).release().value(); MojoMessageHandle message; void* buffer = nullptr; uint32_t buffer_size = 0; EXPECT_EQ(MOJO_RESULT_OK, MojoCreateMessage(nullptr, &message)); EXPECT_EQ(MOJO_RESULT_OK, MojoAppendMessageData(message, 0, handles, 5, nullptr, &buffer, &buffer_size)); // Force buffer reallocation by extending the payload beyond the original // buffer size. This should typically result in a relocation of the buffer as // well -- at least often enough that breakage will be caught by automated // tests. MojoAppendMessageDataOptions options; options.struct_size = sizeof(options); options.flags = MOJO_APPEND_MESSAGE_DATA_FLAG_COMMIT_SIZE; uint32_t payload_size = buffer_size * 64; EXPECT_EQ(MOJO_RESULT_OK, MojoAppendMessageData(message, payload_size, nullptr, 0, &options, &buffer, &buffer_size)); ASSERT_GE(buffer_size, payload_size); memset(buffer, 'x', payload_size); RunTestClient("ReadAndIgnoreMessage", [&](MojoHandle h) { // Send the message out of process to exercise the regression path where // internally cached, stale payload pointers may be dereferenced and written // into. EXPECT_EQ(MOJO_RESULT_OK, MojoWriteMessage(h, message, nullptr)); }); } DEFINE_TEST_CLIENT_TEST_WITH_PIPE(ReadAndIgnoreMessage, MessageTest, h) { MojoTestBase::WaitForSignals(h, MOJO_HANDLE_SIGNAL_READABLE); MojoHandle handles[5]; MojoTestBase::ReadMessageWithHandles(h, handles, 5); for (size_t i = 0; i < 5; ++i) EXPECT_EQ(MOJO_RESULT_OK, MojoClose(handles[i])); } TEST_F(MessageTest, ExtendPayloadWithHandlesAttachedViaExtension) { MojoHandle handles[5]; CreateMessagePipe(&handles[0], &handles[4]); PlatformChannel channel; handles[1] = WrapPlatformHandle(channel.TakeLocalEndpoint().TakePlatformHandle()) .release() .value(); handles[2] = WrapPlatformHandle(channel.TakeRemoteEndpoint().TakePlatformHandle()) .release() .value(); handles[3] = SharedBufferHandle::Create(64).release().value(); MojoMessageHandle message; void* buffer = nullptr; uint32_t buffer_size = 0; EXPECT_EQ(MOJO_RESULT_OK, MojoCreateMessage(nullptr, &message)); EXPECT_EQ(MOJO_RESULT_OK, MojoAppendMessageData(message, 0, handles, 1, nullptr, &buffer, &buffer_size)); uint32_t payload_size = buffer_size * 64; EXPECT_EQ(MOJO_RESULT_OK, MojoAppendMessageData(message, payload_size, nullptr, 0, nullptr, &buffer, nullptr)); // Add more handles. EXPECT_EQ(MOJO_RESULT_OK, MojoAppendMessageData(message, 0, handles + 1, 1, nullptr, &buffer, nullptr)); MojoAppendMessageDataOptions options; options.struct_size = sizeof(options); options.flags = MOJO_APPEND_MESSAGE_DATA_FLAG_COMMIT_SIZE; EXPECT_EQ(MOJO_RESULT_OK, MojoAppendMessageData(message, 0, handles + 2, 3, &options, &buffer, nullptr)); memset(buffer, 'x', payload_size); RunTestClient("ReadMessageAndCheckPipe", [&](MojoHandle h) { // Send the message out of process to exercise the regression path where // internally cached, stale payload pointers may be dereferenced and written // into. EXPECT_EQ(MOJO_RESULT_OK, MojoWriteMessage(h, message, nullptr)); }); } DEFINE_TEST_CLIENT_TEST_WITH_PIPE(ReadMessageAndCheckPipe, MessageTest, h) { MojoTestBase::WaitForSignals(h, MOJO_HANDLE_SIGNAL_READABLE); const std::string kTestMessage("hey pipe"); MojoHandle handles[5]; MojoTestBase::ReadMessageWithHandles(h, handles, 5); MojoTestBase::WriteMessage(handles[0], kTestMessage); MojoTestBase::WaitForSignals(handles[4], MOJO_HANDLE_SIGNAL_READABLE); EXPECT_EQ(kTestMessage, MojoTestBase::ReadMessage(handles[4])); for (size_t i = 0; i < 5; ++i) EXPECT_EQ(MOJO_RESULT_OK, MojoClose(handles[i])); } #endif // !defined(OS_IOS) TEST_F(MessageTest, PartiallySerializedMessagesDontLeakHandles) { MojoMessageHandle message; EXPECT_EQ(MOJO_RESULT_OK, MojoCreateMessage(nullptr, &message)); MojoHandle handles[2]; CreateMessagePipe(&handles[0], &handles[1]); const std::string kTestMessagePart1("hello i am message."); void* buffer; uint32_t buffer_size; EXPECT_EQ(MOJO_RESULT_OK, MojoAppendMessageData( message, static_cast<uint32_t>(kTestMessagePart1.size()), nullptr, 0, nullptr, &buffer, &buffer_size)); ASSERT_GE(buffer_size, static_cast<uint32_t>(kTestMessagePart1.size())); memcpy(buffer, kTestMessagePart1.data(), kTestMessagePart1.size()); EXPECT_EQ(MOJO_RESULT_OK, MojoAppendMessageData(message, 0, handles, 1, nullptr, &buffer, &buffer_size)); // This must close |handles[0]|, which we can detect by observing the // signal state of |handles[1]. EXPECT_EQ(MOJO_RESULT_OK, MojoDestroyMessage(message)); EXPECT_EQ(MOJO_RESULT_OK, WaitForSignals(handles[1], MOJO_HANDLE_SIGNAL_PEER_CLOSED)); } } // namespace } // namespace core } // namespace mojo