blob: fed72cd6b81723f833c2aac41a35b5548e906aec [file] [log] [blame]
// 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_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<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;
}