957 lines
38 KiB
C++
957 lines
38 KiB
C++
// 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
|