summaryrefslogtreecommitdiff
path: root/chromium/mojo/core/message_unittest.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/mojo/core/message_unittest.cc')
-rw-r--r--chromium/mojo/core/message_unittest.cc956
1 files changed, 956 insertions, 0 deletions
diff --git a/chromium/mojo/core/message_unittest.cc b/chromium/mojo/core/message_unittest.cc
new file mode 100644
index 00000000000..ce44f221441
--- /dev/null
+++ b/chromium/mojo/core/message_unittest.cc
@@ -0,0 +1,956 @@
+// 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