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") { ...@@ -33,9 +33,8 @@ template("core_impl_source_set") {
visibility = [ ":shared_library" ] visibility = [ ":shared_library" ]
} else { } else {
visibility = [ visibility = [
":core_impl_for_fuzzers",
":embedder_internal", ":embedder_internal",
":mojo_core_channel_fuzzer",
":mojo_core_node_channel_fuzzer",
] ]
} }
...@@ -334,12 +333,18 @@ source_set("test_sources") { ...@@ -334,12 +333,18 @@ source_set("test_sources") {
] ]
} }
group("core_impl_for_fuzzers") {
public_deps = [
":impl_for_embedder",
]
}
fuzzer_test("mojo_core_channel_fuzzer") { fuzzer_test("mojo_core_channel_fuzzer") {
sources = [ sources = [
"channel_fuzzer.cc", "channel_fuzzer.cc",
] ]
deps = [ deps = [
":impl_for_embedder", ":core_impl_for_fuzzers",
"//base", "//base",
"//mojo/public/cpp/platform", "//mojo/public/cpp/platform",
] ]
...@@ -350,8 +355,28 @@ fuzzer_test("mojo_core_node_channel_fuzzer") { ...@@ -350,8 +355,28 @@ fuzzer_test("mojo_core_node_channel_fuzzer") {
"node_channel_fuzzer.cc", "node_channel_fuzzer.cc",
] ]
deps = [ deps = [
":impl_for_embedder", ":core_impl_for_fuzzers",
"//base", "//base",
"//mojo/public/cpp/platform", "//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 @@ ...@@ -11,6 +11,7 @@
#include "build/build_config.h" #include "build/build_config.h"
#include "mojo/core/channel.h" #include "mojo/core/channel.h"
#include "mojo/core/connection_params.h" #include "mojo/core/connection_params.h"
#include "mojo/core/entrypoints.h"
#include "mojo/public/cpp/platform/platform_channel.h" #include "mojo/public/cpp/platform/platform_channel.h"
using namespace mojo::core; using namespace mojo::core;
...@@ -30,9 +31,16 @@ class FakeChannelDelegate : public Channel::Delegate { ...@@ -30,9 +31,16 @@ class FakeChannelDelegate : public Channel::Delegate {
void OnChannelError(Channel::Error error) override {} 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) { extern "C" int LLVMFuzzerTestOneInput(const unsigned char* data, size_t size) {
static base::NoDestructor<base::MessageLoop> message_loop( static base::NoDestructor<Environment> environment;
base::MessageLoop::TYPE_IO);
// Platform-specific implementation of an OS IPC primitive that is normally // Platform-specific implementation of an OS IPC primitive that is normally
// used to carry messages between processes. // used to carry messages between processes.
...@@ -41,13 +49,13 @@ extern "C" int LLVMFuzzerTestOneInput(const unsigned char* data, size_t size) { ...@@ -41,13 +49,13 @@ extern "C" int LLVMFuzzerTestOneInput(const unsigned char* data, size_t size) {
FakeChannelDelegate receiver_delegate; FakeChannelDelegate receiver_delegate;
auto receiver = Channel::Create(&receiver_delegate, auto receiver = Channel::Create(&receiver_delegate,
ConnectionParams(channel.TakeLocalEndpoint()), ConnectionParams(channel.TakeLocalEndpoint()),
message_loop->task_runner()); environment->message_loop.task_runner());
receiver->Start(); receiver->Start();
FakeChannelDelegate sender_delegate; FakeChannelDelegate sender_delegate;
auto sender = Channel::Create(&sender_delegate, auto sender = Channel::Create(&sender_delegate,
ConnectionParams(channel.TakeRemoteEndpoint()), ConnectionParams(channel.TakeRemoteEndpoint()),
message_loop->task_runner()); environment->message_loop.task_runner());
sender->Start(); sender->Start();
sender->Write( sender->Write(
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include "build/build_config.h" #include "build/build_config.h"
#include "mojo/core/channel.h" #include "mojo/core/channel.h"
#include "mojo/core/connection_params.h" #include "mojo/core/connection_params.h"
#include "mojo/core/entrypoints.h"
#include "mojo/core/node_channel.h" // nogncheck #include "mojo/core/node_channel.h" // nogncheck
#include "mojo/public/cpp/platform/platform_channel.h" #include "mojo/public/cpp/platform/platform_channel.h"
...@@ -83,9 +84,16 @@ class FakeChannelDelegate : public Channel::Delegate { ...@@ -83,9 +84,16 @@ class FakeChannelDelegate : public Channel::Delegate {
void OnChannelError(Channel::Error error) override {} 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) { extern "C" int LLVMFuzzerTestOneInput(const unsigned char* data, size_t size) {
static base::NoDestructor<base::MessageLoop> message_loop( static base::NoDestructor<Environment> environment;
base::MessageLoop::TYPE_IO);
// Platform-specific implementation of an OS IPC primitive that is normally // Platform-specific implementation of an OS IPC primitive that is normally
// used to carry messages between processes. // used to carry messages between processes.
...@@ -94,7 +102,7 @@ extern "C" int LLVMFuzzerTestOneInput(const unsigned char* data, size_t size) { ...@@ -94,7 +102,7 @@ extern "C" int LLVMFuzzerTestOneInput(const unsigned char* data, size_t size) {
FakeNodeChannelDelegate receiver_delegate; FakeNodeChannelDelegate receiver_delegate;
auto receiver = NodeChannel::Create( auto receiver = NodeChannel::Create(
&receiver_delegate, ConnectionParams(channel.TakeLocalEndpoint()), &receiver_delegate, ConnectionParams(channel.TakeLocalEndpoint()),
message_loop->task_runner(), base::DoNothing()); environment->message_loop.task_runner(), base::DoNothing());
receiver->Start(); receiver->Start();
// We only use a Channel for the sender side, since it allows us to easily // 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) { ...@@ -105,7 +113,7 @@ extern "C" int LLVMFuzzerTestOneInput(const unsigned char* data, size_t size) {
FakeChannelDelegate sender_delegate; FakeChannelDelegate sender_delegate;
auto sender = Channel::Create(&sender_delegate, auto sender = Channel::Create(&sender_delegate,
ConnectionParams(channel.TakeRemoteEndpoint()), ConnectionParams(channel.TakeRemoteEndpoint()),
message_loop->task_runner()); environment->message_loop.task_runner());
sender->Start(); sender->Start();
auto message = std::make_unique<Channel::Message>(size, 0 /* num_handles */); auto message = std::make_unique<Channel::Message>(size, 0 /* num_handles */);
std::copy(data, data + size, std::copy(data, data + size,
......
...@@ -101,8 +101,10 @@ ports::ScopedEvent DeserializeEventMessage( ...@@ -101,8 +101,10 @@ ports::ScopedEvent DeserializeEventMessage(
auto message = UserMessageImpl::CreateFromChannelMessage( auto message = UserMessageImpl::CreateFromChannelMessage(
message_event.get(), std::move(channel_message), message_event.get(), std::move(channel_message),
static_cast<uint8_t*>(data) + event_size, size - event_size); 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)); message_event->AttachMessage(std::move(message));
return std::move(message_event); return std::move(message_event);
} }
...@@ -318,6 +320,22 @@ void NodeController::NotifyBadMessageFrom(const ports::NodeName& source_node, ...@@ -318,6 +320,22 @@ void NodeController::NotifyBadMessageFrom(const ports::NodeName& source_node,
peer->NotifyBadMessage(error); 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( void NodeController::SendBrokerClientInvitationOnIOThread(
ScopedProcessHandle target_process, ScopedProcessHandle target_process,
ConnectionParams connection_params, ConnectionParams connection_params,
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
#include "base/callback.h" #include "base/callback.h"
#include "base/containers/hash_tables.h" #include "base/containers/hash_tables.h"
#include "base/containers/queue.h" #include "base/containers/queue.h"
#include "base/containers/span.h"
#include "base/macros.h" #include "base/macros.h"
#include "base/memory/ref_counted.h" #include "base/memory/ref_counted.h"
#include "base/memory/writable_shared_memory_region.h" #include "base/memory/writable_shared_memory_region.h"
...@@ -130,6 +131,10 @@ class MOJO_SYSTEM_IMPL_EXPORT NodeController : public ports::NodeDelegate, ...@@ -130,6 +131,10 @@ class MOJO_SYSTEM_IMPL_EXPORT NodeController : public ports::NodeDelegate,
void NotifyBadMessageFrom(const ports::NodeName& source_node, void NotifyBadMessageFrom(const ports::NodeName& source_node,
const std::string& error); const std::string& error);
static void DeserializeRawBytesAsEventForFuzzer(
base::span<const unsigned char> data);
static void DeserializeMessageAsEventForFuzzer(Channel::MessagePtr message);
private: private:
friend Core; 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 { ...@@ -35,6 +35,11 @@ namespace {
// incur any reallocations as they're expanded to full size. // incur any reallocations as they're expanded to full size.
const uint32_t kMinimumPayloadBufferSize = 128; 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. // Indicates whether handle serialization failure should be emulated in testing.
bool g_always_fail_handle_serialization = false; bool g_always_fail_handle_serialization = false;
...@@ -383,6 +388,9 @@ std::unique_ptr<UserMessageImpl> UserMessageImpl::CreateFromChannelMessage( ...@@ -383,6 +388,9 @@ std::unique_ptr<UserMessageImpl> UserMessageImpl::CreateFromChannelMessage(
if (header_size > payload_size) if (header_size > payload_size)
return nullptr; return nullptr;
if (header->num_dispatchers > kMaxMojoHandleAttachments)
return nullptr;
void* user_payload = static_cast<uint8_t*>(payload) + header_size; void* user_payload = static_cast<uint8_t*>(payload) + header_size;
const size_t user_payload_size = payload_size - header_size; const size_t user_payload_size = payload_size - header_size;
return base::WrapUnique( 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