// Copyright 2017 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 <stddef.h>
#include <stdint.h>
#include "base/containers/flat_map.h"
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
#include "base/strings/string_number_conversions.h"
#include "base/task_scheduler/task_scheduler.h"
#include "mojo/core/embedder/embedder.h"
#include "mojo/public/tools/fuzzers/fuzz.mojom.h"
#include "mojo/public/tools/fuzzers/fuzz_impl.h"
/* Environment for the executable. Initializes the mojo EDK and sets up a
* TaskScheduler, because Mojo messages must be sent and processed from
* TaskRunners. */
struct Environment {
Environment() : message_loop() {
base::TaskScheduler::CreateAndStartWithDefaultParams(
"MojoFuzzerMessageDumpProcess");
mojo::core::Init();
}
/* Message loop to send messages on. */
base::MessageLoop message_loop;
/* Impl to be created. Stored in environment to keep it alive after
* DumpMessages returns. */
std::unique_ptr<FuzzImpl> impl;
};
Environment* env = new Environment();
/* MessageReceiver which dumps raw message bytes to disk in the provided
* directory. */
class MessageDumper : public mojo::MessageReceiver {
public:
explicit MessageDumper(std::string directory)
: directory_(directory), count_(0) {}
bool Accept(mojo::Message* message) override {
base::FilePath path = directory_.Append(FILE_PATH_LITERAL("message_") +
base::IntToString(count_++) +
FILE_PATH_LITERAL(".mojomsg"));
base::File file(path,
base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
if (!file.IsValid()) {
LOG(ERROR) << "Failed to create mojo message file: " << path.value();
return false;
}
size_t size = message->data_num_bytes();
const char* data = reinterpret_cast<const char*>(message->data());
int ret = file.WriteAtCurrentPos(data, size);
if (ret != static_cast<int>(size)) {
LOG(ERROR) << "Failed to write " << size << " bytes.";
return false;
}
return true;
}
base::FilePath directory_;
int count_;
};
/* Returns a FuzzUnion with fuzz_bool initialized. */
auto GetBoolFuzzUnion() {
fuzz::mojom::FuzzUnionPtr union_bool = fuzz::mojom::FuzzUnion::New();
union_bool->set_fuzz_bool(true);
return union_bool;
}
/* Returns a FuzzUnion with fuzz_struct_map initialized. Takes in a
* FuzzDummyStructPtr to use within the fuzz_struct_map value. */
auto GetStructMapFuzzUnion(fuzz::mojom::FuzzDummyStructPtr in) {
fuzz::mojom::FuzzUnionPtr union_struct_map = fuzz::mojom::FuzzUnion::New();
base::flat_map<std::string, fuzz::mojom::FuzzDummyStructPtr> struct_map;
struct_map["fuzz"] = std::move(in);
union_struct_map->set_fuzz_struct_map(std::move(struct_map));
return union_struct_map;
}
/* Returns a FuzzUnion with fuzz_complex initialized. Takes in a FuzzUnionPtr
* to use within the fuzz_complex value. */
auto GetComplexFuzzUnion(fuzz::mojom::FuzzUnionPtr in) {
std::remove_reference<decltype(in->get_fuzz_complex())>::type complex_map;
std::remove_reference<decltype(complex_map.value()[0])>::type outer;
std::remove_reference<decltype(
outer[fuzz::mojom::FuzzEnum::FUZZ_VALUE0])>::type inner;
std::remove_reference<decltype(inner['z'])>::type center;
center.emplace();
center.value().push_back(std::move(in));
inner['z'] = std::move(center);
outer[fuzz::mojom::FuzzEnum::FUZZ_VALUE0] = std::move(inner);
complex_map.emplace();
complex_map.value().push_back(std::move(outer));
fuzz::mojom::FuzzUnionPtr union_complex = fuzz::mojom::FuzzUnion::New();
union_complex->set_fuzz_complex(std::move(complex_map));
return union_complex;
}
/* Returns a populated value for FuzzStruct->fuzz_primitive_array. */
auto GetFuzzStructPrimitiveArrayValue() {
decltype(fuzz::mojom::FuzzStruct::fuzz_primitive_array) primitive_array;
primitive_array = {'f', 'u', 'z', 'z'};
return primitive_array;
}
/* Returns a populated value for FuzzStruct->fuzz_primitive_map. */
auto GetFuzzStructPrimitiveMapValue() {
decltype(fuzz::mojom::FuzzStruct::fuzz_primitive_map) primitive_map;
primitive_map["fuzz"] = 'z';
return primitive_map;
}
/* Returns a populated value for FuzzStruct->fuzz_array_map. */
auto GetFuzzStructArrayMapValue() {
decltype(fuzz::mojom::FuzzStruct::fuzz_array_map) array_map;
array_map["fuzz"] = {"fuzz1", "fuzz2"};
return array_map;
}
/* Returns a populated value for FuzzStruct->fuzz_union_map. Takes in a
* FuzzUnionPtr to use within the fuzz_union_map value.*/
auto GetFuzzStructUnionMapValue(fuzz::mojom::FuzzUnionPtr in) {
decltype(fuzz::mojom::FuzzStruct::fuzz_union_map) union_map;
union_map[fuzz::mojom::FuzzEnum::FUZZ_VALUE1] = std::move(in);
return union_map;
}
/* Returns a populated value for FuzzStruct->fuzz_union_array. Takes in a
* FuzzUnionPtr to use within the fuzz_union_array value.*/
auto GetFuzzStructUnionArrayValue(fuzz::mojom::FuzzUnionPtr in) {
decltype(fuzz::mojom::FuzzStruct::fuzz_union_array) union_array;
union_array.push_back(std::move(in));
return union_array;
}
/* Returns a populated value for FuzzStruct->fuzz_struct_array. Takes in a
* FuzzStructPtr to use within the fuzz_struct_array value. */
auto GetFuzzStructStructArrayValue(fuzz::mojom::FuzzStructPtr in) {
decltype(fuzz::mojom::FuzzStruct::fuzz_struct_array) struct_array;
struct_array.push_back(std::move(in));
return struct_array;
}
/* Returns a populated value for FuzzStruct->fuzz_nullable_array. */
auto GetFuzzStructNullableArrayValue() {
decltype(fuzz::mojom::FuzzStruct::fuzz_nullable_array) nullable_array;
return nullable_array;
}
/* Returns a populated value for FuzzStruct->fuzz_complex. */
auto GetFuzzStructComplexValue() {
decltype(fuzz::mojom::FuzzStruct::fuzz_complex) complex_map;
std::remove_reference<decltype(complex_map.value()[0])>::type outer;
std::remove_reference<decltype(
outer[fuzz::mojom::FuzzEnum::FUZZ_VALUE0])>::type inner;
std::remove_reference<decltype(inner['z'])>::type center;
center.emplace();
center.value().push_back(fuzz::mojom::FuzzStruct::New());
inner['z'] = std::move(center);
outer[fuzz::mojom::FuzzEnum::FUZZ_VALUE0] = std::move(inner);
complex_map.emplace();
complex_map.value().push_back(std::move(outer));
return complex_map;
}
/* Returns a FuzzStruct with its fields populated. */
fuzz::mojom::FuzzStructPtr GetPopulatedFuzzStruct() {
/* Make some populated Unions. */
auto union_bool = GetBoolFuzzUnion();
auto union_struct_map =
GetStructMapFuzzUnion(fuzz::mojom::FuzzDummyStruct::New());
auto union_complex = GetComplexFuzzUnion(std::move(union_bool));
/* Prepare the nontrivial fields for the struct. */
auto fuzz_primitive_array = GetFuzzStructPrimitiveArrayValue();
auto fuzz_primitive_map = GetFuzzStructPrimitiveMapValue();
auto fuzz_array_map = GetFuzzStructArrayMapValue();
auto fuzz_union_map = GetFuzzStructUnionMapValue(std::move(union_struct_map));
auto fuzz_union_array =
GetFuzzStructUnionArrayValue(std::move(union_complex));
auto fuzz_struct_array =
GetFuzzStructStructArrayValue(fuzz::mojom::FuzzStruct::New());
auto fuzz_nullable_array = GetFuzzStructNullableArrayValue();
auto fuzz_complex = GetFuzzStructComplexValue();
/* Make a populated struct and return it. */
return fuzz::mojom::FuzzStruct::New(
true, /* fuzz_bool */
-1, /* fuzz_int8 */
1, /* fuzz_uint8 */
-(1 << 8), /* fuzz_int16 */
1 << 8, /* fuzz_uint16 */
-(1 << 16), /* fuzz_int32 */
1 << 16, /* fuzz_uint32 */
-((int64_t)1 << 32), /* fuzz_int64 */
(uint64_t)1 << 32, /* fuzz_uint64 */
1.0, /* fuzz_float */
1.0, /* fuzz_double */
"fuzz", /* fuzz_string */
std::move(fuzz_primitive_array), /* fuzz_primitive_array */
std::move(fuzz_primitive_map), /* fuzz_primitive_map */
std::move(fuzz_array_map), /* fuzz_array_map */
std::move(fuzz_union_map), /* fuzz_union_map */
std::move(fuzz_union_array), /* fuzz_union_array */
std::move(fuzz_struct_array), /* fuzz_struct_array */
std::move(fuzz_nullable_array), /* fuzz_nullable_array */
std::move(fuzz_complex)); /* fuzz_complex */
}
/* Callback used for messages with responses. Does nothing. */
void FuzzCallback() {}
/* Invokes each method in the FuzzInterface and dumps the messages to the
* supplied directory. */
void DumpMessages(std::string output_directory) {
fuzz::mojom::FuzzInterfacePtr fuzz;
fuzz::mojom::FuzzDummyInterfaceAssociatedPtr dummy;
/* Create the impl and add a MessageDumper to the filter chain. */
env->impl = std::make_unique<FuzzImpl>(MakeRequest(&fuzz));
env->impl->binding_.RouterForTesting()->AddIncomingMessageFilter(
std::make_unique<MessageDumper>(output_directory));
/* Call methods in various ways to generate interesting messages. */
fuzz->FuzzBasic();
fuzz->FuzzBasicResp(base::Bind(FuzzCallback));
fuzz->FuzzBasicSyncResp();
fuzz->FuzzArgs(fuzz::mojom::FuzzStruct::New(),
fuzz::mojom::FuzzStructPtr(nullptr));
fuzz->FuzzArgs(fuzz::mojom::FuzzStruct::New(), GetPopulatedFuzzStruct());
fuzz->FuzzArgsResp(fuzz::mojom::FuzzStruct::New(), GetPopulatedFuzzStruct(),
base::Bind(FuzzCallback));
fuzz->FuzzArgsResp(fuzz::mojom::FuzzStruct::New(), GetPopulatedFuzzStruct(),
base::Bind(FuzzCallback));
fuzz->FuzzArgsSyncResp(fuzz::mojom::FuzzStruct::New(),
GetPopulatedFuzzStruct(), base::Bind(FuzzCallback));
fuzz->FuzzArgsSyncResp(fuzz::mojom::FuzzStruct::New(),
GetPopulatedFuzzStruct(), base::Bind(FuzzCallback));
fuzz->FuzzAssociated(MakeRequest(&dummy));
dummy->Ping();
}
int main(int argc, char** argv) {
if (argc < 2) {
printf("Usage: %s [output_directory]\n", argv[0]);
exit(1);
}
std::string output_directory(argv[1]);
/* Dump the messages from a MessageLoop, and wait for it to finish. */
env->message_loop.task_runner()->PostTask(
FROM_HERE, base::BindOnce(&DumpMessages, output_directory));
base::RunLoop().RunUntilIdle();
return 0;
}