| // 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/files/file.h" |
| #include "base/message_loop/message_loop.h" |
| #include "base/run_loop.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "base/task/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; |
| } |