blob: 62932f3cd12845585e2fa9a9f91bb16fb898a432 [file] [log] [blame]
// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "mojo/public/tools/fuzzers/mojolpm.h"
#include "base/compiler_specific.h"
#include "base/containers/span.h"
#include "base/no_destructor.h"
#include "mojo/public/c/system/data_pipe.h"
#include "mojo/public/cpp/system/data_pipe.h"
namespace mojolpm {
const uint32_t kPipeElementMaxSize = 0x1000u;
const uint32_t kPipeCapacityMaxSize = 0x100000u;
const uint32_t kPipeActionMaxSize = 0x100000u;
const uint64_t kSharedBufferMaxSize = 0x100000u;
const uint32_t kSharedBufferActionMaxSize = 0x100000u;
Context::Context() = default;
Context::~Context() = default;
Context::Storage::Storage() = default;
Context::Storage::~Storage() = default;
void Context::StartTestcase() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
void Context::EndTestcase() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// Some fuzzers need to destroy the fuzzer thread along with their testcase,
// so we need to detach the sequence checker here so that it will be attached
// to the new sequence for the next testcase.
DETACH_FROM_SEQUENCE(sequence_checker_);
// We need to destroy all Remotes/Receivers before we start destroying other
// objects (like callbacks).
for (const TypeId& interface_type_id : interface_type_ids_) {
auto instances_iter = instances_.find(interface_type_id);
if (instances_iter != instances_.end()) {
instances_iter->second.clear();
}
}
interface_type_ids_.clear();
instances_.clear();
}
void Context::StartDeserialization() {
rollback_.clear();
}
void Context::EndDeserialization(Rollback rollback) {
if (rollback == Rollback::kRollback) {
for (const auto& entry : rollback_) {
RemoveInstance(entry.first, entry.second);
}
}
rollback_.clear();
}
void Context::RemoveInstance(TypeId type_id, uint32_t id) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
auto instances_iter = instances_.find(type_id);
if (instances_iter != instances_.end()) {
auto& instance_map = instances_iter->second;
// normalize id to [0, max_id]
if (instance_map.size() > 0 && instance_map.rbegin()->first < id) {
id = id % (instance_map.rbegin()->first + 1);
}
// choose the first valid entry after id
auto instance = instance_map.lower_bound(id);
if (instance == instance_map.end()) {
mojolpmdbg("failed!\n");
return;
}
instance_map.erase(instance);
} else {
mojolpmdbg("failed!\n");
}
}
Context* GetContext() {
static base::NoDestructor<Context> context;
return context.get();
}
bool FromProto(const bool& input, bool& output) {
output = input;
return true;
}
bool ToProto(const bool& input, bool& output) {
output = input;
return true;
}
bool FromProto(const ::google::protobuf::int32& input, int8_t& output) {
output = input;
return true;
}
bool ToProto(const int8_t& input, ::google::protobuf::int32& output) {
output = input;
return true;
}
bool FromProto(const ::google::protobuf::int32& input, int16_t& output) {
output = input;
return true;
}
bool ToProto(const int16_t& input, ::google::protobuf::int32& output) {
output = input;
return true;
}
bool FromProto(const ::google::protobuf::int32& input, int32_t& output) {
output = input;
return true;
}
bool ToProto(const int32_t& input, ::google::protobuf::int32& output) {
output = input;
return true;
}
bool FromProto(const ::google::protobuf::int64& input, int64_t& output) {
output = input;
return true;
}
bool ToProto(const int64_t& input, ::google::protobuf::int64& output) {
output = input;
return true;
}
bool FromProto(const ::google::protobuf::uint32& input, uint8_t& output) {
output = input;
return true;
}
bool ToProto(const uint8_t& input, ::google::protobuf::uint32& output) {
output = input;
return true;
}
bool FromProto(const ::google::protobuf::uint32& input, uint16_t& output) {
output = input;
return true;
}
bool ToProto(const uint16_t& input, ::google::protobuf::uint32& output) {
output = input;
return true;
}
bool FromProto(const ::google::protobuf::uint32& input, uint32_t& output) {
output = input;
return true;
}
bool ToProto(const uint32_t& input, ::google::protobuf::uint32& output) {
output = input;
return true;
}
bool FromProto(const ::google::protobuf::uint64& input, uint64_t& output) {
output = input;
return true;
}
bool ToProto(const uint64_t& input, ::google::protobuf::uint64& output) {
output = input;
return true;
}
bool FromProto(const double& input, double& output) {
output = input;
return true;
}
bool ToProto(const double& input, double& output) {
output = input;
return true;
}
bool FromProto(const float& input, float& output) {
output = input;
return true;
}
bool ToProto(const float& input, float& output) {
output = input;
return true;
}
bool FromProto(const std::string& input, std::string& output) {
output = input;
return true;
}
bool ToProto(const std::string& input, std::string& output) {
output = input;
return true;
}
bool FromProto(const ::mojolpm::Handle& input, mojo::ScopedHandle& output) {
return true;
}
bool ToProto(const mojo::ScopedHandle& input, ::mojolpm::Handle& output) {
return true;
}
bool FromProto(const ::mojolpm::DataPipeConsumerHandle& input,
mojo::ScopedDataPipeConsumerHandle& output) {
bool result = false;
if (input.instance_case() == ::mojolpm::DataPipeConsumerHandle::kOld) {
auto old = mojolpm::GetContext()
->GetAndRemoveInstance<mojo::ScopedDataPipeConsumerHandle>(
input.old());
if (old) {
output = std::move(*old.release());
}
} else {
MojoCreateDataPipeOptions options;
mojo::ScopedDataPipeConsumerHandle consumer;
mojo::ScopedDataPipeProducerHandle producer;
options.struct_size = sizeof(MojoCreateDataPipeOptions);
options.flags = input.new_().flags();
options.element_num_bytes =
std::min(input.new_().element_num_bytes(), kPipeElementMaxSize);
options.capacity_num_bytes =
std::min(input.new_().capacity_num_bytes(), kPipeCapacityMaxSize);
if (MOJO_RESULT_OK == mojo::CreateDataPipe(&options, producer, consumer)) {
result = true;
output = std::move(consumer);
mojolpm::GetContext()->AddInstance(std::move(producer));
}
}
return result;
}
bool ToProto(const mojo::ScopedDataPipeConsumerHandle& input,
::mojolpm::DataPipeConsumerHandle& output) {
return true;
}
bool FromProto(const ::mojolpm::DataPipeProducerHandle& input,
mojo::ScopedDataPipeProducerHandle& output) {
bool result = false;
if (input.instance_case() == ::mojolpm::DataPipeProducerHandle::kOld) {
auto old = mojolpm::GetContext()
->GetAndRemoveInstance<mojo::ScopedDataPipeProducerHandle>(
input.old());
if (old) {
output = std::move(*old.release());
}
} else {
MojoCreateDataPipeOptions options;
mojo::ScopedDataPipeConsumerHandle consumer;
mojo::ScopedDataPipeProducerHandle producer;
options.struct_size = sizeof(MojoCreateDataPipeOptions);
options.flags = input.new_().flags();
options.element_num_bytes =
std::min(input.new_().element_num_bytes(), kPipeElementMaxSize);
options.capacity_num_bytes =
std::min(input.new_().capacity_num_bytes(), kPipeCapacityMaxSize);
if (MOJO_RESULT_OK == mojo::CreateDataPipe(&options, producer, consumer)) {
result = true;
output = std::move(producer);
mojolpm::GetContext()->AddInstance(std::move(consumer));
}
}
return result;
}
bool ToProto(const mojo::ScopedDataPipeProducerHandle& input,
::mojolpm::DataPipeProducerHandle& output) {
return true;
}
bool FromProto(const ::mojolpm::MessagePipeHandle& input,
mojo::ScopedMessagePipeHandle& output) {
return true;
}
bool ToProto(const mojo::ScopedMessagePipeHandle& input,
::mojolpm::MessagePipeHandle& output) {
return true;
}
bool FromProto(const ::mojolpm::SharedBufferHandle& input,
mojo::ScopedSharedBufferHandle& output) {
bool result = false;
if (input.instance_case() == ::mojolpm::SharedBufferHandle::kOld) {
auto old =
mojolpm::GetContext()
->GetAndRemoveInstance<mojo::ScopedSharedBufferHandle>(input.old());
if (old) {
output = std::move(*old.release());
}
} else {
output = mojo::SharedBufferHandle::Create(
std::min(input.new_().num_bytes(), kSharedBufferMaxSize));
result = true;
}
return result;
}
bool ToProto(const mojo::ScopedSharedBufferHandle& input,
::mojolpm::SharedBufferHandle& output) {
return true;
}
bool FromProto(const ::mojolpm::PlatformHandle& input,
mojo::PlatformHandle& output) {
return true;
}
bool ToProto(const mojo::PlatformHandle& input,
::mojolpm::PlatformHandle& output) {
return true;
}
void HandleDataPipeRead(const ::mojolpm::DataPipeRead& input) {
mojo::ScopedDataPipeConsumerHandle* consumer_ptr = nullptr;
if (input.handle().instance_case() ==
::mojolpm::DataPipeConsumerHandle::kOld) {
consumer_ptr =
mojolpm::GetContext()->GetInstance<mojo::ScopedDataPipeConsumerHandle>(
input.handle().old());
} else {
MojoCreateDataPipeOptions options;
mojo::ScopedDataPipeConsumerHandle consumer;
mojo::ScopedDataPipeProducerHandle producer;
options.struct_size = sizeof(MojoCreateDataPipeOptions);
options.flags = input.handle().new_().flags();
options.element_num_bytes = std::min(
input.handle().new_().element_num_bytes(), kPipeElementMaxSize);
options.capacity_num_bytes = std::min(
input.handle().new_().capacity_num_bytes(), kPipeCapacityMaxSize);
if (MOJO_RESULT_OK == mojo::CreateDataPipe(&options, producer, consumer)) {
int id = mojolpm::GetContext()->AddInstance(std::move(consumer));
mojolpm::GetContext()->AddInstance(std::move(producer));
consumer_ptr = mojolpm::GetContext()
->GetInstance<mojo::ScopedDataPipeConsumerHandle>(id);
}
}
if (consumer_ptr) {
size_t size = size_t{input.size()};
if (size > kPipeActionMaxSize) {
size = kPipeActionMaxSize;
}
std::vector<uint8_t> data(size);
size_t bytes_read = 0;
consumer_ptr->get().ReadData(MOJO_READ_DATA_FLAG_NONE, data, bytes_read);
}
}
void HandleDataPipeWrite(const ::mojolpm::DataPipeWrite& input) {
mojo::ScopedDataPipeProducerHandle* producer_ptr = nullptr;
if (input.handle().instance_case() ==
::mojolpm::DataPipeProducerHandle::kOld) {
producer_ptr =
mojolpm::GetContext()->GetInstance<mojo::ScopedDataPipeProducerHandle>(
input.handle().old());
} else {
MojoCreateDataPipeOptions options;
mojo::ScopedDataPipeConsumerHandle consumer;
mojo::ScopedDataPipeProducerHandle producer;
options.struct_size = sizeof(MojoCreateDataPipeOptions);
options.flags = input.handle().new_().flags();
options.element_num_bytes = std::min(
input.handle().new_().element_num_bytes(), kPipeElementMaxSize);
options.capacity_num_bytes = std::min(
input.handle().new_().capacity_num_bytes(), kPipeCapacityMaxSize);
if (MOJO_RESULT_OK == mojo::CreateDataPipe(&options, producer, consumer)) {
mojolpm::GetContext()->AddInstance(std::move(consumer));
int id = mojolpm::GetContext()->AddInstance(std::move(producer));
producer_ptr = mojolpm::GetContext()
->GetInstance<mojo::ScopedDataPipeProducerHandle>(id);
}
}
if (producer_ptr) {
size_t size = input.data().size();
if (size > kPipeActionMaxSize) {
size = kPipeActionMaxSize;
}
size_t bytes_written = 0;
producer_ptr->get().WriteData(base::as_byte_span(input.data()),
MOJO_WRITE_DATA_FLAG_NONE, bytes_written);
}
}
void HandleSharedBufferWrite(const ::mojolpm::SharedBufferWrite& input) {
mojo::ScopedSharedBufferHandle handle;
if (!FromProto(input.handle(), handle)) {
return;
}
size_t size = input.data().size();
if (size > kSharedBufferActionMaxSize) {
size = kSharedBufferActionMaxSize;
}
size = std::min(handle->GetSize(), static_cast<uint64_t>(size));
auto mem = handle->Map(size);
if (!mem) {
return;
}
UNSAFE_TODO(std::memcpy(mem.get(), input.data().data(), size));
}
void HandleDataPipeConsumerClose(
const ::mojolpm::DataPipeConsumerClose& input) {
mojolpm::GetContext()->RemoveInstance<mojo::ScopedDataPipeConsumerHandle>(
input.id());
}
void HandleDataPipeProducerClose(
const ::mojolpm::DataPipeProducerClose& input) {
mojolpm::GetContext()->RemoveInstance<mojo::ScopedDataPipeProducerHandle>(
input.id());
}
void HandleSharedBufferRelease(const ::mojolpm::SharedBufferRelease& input) {
mojolpm::GetContext()->RemoveInstance<mojo::ScopedSharedBufferHandle>(
input.id());
}
} // namespace mojolpm