|  | // 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"; | 
|  | const char kTestQuitMessage[] = "quit"; | 
|  |  | 
|  | 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); | 
|  | EXPECT_EQ(kTestQuitMessage, MojoTestBase::ReadMessage(h)); | 
|  | } | 
|  |  | 
|  | 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())); | 
|  | MojoTestBase::WriteMessage(h, kTestQuitMessage); | 
|  | }); | 
|  | } | 
|  |  | 
|  | 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); | 
|  |  | 
|  | EXPECT_EQ(kTestQuitMessage, MojoTestBase::ReadMessage(h)); | 
|  | } | 
|  |  | 
|  | 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())); | 
|  |  | 
|  | MojoTestBase::WriteMessage(h, kTestQuitMessage); | 
|  | }); | 
|  | } | 
|  |  | 
|  | #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 |