• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2024 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 // A wrapper which knows to execute a given fuzzer within a fuzztest
6 // executable that contains multiple fuzzers.
7 // The fuzzer binary is assumed to be in the same directory as this binary.
8 
9 #include <iostream>
10 
11 #include "base/command_line.h"
12 #include "base/files/file_path.h"
13 #include "base/files/file_util.h"
14 #include "base/path_service.h"
15 #include "base/process/launch.h"
16 #include "base/strings/string_split.h"
17 #include "base/strings/stringprintf.h"
18 #include "base/strings/utf_string_conversions.h"
19 #include "testing/libfuzzer/fuzztest_wrapper_buildflags.h"
20 
21 extern const char* kFuzzerBinary;
22 extern const char* kFuzzerArgs;
23 
24 #if BUILDFLAG(USE_CENTIPEDE)
25 
26 namespace {
HandleReplayMode(auto & args)27 void HandleReplayMode(auto& args) {
28   // We're handling a centipede based fuzzer. If the last argument is a
29   // filepath, we're trying to replay a testcase, since it doesn't make sense
30   // to get a filepath when running with the centipede binary.
31   if (args.size() <= 1) {
32     return;
33   }
34   base::FilePath test_case(args.back());
35   if (!base::PathExists(test_case)) {
36     return;
37   }
38 
39   auto env = base::Environment::Create();
40 #if BUILDFLAG(IS_WIN)
41   auto env_value = base::WideToUTF8(args.back());
42 #else
43   auto env_value = args.back();
44 #endif
45   env->SetVar("FUZZTEST_REPLAY", env_value);
46   env->UnSetVar("CENTIPEDE_RUNNER_FLAGS");
47   std::cerr << "FuzzTest wrapper setting env var: FUZZTEST_REPLAY="
48             << args.back() << '\n';
49 
50   // We must not add the testcase to the command line, as this will not be
51   // parsed correctly by centipede.
52   args.pop_back();
53 }
54 }  // namespace
55 
56 #endif  // BUILDFLAG(USE_CENTIPEDE)
57 
main(int argc,const char * const * argv)58 int main(int argc, const char* const* argv) {
59   base::CommandLine::Init(argc, argv);
60   base::FilePath fuzzer_path;
61   if (!base::PathService::Get(base::DIR_EXE, &fuzzer_path)) {
62     return -1;
63   }
64   fuzzer_path = fuzzer_path.AppendASCII(kFuzzerBinary);
65   base::LaunchOptions launch_options;
66   base::CommandLine cmdline(fuzzer_path);
67   std::vector<std::string_view> additional_args = base::SplitStringPiece(
68       kFuzzerArgs, " ", base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL);
69   for (auto arg : additional_args) {
70     cmdline.AppendArg(arg);
71   }
72   auto args = base::CommandLine::ForCurrentProcess()->argv();
73 #if BUILDFLAG(USE_CENTIPEDE)
74   HandleReplayMode(args);
75 #endif  // BUILDFLAG(USE_CENTIPEDE)
76 
77   bool skipped_first = false;
78   for (auto arg : args) {
79     if (!skipped_first) {
80       skipped_first = true;
81       continue;
82     }
83     // We avoid AppendArguments because it parses switches then reorders things.
84     cmdline.AppendArgNative(arg);
85   }
86   std::cerr << "FuzzTest wrapper launching:" << cmdline.GetCommandLineString()
87             << "\n";
88   base::Process p = base::LaunchProcess(cmdline, launch_options);
89   int exit_code;
90   p.WaitForExit(&exit_code);
91   return exit_code;
92 }
93 
94 #if defined(WIN32)
95 #define ALWAYS_EXPORT __declspec(dllexport)
96 #else
97 #define ALWAYS_EXPORT __attribute__((visibility("default")))
98 #endif
99 
LLVMFuzzerTestOneInput(const uint8_t * data,size_t size)100 ALWAYS_EXPORT extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data,
101                                                     size_t size) {
102   // No-op. This symbol exists to ensure that this binary is detected as
103   // a fuzzer by ClusterFuzz's heuristics. It never actually gets called.
104   return -1;
105 }
106