blob: e45e6bbe5c28501d2cfc04e295da73cf01da8771 [file] [log] [blame]
// Copyright 2019 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 <list>
#include "base/logging.h"
#include "base/mac/mach_logging.h"
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
#include "base/test/scoped_feature_list.h"
#include "mojo/core/channel.h"
#include "mojo/core/entrypoints.h"
#include "mojo/core/test/data/channel_mac/channel_mac.pb.h"
#include "mojo/public/cpp/platform/features.h"
#include "mojo/public/cpp/platform/platform_channel.h"
#include "testing/libfuzzer/fuzzers/mach/mach_message_converter.h"
#include "testing/libfuzzer/proto/lpm_interface.h"
namespace {
class ChannelMacFuzzer {
public:
ChannelMacFuzzer() {
feature_list_.InitAndEnableFeature(mojo::features::kMojoChannelMac);
mojo::core::InitializeCore();
logging::SetMinLogLevel(logging::LOG_FATAL);
}
scoped_refptr<base::TaskRunner> io_task_runner() {
return message_loop_.task_runner();
}
private:
base::test::ScopedFeatureList feature_list_;
base::MessageLoopForIO message_loop_;
};
class FakeChannelDelegate : public mojo::core::Channel::Delegate {
public:
FakeChannelDelegate() = default;
~FakeChannelDelegate() override = default;
void OnChannelMessage(const void* payload,
size_t payload_size,
std::vector<mojo::PlatformHandle> handles) override {}
void OnChannelError(mojo::core::Channel::Error error) override {}
};
} // namespace
DEFINE_BINARY_PROTO_FUZZER(mojo_fuzzer::ChannelMac proto) {
static ChannelMacFuzzer environment;
mojo::PlatformChannel platform_channel;
mojo::PlatformChannelEndpoint fuzzed_endpoint;
mojo::PlatformChannelEndpoint other_endpoint;
mach_port_t send_port = platform_channel.local_endpoint()
.platform_handle()
.GetMachSendRight()
.get();
if (proto.endpoint_type() == mojo_fuzzer::ChannelMac_EndpointType_LOCAL) {
fuzzed_endpoint = platform_channel.TakeLocalEndpoint();
other_endpoint = platform_channel.TakeRemoteEndpoint();
} else if (proto.endpoint_type() ==
mojo_fuzzer::ChannelMac_EndpointType_REMOTE) {
fuzzed_endpoint = platform_channel.TakeRemoteEndpoint();
other_endpoint = platform_channel.TakeLocalEndpoint();
}
FakeChannelDelegate delegate;
auto channel = mojo::core::Channel::Create(
&delegate, mojo::core::ConnectionParams(std::move(fuzzed_endpoint)),
mojo::core::Channel::HandlePolicy::kAcceptHandles,
environment.io_task_runner());
channel->Start();
// If the handshake is not being fuzzed, establish a peer Channel that will
// put |channel| into a good state by performing the handshake.
scoped_refptr<mojo::core::Channel> peer_channel;
if (!proto.fuzz_handshake()) {
peer_channel = mojo::core::Channel::Create(
&delegate, mojo::core::ConnectionParams(std::move(other_endpoint)),
mojo::core::Channel::HandlePolicy::kAcceptHandles,
environment.io_task_runner());
peer_channel->Start();
}
base::RunLoop().RunUntilIdle();
// Save off any ports that were sent as part of a message until after the
// channel has been shut down.
std::list<mach_fuzzer::SendablePort> ports_to_destroy;
for (auto& message : *proto.mutable_messages()) {
if (message.HasExtension(mojo_fuzzer::MojoMessage::mojo_message)) {
// Mojo message data for inline Mach messages is
// [data_length_uint64][data_bytes].
const auto& mojo_message =
message.GetExtension(mojo_fuzzer::MojoMessage::mojo_message);
// If the fuzz data do not specify an explicit length, just use the byte
// length.
uint64_t data_length = mojo_message.has_data_length()
? mojo_message.data_length()
: mojo_message.data().size();
std::string data;
data.append(reinterpret_cast<const char*>(&data_length),
sizeof(data_length));
data.append(mojo_message.data().begin(), mojo_message.data().end());
message.set_data(data);
}
mach_fuzzer::SendResult result = SendMessage(send_port, message);
std::move(result.message.ports.begin(), result.message.ports.end(),
std::back_inserter(ports_to_destroy));
}
base::RunLoop().RunUntilIdle();
channel->ShutDown();
channel.reset();
if (peer_channel) {
peer_channel->ShutDown();
peer_channel.reset();
}
base::RunLoop().RunUntilIdle();
}