Commit 092d94af authored by Ken Rockot's avatar Ken Rockot Committed by Commit Bot

[mojo-core] Add fuzzers for port events and user messages

Adds two new fuzzers: one to fuzz deserialization of port events in
general, and one to fuzz user message events specifically, which have
an additional layer of parsing beyond the port event header.

A successfully parsed user message event is ultimately how we carry
application payloads end-to-end across message pipes via the public
message pipe API. With these fuzzers in addition to the Channel and
NodeChannel fuzzers, we have fuzz coverage of every part of the stack
between the OS and the generated bindings.

This CL fixes some low-hanging fruit where we (a) weren't properly
handling certain deserialization failure cases, leading to nullptr
deref; and (b) weren't properly rejecting messages with far too
many handles (ostensibly) attached.

Finally this also ensures that Mojo core is initialized in the
other existing fuzzers, since they may also end up deserializing
handles and thus require the global handle table to be set up.

Bug: 897743
Change-Id: Ie5d5f8025728f6e57b2ce46d3c41532bf134eb45
Reviewed-on: https://chromium-review.googlesource.com/c/1352976
Commit-Queue: Ken Rockot <rockot@google.com>
Reviewed-by: default avatarOliver Chang <ochang@chromium.org>
Cr-Commit-Position: refs/heads/master@{#612043}
parent 60efce52
......@@ -33,9 +33,8 @@ template("core_impl_source_set") {
visibility = [ ":shared_library" ]
} else {
visibility = [
":core_impl_for_fuzzers",
":embedder_internal",
":mojo_core_channel_fuzzer",
":mojo_core_node_channel_fuzzer",
]
}
......@@ -334,12 +333,18 @@ source_set("test_sources") {
]
}
group("core_impl_for_fuzzers") {
public_deps = [
":impl_for_embedder",
]
}
fuzzer_test("mojo_core_channel_fuzzer") {
sources = [
"channel_fuzzer.cc",
]
deps = [
":impl_for_embedder",
":core_impl_for_fuzzers",
"//base",
"//mojo/public/cpp/platform",
]
......@@ -350,8 +355,28 @@ fuzzer_test("mojo_core_node_channel_fuzzer") {
"node_channel_fuzzer.cc",
]
deps = [
":impl_for_embedder",
":core_impl_for_fuzzers",
"//base",
"//mojo/public/cpp/platform",
]
}
fuzzer_test("mojo_core_port_event_fuzzer") {
sources = [
"port_event_fuzzer.cc",
]
deps = [
":core_impl_for_fuzzers",
"//base",
]
}
fuzzer_test("mojo_core_user_message_fuzzer") {
sources = [
"user_message_fuzzer.cc",
]
deps = [
":core_impl_for_fuzzers",
"//base",
]
}
......@@ -11,6 +11,7 @@
#include "build/build_config.h"
#include "mojo/core/channel.h"
#include "mojo/core/connection_params.h"
#include "mojo/core/entrypoints.h"
#include "mojo/public/cpp/platform/platform_channel.h"
using namespace mojo::core;
......@@ -30,9 +31,16 @@ class FakeChannelDelegate : public Channel::Delegate {
void OnChannelError(Channel::Error error) override {}
};
// Message deserialization may register handles in the global handle table. We
// need to initialize Core for that to be OK.
struct Environment {
Environment() : message_loop(base::MessageLoop::TYPE_IO) { InitializeCore(); }
base::MessageLoop message_loop;
};
extern "C" int LLVMFuzzerTestOneInput(const unsigned char* data, size_t size) {
static base::NoDestructor<base::MessageLoop> message_loop(
base::MessageLoop::TYPE_IO);
static base::NoDestructor<Environment> environment;
// Platform-specific implementation of an OS IPC primitive that is normally
// used to carry messages between processes.
......@@ -41,13 +49,13 @@ extern "C" int LLVMFuzzerTestOneInput(const unsigned char* data, size_t size) {
FakeChannelDelegate receiver_delegate;
auto receiver = Channel::Create(&receiver_delegate,
ConnectionParams(channel.TakeLocalEndpoint()),
message_loop->task_runner());
environment->message_loop.task_runner());
receiver->Start();
FakeChannelDelegate sender_delegate;
auto sender = Channel::Create(&sender_delegate,
ConnectionParams(channel.TakeRemoteEndpoint()),
message_loop->task_runner());
environment->message_loop.task_runner());
sender->Start();
sender->Write(
......
......@@ -11,6 +11,7 @@
#include "build/build_config.h"
#include "mojo/core/channel.h"
#include "mojo/core/connection_params.h"
#include "mojo/core/entrypoints.h"
#include "mojo/core/node_channel.h" // nogncheck
#include "mojo/public/cpp/platform/platform_channel.h"
......@@ -83,9 +84,16 @@ class FakeChannelDelegate : public Channel::Delegate {
void OnChannelError(Channel::Error error) override {}
};
// Message deserialization may register handles in the global handle table. We
// need to initialize Core for that to be OK.
struct Environment {
Environment() : message_loop(base::MessageLoop::TYPE_IO) { InitializeCore(); }
base::MessageLoop message_loop;
};
extern "C" int LLVMFuzzerTestOneInput(const unsigned char* data, size_t size) {
static base::NoDestructor<base::MessageLoop> message_loop(
base::MessageLoop::TYPE_IO);
static base::NoDestructor<Environment> environment;
// Platform-specific implementation of an OS IPC primitive that is normally
// used to carry messages between processes.
......@@ -94,7 +102,7 @@ extern "C" int LLVMFuzzerTestOneInput(const unsigned char* data, size_t size) {
FakeNodeChannelDelegate receiver_delegate;
auto receiver = NodeChannel::Create(
&receiver_delegate, ConnectionParams(channel.TakeLocalEndpoint()),
message_loop->task_runner(), base::DoNothing());
environment->message_loop.task_runner(), base::DoNothing());
receiver->Start();
// We only use a Channel for the sender side, since it allows us to easily
......@@ -105,7 +113,7 @@ extern "C" int LLVMFuzzerTestOneInput(const unsigned char* data, size_t size) {
FakeChannelDelegate sender_delegate;
auto sender = Channel::Create(&sender_delegate,
ConnectionParams(channel.TakeRemoteEndpoint()),
message_loop->task_runner());
environment->message_loop.task_runner());
sender->Start();
auto message = std::make_unique<Channel::Message>(size, 0 /* num_handles */);
std::copy(data, data + size,
......
......@@ -101,8 +101,10 @@ ports::ScopedEvent DeserializeEventMessage(
auto message = UserMessageImpl::CreateFromChannelMessage(
message_event.get(), std::move(channel_message),
static_cast<uint8_t*>(data) + event_size, size - event_size);
message->set_source_node(from_node);
if (!message)
return nullptr;
message->set_source_node(from_node);
message_event->AttachMessage(std::move(message));
return std::move(message_event);
}
......@@ -318,6 +320,22 @@ void NodeController::NotifyBadMessageFrom(const ports::NodeName& source_node,
peer->NotifyBadMessage(error);
}
// static
void NodeController::DeserializeRawBytesAsEventForFuzzer(
base::span<const unsigned char> data) {
void* payload;
auto message = NodeChannel::CreateEventMessage(0, data.size(), &payload, 0);
DCHECK(message);
std::copy(data.begin(), data.end(), static_cast<unsigned char*>(payload));
DeserializeEventMessage(ports::NodeName(), std::move(message));
}
// static
void NodeController::DeserializeMessageAsEventForFuzzer(
Channel::MessagePtr message) {
DeserializeEventMessage(ports::NodeName(), std::move(message));
}
void NodeController::SendBrokerClientInvitationOnIOThread(
ScopedProcessHandle target_process,
ConnectionParams connection_params,
......
......@@ -16,6 +16,7 @@
#include "base/callback.h"
#include "base/containers/hash_tables.h"
#include "base/containers/queue.h"
#include "base/containers/span.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/memory/writable_shared_memory_region.h"
......@@ -130,6 +131,10 @@ class MOJO_SYSTEM_IMPL_EXPORT NodeController : public ports::NodeDelegate,
void NotifyBadMessageFrom(const ports::NodeName& source_node,
const std::string& error);
static void DeserializeRawBytesAsEventForFuzzer(
base::span<const unsigned char> data);
static void DeserializeMessageAsEventForFuzzer(Channel::MessagePtr message);
private:
friend Core;
......
// 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/containers/span.h"
#include "base/no_destructor.h"
#include "mojo/core/entrypoints.h"
#include "mojo/core/node_controller.h"
using namespace mojo::core;
// Message deserialization may register handles in the global handle table. We
// need to initialize Core for that to be OK.
struct Environment {
Environment() { InitializeCore(); }
};
extern "C" int LLVMFuzzerTestOneInput(const unsigned char* data, size_t size) {
static base::NoDestructor<Environment> environment;
// Try using the fuzz as the full contents of a port event.
NodeController::DeserializeRawBytesAsEventForFuzzer(
base::make_span(data, size));
return 0;
}
// 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/containers/span.h"
#include "base/no_destructor.h"
#include "mojo/core/entrypoints.h"
#include "mojo/core/node_controller.h"
#include "mojo/core/user_message_impl.h"
using namespace mojo::core;
// Message deserialization may register handles in the global handle table. We
// need to initialize Core for that to be OK.
struct Environment {
Environment() { InitializeCore(); }
};
extern "C" int LLVMFuzzerTestOneInput(const unsigned char* data, size_t size) {
static base::NoDestructor<Environment> environment;
// Try using our fuzz input as the payload of an otherwise well-formed user
// message event.
std::unique_ptr<ports::UserMessageEvent> event;
MojoResult result = UserMessageImpl::CreateEventForNewSerializedMessage(
static_cast<uint32_t>(size), nullptr, 0, &event);
DCHECK_EQ(result, MOJO_RESULT_OK);
DCHECK(event);
auto* message = event->GetMessage<UserMessageImpl>();
std::copy(data, data + size,
static_cast<unsigned char*>(message->user_payload()));
Channel::MessagePtr serialized_event =
UserMessageImpl::FinalizeEventMessage(std::move(event));
NodeController::DeserializeMessageAsEventForFuzzer(
std::move(serialized_event));
return 0;
}
......@@ -35,6 +35,11 @@ namespace {
// incur any reallocations as they're expanded to full size.
const uint32_t kMinimumPayloadBufferSize = 128;
// The maximum number of Mojo handles which can be attached to a serialized
// user message. Much larger than should ever be necessary, but small enough
// to not be a problem.
const uint32_t kMaxMojoHandleAttachments = 1024 * 1024;
// Indicates whether handle serialization failure should be emulated in testing.
bool g_always_fail_handle_serialization = false;
......@@ -383,6 +388,9 @@ std::unique_ptr<UserMessageImpl> UserMessageImpl::CreateFromChannelMessage(
if (header_size > payload_size)
return nullptr;
if (header->num_dispatchers > kMaxMojoHandleAttachments)
return nullptr;
void* user_payload = static_cast<uint8_t*>(payload) + header_size;
const size_t user_payload_size = payload_size - header_size;
return base::WrapUnique(
......
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