Commit 24170150 authored by Ken Rockot's avatar Ken Rockot Committed by Commit Bot

[mojo-core] Add fuzzers for Channel & NodeChannel

Adds fuzzers for Channel and NodeChannel, the two main primitives
over which Mojo transmits all IPC messages. Both objects define and
parse different layers of the internal control protocol.

The Channel fuzzer just fires random data at a platform-specific
Channel implementation, covering the lowest protocol layer which
parses a Channel::Message header.

The NodeChannel fuzzer sends well-formed Channel::Message header
data with a fuzzed payload to a NodeChannel receiver, which covers
parsing the rest of the internal control protocol.

Bug: 900440
Change-Id: I9fe9b88fc3bced17427db28daad931fb64a44280
Reviewed-on: https://chromium-review.googlesource.com/c/1352828
Commit-Queue: Ken Rockot <rockot@google.com>
Reviewed-by: default avatarOliver Chang <ochang@chromium.org>
Cr-Commit-Position: refs/heads/master@{#611583}
parent 5acc3f10
......@@ -3,6 +3,7 @@
# found in the LICENSE file.
import("//build/config/nacl/config.gni")
import("//testing/libfuzzer/fuzzer_test.gni")
import("//testing/test.gni")
component("embedder_internal") {
......@@ -31,7 +32,11 @@ template("core_impl_source_set") {
if (invoker.for_shared_library) {
visibility = [ ":shared_library" ]
} else {
visibility = [ ":embedder_internal" ]
visibility = [
":embedder_internal",
":mojo_core_channel_fuzzer",
":mojo_core_node_channel_fuzzer",
]
}
public = [
......@@ -328,3 +333,25 @@ source_set("test_sources") {
"//testing/gtest",
]
}
fuzzer_test("mojo_core_channel_fuzzer") {
sources = [
"channel_fuzzer.cc",
]
deps = [
":impl_for_embedder",
"//base",
"//mojo/public/cpp/platform",
]
}
fuzzer_test("mojo_core_node_channel_fuzzer") {
sources = [
"node_channel_fuzzer.cc",
]
deps = [
":impl_for_embedder",
"//base",
"//mojo/public/cpp/platform",
]
}
......@@ -13,6 +13,7 @@
#include "base/macros.h"
#include "base/memory/aligned_memory.h"
#include "base/memory/ptr_util.h"
#include "base/numerics/safe_math.h"
#include "base/process/process_handle.h"
#include "build/build_config.h"
......@@ -58,6 +59,8 @@ const size_t kMaxUnusedReadBufferCapacity = 4096;
// Fuchsia: The zx_channel_write() API supports up to 64 handles.
const size_t kMaxAttachedHandles = 64;
Channel::Message::Message() = default;
Channel::Message::Message(size_t payload_size, size_t max_handles)
: Message(payload_size, payload_size, max_handles) {}
......@@ -161,6 +164,19 @@ Channel::Message::~Message() {
base::AlignedFree(data_);
}
// static
Channel::MessagePtr Channel::Message::CreateRawForFuzzing(
base::span<const unsigned char> data) {
auto message = base::WrapUnique(new Message);
message->size_ = data.size();
if (data.size()) {
message->data_ = static_cast<char*>(
base::AlignedAlloc(data.size(), kChannelMessageAlignment));
std::copy(data.begin(), data.end(), message->data_);
}
return message;
}
// static
Channel::MessagePtr Channel::Message::Deserialize(
const void* data,
......
......@@ -7,6 +7,7 @@
#include <vector>
#include "base/containers/span.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/process/process_handle.h"
......@@ -143,6 +144,8 @@ class MOJO_SYSTEM_IMPL_EXPORT Channel
MessageType message_type);
~Message();
static MessagePtr CreateRawForFuzzing(base::span<const unsigned char> data);
// Constructs a Message from serialized message data, optionally coming from
// a known remote process.
static MessagePtr Deserialize(
......@@ -197,6 +200,8 @@ class MOJO_SYSTEM_IMPL_EXPORT Channel
void SetVersionForTest(uint16_t version_number);
private:
Message();
// The message data buffer.
char* data_ = nullptr;
......
// Copyright 2018 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 <stdint.h>
#include "base/bind_helpers.h"
#include "base/message_loop/message_loop.h"
#include "base/no_destructor.h"
#include "base/run_loop.h"
#include "build/build_config.h"
#include "mojo/core/channel.h"
#include "mojo/core/connection_params.h"
#include "mojo/public/cpp/platform/platform_channel.h"
using namespace mojo::core;
// A fake delegate for each Channel endpoint. By the time an incoming message
// reaches a Delegate, all interesting message parsing at the lowest protocol
// layer has already been done by the receiving Channel implementation, so this
// doesn't need to do any work.
class FakeChannelDelegate : public 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(Channel::Error error) override {}
};
extern "C" int LLVMFuzzerTestOneInput(const unsigned char* data, size_t size) {
static base::NoDestructor<base::MessageLoop> message_loop(
base::MessageLoop::TYPE_IO);
// Platform-specific implementation of an OS IPC primitive that is normally
// used to carry messages between processes.
mojo::PlatformChannel channel;
FakeChannelDelegate receiver_delegate;
auto receiver = Channel::Create(&receiver_delegate,
ConnectionParams(channel.TakeLocalEndpoint()),
message_loop->task_runner());
receiver->Start();
FakeChannelDelegate sender_delegate;
auto sender = Channel::Create(&sender_delegate,
ConnectionParams(channel.TakeRemoteEndpoint()),
message_loop->task_runner());
sender->Start();
sender->Write(
Channel::Message::CreateRawForFuzzing(base::make_span(data, size)));
// Make sure |receiver| does whatever work it's gonna do in response to our
// message. By the time the loop goes idle, all parsing will be done.
base::RunLoop().RunUntilIdle();
// Clean up our channels so we don't leak their underlying OS primitives.
sender->ShutDown();
sender.reset();
receiver->ShutDown();
receiver.reset();
base::RunLoop().RunUntilIdle();
return 0;
}
......@@ -48,7 +48,7 @@ class MessageView {
: message_(std::move(message)),
offset_(offset),
handles_(message_->TakeHandlesForTransport()) {
DCHECK_GT(message_->data_num_bytes(), offset_);
DCHECK(!message_->data_num_bytes() || message_->data_num_bytes() > offset_);
}
MessageView(MessageView&& other) { *this = std::move(other); }
......@@ -70,8 +70,10 @@ class MessageView {
size_t data_offset() const { return offset_; }
void advance_data_offset(size_t num_bytes) {
DCHECK_GT(message_->data_num_bytes(), offset_ + num_bytes);
offset_ += num_bytes;
if (num_bytes) {
DCHECK_GT(message_->data_num_bytes(), offset_ + num_bytes);
offset_ += num_bytes;
}
}
std::vector<PlatformHandleInTransit> TakeHandles() {
......
// Copyright 2018 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 <stdint.h>
#include "base/bind_helpers.h"
#include "base/message_loop/message_loop.h"
#include "base/no_destructor.h"
#include "base/run_loop.h"
#include "build/build_config.h"
#include "mojo/core/channel.h"
#include "mojo/core/connection_params.h"
#include "mojo/core/node_channel.h" // nogncheck
#include "mojo/public/cpp/platform/platform_channel.h"
using namespace mojo::core;
// Implementation of NodeChannel::Delegate which does nothing. All of the
// interesting NodeChannel control message message parsing is done by
// NodeChannel by the time any of the delegate methods are invoked, so there's
// no need for this to do any work.
class FakeNodeChannelDelegate : public NodeChannel::Delegate {
public:
FakeNodeChannelDelegate() = default;
~FakeNodeChannelDelegate() override = default;
void OnAcceptInvitee(const ports::NodeName& from_node,
const ports::NodeName& inviter_name,
const ports::NodeName& token) override {}
void OnAcceptInvitation(const ports::NodeName& from_node,
const ports::NodeName& token,
const ports::NodeName& invitee_name) override {}
void OnAddBrokerClient(const ports::NodeName& from_node,
const ports::NodeName& client_name,
base::ProcessHandle process_handle) override {}
void OnBrokerClientAdded(const ports::NodeName& from_node,
const ports::NodeName& client_name,
mojo::PlatformHandle broker_channel) override {}
void OnAcceptBrokerClient(const ports::NodeName& from_node,
const ports::NodeName& broker_name,
mojo::PlatformHandle broker_channel) override {}
void OnEventMessage(const ports::NodeName& from_node,
Channel::MessagePtr message) override {}
void OnRequestPortMerge(const ports::NodeName& from_node,
const ports::PortName& connector_port_name,
const std::string& token) override {}
void OnRequestIntroduction(const ports::NodeName& from_node,
const ports::NodeName& name) override {}
void OnIntroduce(const ports::NodeName& from_node,
const ports::NodeName& name,
mojo::PlatformHandle channel_handle) override {}
void OnBroadcast(const ports::NodeName& from_node,
Channel::MessagePtr message) override {}
#if defined(OS_WIN) || (defined(OS_MACOSX) && !defined(OS_IOS))
void OnRelayEventMessage(const ports::NodeName& from_node,
base::ProcessHandle from_process,
const ports::NodeName& destination,
Channel::MessagePtr message) override {}
void OnEventMessageFromRelay(const ports::NodeName& from_node,
const ports::NodeName& source_node,
Channel::MessagePtr message) override {}
#endif
void OnAcceptPeer(const ports::NodeName& from_node,
const ports::NodeName& token,
const ports::NodeName& peer_name,
const ports::PortName& port_name) override {}
void OnChannelError(const ports::NodeName& node,
NodeChannel* channel) override {}
};
// A fake delegate for the sending Channel endpoint. The sending Channel is not
// being fuzzed and won't receive any interesting messages, so this doesn't need
// to do anything.
class FakeChannelDelegate : public 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(Channel::Error error) override {}
};
extern "C" int LLVMFuzzerTestOneInput(const unsigned char* data, size_t size) {
static base::NoDestructor<base::MessageLoop> message_loop(
base::MessageLoop::TYPE_IO);
// Platform-specific implementation of an OS IPC primitive that is normally
// used to carry messages between processes.
mojo::PlatformChannel channel;
FakeNodeChannelDelegate receiver_delegate;
auto receiver = NodeChannel::Create(
&receiver_delegate, ConnectionParams(channel.TakeLocalEndpoint()),
message_loop->task_runner(), base::DoNothing());
receiver->Start();
// We only use a Channel for the sender side, since it allows us to easily
// encode and transmit raw messages. For this fuzzer, we allocate a valid
// Channel Message with a valid header, but fill its payload contents with
// fuzz. Such messages will always reach the receiving NodeChannel to be
// parsed further.
FakeChannelDelegate sender_delegate;
auto sender = Channel::Create(&sender_delegate,
ConnectionParams(channel.TakeRemoteEndpoint()),
message_loop->task_runner());
sender->Start();
auto message = std::make_unique<Channel::Message>(size, 0 /* num_handles */);
std::copy(data, data + size,
static_cast<unsigned char*>(message->mutable_payload()));
sender->Write(std::move(message));
// Make sure |receiver| does whatever work it's gonna do in response to our
// message. By the time the loop goes idle, all parsing will be done.
base::RunLoop().RunUntilIdle();
// Clean up our channels so we don't leak the underlying OS primitives.
sender->ShutDown();
sender.reset();
receiver->ShutDown();
receiver.reset();
base::RunLoop().RunUntilIdle();
return 0;
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment