// Copyright 2024 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // A wrapper which knows to execute a given fuzzer within a fuzztest // executable that contains multiple fuzzers. // The fuzzer binary is assumed to be in the same directory as this binary. #include #include "base/command_line.h" #include "base/files/file_path.h" #include "base/files/file_util.h" #include "base/path_service.h" #include "base/process/launch.h" #include "base/strings/string_split.h" #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" #include "testing/libfuzzer/fuzztest_wrapper_buildflags.h" extern const char* kFuzzerBinary; extern const char* kFuzzerArgs; #if BUILDFLAG(USE_CENTIPEDE) namespace { void HandleReplayMode(auto& args) { // We're handling a centipede based fuzzer. If the last argument is a // filepath, we're trying to replay a testcase, since it doesn't make sense // to get a filepath when running with the centipede binary. if (args.size() <= 1) { return; } base::FilePath test_case(args.back()); if (!base::PathExists(test_case)) { return; } auto env = base::Environment::Create(); #if BUILDFLAG(IS_WIN) auto env_value = base::WideToUTF8(args.back()); #else auto env_value = args.back(); #endif env->SetVar("FUZZTEST_REPLAY", env_value); env->UnSetVar("CENTIPEDE_RUNNER_FLAGS"); std::cerr << "FuzzTest wrapper setting env var: FUZZTEST_REPLAY=" << args.back() << '\n'; // We must not add the testcase to the command line, as this will not be // parsed correctly by centipede. args.pop_back(); } } // namespace #endif // BUILDFLAG(USE_CENTIPEDE) int main(int argc, const char* const* argv) { base::CommandLine::Init(argc, argv); base::FilePath fuzzer_path; if (!base::PathService::Get(base::DIR_EXE, &fuzzer_path)) { return -1; } fuzzer_path = fuzzer_path.AppendASCII(kFuzzerBinary); base::LaunchOptions launch_options; base::CommandLine cmdline(fuzzer_path); std::vector additional_args = base::SplitStringPiece( kFuzzerArgs, " ", base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL); for (auto arg : additional_args) { cmdline.AppendArg(arg); } auto args = base::CommandLine::ForCurrentProcess()->argv(); #if BUILDFLAG(USE_CENTIPEDE) HandleReplayMode(args); #endif // BUILDFLAG(USE_CENTIPEDE) bool skipped_first = false; for (auto arg : args) { if (!skipped_first) { skipped_first = true; continue; } // We avoid AppendArguments because it parses switches then reorders things. cmdline.AppendArgNative(arg); } std::cerr << "FuzzTest wrapper launching:" << cmdline.GetCommandLineString() << "\n"; base::Process p = base::LaunchProcess(cmdline, launch_options); int exit_code; p.WaitForExit(&exit_code); return exit_code; } #if defined(WIN32) #define ALWAYS_EXPORT __declspec(dllexport) #else #define ALWAYS_EXPORT __attribute__((visibility("default"))) #endif ALWAYS_EXPORT extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { // No-op. This symbol exists to ensure that this binary is detected as // a fuzzer by ClusterFuzz's heuristics. It never actually gets called. return -1; }