Commit 1798aeb5 authored by Oliver Chang's avatar Oliver Chang Committed by Commit Bot

Implement Mojo message interception and dumping for fuzzing.

When --ipc-dump-directory=DIR is passed, outgoing Mojo messages from
the renderer are written to:

DIR/INTERFACE_NAME/CONNECTOR_ID/NUM.METHOD_NAME.mojomsg

Where INTERFACE_NAME is the interface name of the message,
CONNECTOR_ID is a random number unique to each instance of a Connector,
METHOD_NAME is the the name of the method being called, and
NUM is a number used for ordering messages for a single connector.

Bug: 607649

Change-Id: I5b8735d3ca94707c6033563cb8dcc21e0f815ac3
Reviewed-on: https://chromium-review.googlesource.com/1070550Reviewed-by: default avatarMartin Barbella <mbarbella@chromium.org>
Reviewed-by: default avatarJohn Abd-El-Malek <jam@chromium.org>
Reviewed-by: default avatarKen Rockot <rockot@chromium.org>
Commit-Queue: Oliver Chang <ochang@chromium.org>
Cr-Commit-Position: refs/heads/master@{#569945}
parent 308abdfe
......@@ -194,6 +194,7 @@
#if defined(ENABLE_IPC_FUZZER)
#include "content/common/external_ipc_dumper.h"
#include "mojo/public/cpp/bindings/message_dumper.h"
#endif
#if defined(OS_MACOSX)
......@@ -880,6 +881,7 @@ void RenderThreadImpl::Init(
IPC::ChannelProxy::OutgoingMessageFilter* filter =
LoadExternalIPCDumper(dump_directory);
GetChannel()->set_outgoing_message_filter(filter);
mojo::MessageDumper::SetMessageDumpDirectory(dump_directory);
}
#endif
......
......@@ -3,6 +3,8 @@
# found in the LICENSE file.
import("//build/buildflag_header.gni")
import("//tools/ipc_fuzzer/ipc_fuzzer.gni")
declare_args() {
enable_mojo_tracing = false
}
......@@ -91,6 +93,10 @@ component("bindings_base") {
"//base",
"//mojo/public/cpp/system",
]
if (enable_ipc_fuzzer) {
all_dependent_configs = [ "//tools/ipc_fuzzer:ipc_fuzzer_config" ]
}
}
component("bindings") {
......@@ -161,6 +167,13 @@ component("bindings") {
"unique_ptr_impl_ref_traits.h",
]
if (enable_ipc_fuzzer && current_toolchain == host_toolchain) {
sources += [
"lib/message_dumper.cc",
"message_dumper.h",
]
}
public_deps = [
":bindings_base",
":struct_traits",
......
......@@ -271,6 +271,10 @@ class MOJO_CPP_BINDINGS_EXPORT Connector : public MessageReceiver {
// nested dispatch operations.
bool is_dispatching_ = false;
#if defined(ENABLE_IPC_FUZZER)
std::unique_ptr<MessageReceiver> message_dumper_;
#endif
// Create a single weak ptr and use it everywhere, to avoid the malloc/free
// cost of creating a new weak ptr whenever it is needed.
// NOTE: This weak pointer is invalidated when the message pipe is closed or
......
......@@ -22,6 +22,10 @@
#include "mojo/public/cpp/bindings/sync_handle_watcher.h"
#include "mojo/public/cpp/system/wait.h"
#if defined(ENABLE_IPC_FUZZER)
#include "mojo/public/cpp/bindings/message_dumper.h"
#endif
namespace mojo {
namespace {
......@@ -148,6 +152,11 @@ Connector::Connector(ScopedMessagePipeHandle message_pipe,
if (config == MULTI_THREADED_SEND)
lock_.emplace();
#if defined(ENABLE_IPC_FUZZER)
if (!MessageDumper::GetMessageDumpDirectory().empty())
message_dumper_ = std::make_unique<MessageDumper>();
#endif
weak_self_ = weak_factory_.GetWeakPtr();
// Even though we don't have an incoming receiver, we still want to monitor
// the message pipe to know if is closed or encounters an error.
......@@ -272,6 +281,13 @@ bool Connector::Accept(Message* message) {
if (!message_pipe_.is_valid() || drop_writes_)
return true;
#if defined(ENABLE_IPC_FUZZER)
if (message_dumper_ && message->is_serialized()) {
bool dump_result = message_dumper_->Accept(message);
DCHECK(dump_result);
}
#endif
MojoResult rv =
WriteMessageNew(message_pipe_.get(), message->TakeMojoMessage(),
MOJO_WRITE_MESSAGE_FLAG_NONE);
......
......@@ -185,6 +185,10 @@ Message::Message(Message&& other)
serialized_(other.serialized_) {
other.transferable_ = false;
other.serialized_ = false;
#if defined(ENABLE_IPC_FUZZER)
interface_name_ = other.interface_name_;
method_name_ = other.method_name_;
#endif
}
Message::Message(std::unique_ptr<internal::UnserializedMessageContext> context)
......@@ -262,6 +266,10 @@ Message& Message::operator=(Message&& other) {
other.transferable_ = false;
serialized_ = other.serialized_;
other.serialized_ = false;
#if defined(ENABLE_IPC_FUZZER)
interface_name_ = other.interface_name_;
method_name_ = other.method_name_;
#endif
return *this;
}
......
// 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 "mojo/public/cpp/bindings/message_dumper.h"
#include "base/files/file.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/logging.h"
#include "base/no_destructor.h"
#include "base/process/process.h"
#include "base/rand_util.h"
#include "base/strings/string_number_conversions.h"
#include "base/task_scheduler/post_task.h"
#include "mojo/public/cpp/bindings/message.h"
namespace {
base::FilePath& DumpDirectory() {
static base::NoDestructor<base::FilePath> dump_directory;
return *dump_directory;
}
void WriteMessage(uint32_t identifier,
const mojo::MessageDumper::MessageEntry& entry) {
static uint64_t num = 0;
if (!entry.interface_name)
return;
base::FilePath message_directory =
DumpDirectory()
.AppendASCII(entry.interface_name)
.AppendASCII(base::NumberToString(identifier));
if (!base::DirectoryExists(message_directory) &&
!base::CreateDirectory(message_directory)) {
LOG(ERROR) << "Failed to create" << message_directory.value();
return;
}
std::string filename =
base::NumberToString(num++) + "." + entry.method_name + ".mojomsg";
base::FilePath path = message_directory.AppendASCII(filename);
base::File file(path,
base::File::FLAG_WRITE | base::File::FLAG_CREATE_ALWAYS);
file.WriteAtCurrentPos(reinterpret_cast<const char*>(entry.data_bytes.data()),
static_cast<int>(entry.data_bytes.size()));
}
} // namespace
namespace mojo {
MessageDumper::MessageEntry::MessageEntry(const uint8_t* data,
uint32_t data_size,
const char* interface_name,
const char* method_name)
: interface_name(interface_name),
method_name(method_name),
data_bytes(data, data + data_size) {}
MessageDumper::MessageEntry::MessageEntry(const MessageEntry& entry) = default;
MessageDumper::MessageEntry::~MessageEntry() {}
MessageDumper::MessageDumper() : identifier_(base::RandUint64()) {}
MessageDumper::~MessageDumper() {}
bool MessageDumper::Accept(mojo::Message* message) {
MessageEntry entry(message->data(), message->data_num_bytes(),
message->interface_name(), message->method_name());
static base::NoDestructor<scoped_refptr<base::TaskRunner>> task_runner(
base::CreateSequencedTaskRunnerWithTraits(
{base::MayBlock(), base::TaskPriority::USER_BLOCKING,
base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN}));
(*task_runner)
->PostTask(FROM_HERE,
base::BindOnce(&WriteMessage, identifier_, std::move(entry)));
return true;
}
void MessageDumper::SetMessageDumpDirectory(const base::FilePath& directory) {
DumpDirectory() = directory;
}
const base::FilePath& MessageDumper::GetMessageDumpDirectory() {
return DumpDirectory();
}
} // namespace mojo
......@@ -211,6 +211,16 @@ class COMPONENT_EXPORT(MOJO_CPP_BINDINGS_BASE) Message {
generic_context.release()->template SafeCast<MessageType>());
}
#if defined(ENABLE_IPC_FUZZER)
const char* interface_name() const { return interface_name_; }
void set_interface_name(const char* interface_name) {
interface_name_ = interface_name;
}
const char* method_name() const { return method_name_; }
void set_method_name(const char* method_name) { method_name_ = method_name; }
#endif
private:
ScopedMessageHandle handle_;
......@@ -230,6 +240,11 @@ class COMPONENT_EXPORT(MOJO_CPP_BINDINGS_BASE) Message {
// Indicates whether this Message object is serialized.
bool serialized_ = false;
#if defined(ENABLE_IPC_FUZZER)
const char* interface_name_ = nullptr;
const char* method_name_ = nullptr;
#endif
DISALLOW_COPY_AND_ASSIGN(Message);
};
......
// 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.
#ifndef MOJO_PUBLIC_CPP_BINDINGS_MESSAGE_DUMPER_H_
#define MOJO_PUBLIC_CPP_BINDINGS_MESSAGE_DUMPER_H_
#include "base/files/file_path.h"
#include "mojo/public/cpp/bindings/message.h"
#include "mojo/public/cpp/bindings/message_dumper.h"
namespace mojo {
class MessageDumper : public mojo::MessageReceiver {
public:
MessageDumper();
~MessageDumper() override;
bool Accept(mojo::Message* message) override;
struct MessageEntry {
MessageEntry(const uint8_t* data,
uint32_t data_size,
const char* interface_name,
const char* method_name);
MessageEntry(const MessageEntry& entry);
~MessageEntry();
const char* interface_name;
const char* method_name;
std::vector<uint8_t> data_bytes;
};
static void SetMessageDumpDirectory(const base::FilePath& directory);
static const base::FilePath& GetMessageDumpDirectory();
private:
uint32_t identifier_;
};
} // namespace mojo
#endif // MOJO_PUBLIC_CPP_BINDINGS_MESSAGE_DUMPER_H_
......@@ -142,6 +142,11 @@ bool {{proxy_name}}::{{method.name}}(
"message")}}
{%- endif %}
#if defined(ENABLE_IPC_FUZZER)
message.set_interface_name({{class_name}}::Name_);
message.set_method_name("{{method.name}}");
#endif
bool result = false;
std::unique_ptr<mojo::MessageReceiver> responder(
new {{class_name}}_{{method.name}}_HandleSyncResponse(
......@@ -180,6 +185,11 @@ void {{proxy_name}}::{{method.name}}(
"message")}}
{%- endif %}
#if defined(ENABLE_IPC_FUZZER)
message.set_interface_name({{class_name}}::Name_);
message.set_method_name("{{method.name}}");
#endif
{%- if method.response_parameters != None %}
std::unique_ptr<mojo::MessageReceiver> responder(
new {{class_name}}_{{method.name}}_ForwardToCallback(
......@@ -329,6 +339,12 @@ void {{class_name}}_{{method.name}}_ProxyToResponder::Run(
TRACE_EVENT1("mojom", "(Impl){{namespace_as_string}}::{{class_name}}::{{method.name}}Callback",
"message", message.name());
#endif
#if defined(ENABLE_IPC_FUZZER)
message.set_interface_name({{class_name}}::Name_);
message.set_method_name("{{method.name}}");
#endif
message.set_request_id(request_id_);
ignore_result(responder_->Accept(&message));
// TODO(darin): Accept() returning false indicates a malformed message, and
......
......@@ -5,7 +5,9 @@
import("//tools/ipc_fuzzer/ipc_fuzzer.gni")
config("ipc_fuzzer_config") {
defines = [ "ENABLE_IPC_FUZZER" ]
if (current_toolchain == host_toolchain) {
defines = [ "ENABLE_IPC_FUZZER" ]
}
}
config("ipc_fuzzer_tool_config") {
......
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