| // 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 <iostream> | 
 |  | 
 | #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_ostream_operators.h" | 
 | #include "base/strings/utf_string_conversions.h" | 
 | #include "testing/libfuzzer/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<std::string_view> 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; | 
 | } |