| // 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 |