// Copyright 2015 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 #include #include #include "base/command_line.h" #include "base/strings/string_split.h" #include "ipc/ipc_message_macros.h" #include "tools/ipc_fuzzer/fuzzer/fuzzer.h" #include "tools/ipc_fuzzer/fuzzer/generator.h" #include "tools/ipc_fuzzer/fuzzer/mutator.h" #include "tools/ipc_fuzzer/fuzzer/rand_util.h" #include "tools/ipc_fuzzer/message_lib/message_file.h" namespace ipc_fuzzer { namespace { // TODO(mbarbella): Check to see if this value is actually reasonable. const int kFrequency = 23; const char kCountSwitch[] = "count"; const char kCountSwitchHelp[] = "Number of messages to generate (generator)."; const char kFrequencySwitch[] = "frequency"; const char kFrequencySwitchHelp[] = "Probability of mutation; tweak every 1/|q| times (mutator)."; const char kFuzzerNameSwitch[] = "fuzzer-name"; const char kFuzzerNameSwitchHelp[] = "Select generator, mutator, or no-op fuzzer. Default: generator"; const char kHelpSwitch[] = "help"; const char kHelpSwitchHelp[] = "Show this message."; const char kPermuteSwitch[] = "permute"; const char kPermuteSwitchHelp[] = "Randomly shuffle the order of all messages (mutator)."; const char kTypeListSwitch[] = "type-list"; const char kTypeListSwitchHelp[] = "Explicit list of the only message-ids to mutate (mutator)."; void usage() { std::cerr << "Mutate messages from an exiting message file.\n"; std::cerr << "Usage:\n" << " ipc_fuzzer" << " [--" << kCountSwitch << "=c]" << " [--" << kFrequencySwitch << "=q]" << " [--" << kFuzzerNameSwitch << "=f]" << " [--" << kHelpSwitch << "]" << " [--" << kTypeListSwitch << "=x,y,z...]" << " [--" << kPermuteSwitch << "]" << " [infile (mutation only)] outfile\n"; std::cerr << " --" << kCountSwitch << " - " << kCountSwitchHelp << "\n" << " --" << kFrequencySwitch << " - " << kFrequencySwitchHelp << "\n" << " --" << kFuzzerNameSwitch << " - " << kFuzzerNameSwitchHelp << "\n" << " --" << kHelpSwitch << " - " << kHelpSwitchHelp << "\n" << " --" << kTypeListSwitch << " - " << kTypeListSwitchHelp << "\n" << " --" << kPermuteSwitch << " - " << kPermuteSwitchHelp << "\n"; } } // namespace class FuzzerFactory { public: static Fuzzer *Create(const std::string& name, int frequency) { if (name == "default") return new Generator(); if (name == "generate") return new Generator(); if (name == "mutate") return new Mutator(frequency); if (name == "no-op") return new NoOpFuzzer(); std::cerr << "No such fuzzer: " << name << "\n"; return 0; } }; static IPC::Message* RewriteMessage( IPC::Message* message, Fuzzer* fuzzer, FuzzerFunctionMap* map) { FuzzerFunctionMap::iterator it = map->find(message->type()); if (it == map->end()) { // This usually indicates a missing message file in all_messages.h, or // that the message dump file is taken from a different revision of // chromium from this executable. std::cerr << "Unknown message type: [" << IPC_MESSAGE_ID_CLASS(message->type()) << ", " << IPC_MESSAGE_ID_LINE(message->type()) << "].\n"; return 0; } return (*it->second)(message, fuzzer); } int Generate(base::CommandLine* cmd, Fuzzer* fuzzer) { base::CommandLine::StringVector args = cmd->GetArgs(); if (args.size() != 1) { usage(); return EXIT_FAILURE; } base::FilePath::StringType output_file_name = args[0]; int message_count = 1000; if (cmd->HasSwitch(kCountSwitch)) message_count = atoi(cmd->GetSwitchValueASCII(kCountSwitch).c_str()); MessageVector message_vector; int bad_count = 0; if (message_count < 0) { // Enumerate them all. for (size_t i = 0; i < g_function_vector.size(); ++i) { if (IPC::Message* new_message = (*g_function_vector[i])(NULL, fuzzer)) message_vector.push_back(new_message); else bad_count += 1; } } else { // Fuzz a random batch. for (int i = 0; i < message_count; ++i) { size_t index = RandInRange(g_function_vector.size()); if (IPC::Message* new_message = (*g_function_vector[index])(NULL, fuzzer)) message_vector.push_back(new_message); else bad_count += 1; } } std::cerr << "Failed to generate " << bad_count << " messages.\n"; if (!MessageFile::Write(base::FilePath(output_file_name), message_vector)) return EXIT_FAILURE; return EXIT_SUCCESS; } int Mutate(base::CommandLine* cmd, Fuzzer* fuzzer) { base::CommandLine::StringVector args = cmd->GetArgs(); if (args.size() != 2) { usage(); return EXIT_FAILURE; } base::FilePath::StringType input_file_name = args[0]; base::FilePath::StringType output_file_name = args[1]; bool permute = cmd->HasSwitch(kPermuteSwitch); std::string type_string_list = cmd->GetSwitchValueASCII(kTypeListSwitch); std::vector type_string_vector; base::SplitString(type_string_list, ',', &type_string_vector); std::set type_set; for (size_t i = 0; i < type_string_vector.size(); ++i) { type_set.insert(atoi(type_string_vector[i].c_str())); } FuzzerFunctionMap fuzz_function_map; PopulateFuzzerFunctionMap(&fuzz_function_map); MessageVector message_vector; if (!MessageFile::Read(base::FilePath(input_file_name), &message_vector)) return EXIT_FAILURE; for (size_t i = 0; i < message_vector.size(); ++i) { IPC::Message* msg = message_vector[i]; // If an explicit type set is specified, make sure we should be mutating // this message type on this run. if (!type_set.empty() && type_set.end() == std::find( type_set.begin(), type_set.end(), msg->type())) { continue; } IPC::Message* new_message = RewriteMessage(msg, fuzzer, &fuzz_function_map); if (new_message) { IPC::Message* old_message = message_vector[i]; delete old_message; message_vector[i] = new_message; } } if (permute) { std::random_shuffle(message_vector.begin(), message_vector.end(), RandInRange); } if (!MessageFile::Write(base::FilePath(output_file_name), message_vector)) return EXIT_FAILURE; return EXIT_SUCCESS; } int FuzzerMain(int argc, char** argv) { base::CommandLine::Init(argc, argv); base::CommandLine* cmd = base::CommandLine::ForCurrentProcess(); base::CommandLine::StringVector args = cmd->GetArgs(); if (args.size() == 0 || args.size() > 2 || cmd->HasSwitch(kHelpSwitch)) { usage(); return EXIT_FAILURE; } InitRand(); PopulateFuzzerFunctionVector(&g_function_vector); std::cerr << "Counted " << g_function_vector.size() << " distinct messages present in chrome.\n"; std::string fuzzer_name = "default"; if (cmd->HasSwitch(kFuzzerNameSwitch)) fuzzer_name = cmd->GetSwitchValueASCII(kFuzzerNameSwitch); int frequency = kFrequency; if (cmd->HasSwitch(kFrequencySwitch)) frequency = atoi(cmd->GetSwitchValueASCII(kFrequencySwitch).c_str()); Fuzzer* fuzzer = FuzzerFactory::Create(fuzzer_name, frequency); if (!fuzzer) return EXIT_FAILURE; int result; base::FilePath::StringType output_file_name; if (fuzzer_name == "default" || fuzzer_name == "generate") { result = Generate(cmd, fuzzer); } else { result = Mutate(cmd, fuzzer); } return result; } } // namespace ipc_fuzzer int main(int argc, char** argv) { return ipc_fuzzer::FuzzerMain(argc, argv); }