• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2021 Code Intelligence GmbH
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //      http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "libfuzzer_driver.h"
16 
17 #include <rules_jni.h>
18 
19 #include <algorithm>
20 #include <filesystem>
21 #include <fstream>
22 #include <random>
23 #include <string>
24 #include <vector>
25 
26 #include "absl/strings/match.h"
27 #include "absl/strings/str_format.h"
28 #include "fuzz_target_runner.h"
29 #include "gflags/gflags.h"
30 #include "glog/logging.h"
31 #include "jvm_tooling.h"
32 
33 using namespace std::string_literals;
34 
35 // Defined by glog
36 DECLARE_bool(log_prefix);
37 
38 // Defined in libfuzzer_callbacks.cpp
39 DECLARE_bool(fake_pcs);
40 
41 // Defined in jvm_tooling.cpp
42 DECLARE_string(id_sync_file);
43 
44 // Defined in fuzz_target_runner.cpp
45 DECLARE_string(coverage_report);
46 
47 // This symbol is defined by sanitizers if linked into Jazzer or in
48 // sanitizer_symbols.cpp if no sanitizer is used.
49 extern "C" void __sanitizer_set_death_callback(void (*)());
50 
51 // We apply a patch to libFuzzer to make it call this function instead of
52 // __sanitizer_set_death_callback to pass us the death callback.
__jazzer_set_death_callback(void (* callback)())53 extern "C" [[maybe_unused]] void __jazzer_set_death_callback(
54     void (*callback)()) {
55   jazzer::AbstractLibfuzzerDriver::libfuzzer_print_crashing_input_ = callback;
56   __sanitizer_set_death_callback(callback);
57 }
58 
59 namespace {
60 char *additional_arg;
61 std::vector<char *> modified_argv;
62 
GetNewTempFilePath()63 std::string GetNewTempFilePath() {
64   auto temp_dir = std::filesystem::temp_directory_path();
65 
66   std::string temp_filename_suffix(32, '\0');
67   std::random_device rng;
68   std::uniform_int_distribution<short> dist(0, 'z' - 'a');
69   std::generate_n(temp_filename_suffix.begin(), temp_filename_suffix.length(),
70                   [&rng, &dist] { return static_cast<char>('a' + dist(rng)); });
71 
72   auto temp_path = temp_dir / ("jazzer-" + temp_filename_suffix);
73   if (std::filesystem::exists(temp_path))
74     throw std::runtime_error("Random temp file path exists: " +
75                              temp_path.string());
76   return temp_path.string();
77 }
78 }  // namespace
79 
80 namespace jazzer {
81 // A libFuzzer-registered callback that outputs the crashing input, but does
82 // not include a stack trace.
83 void (*AbstractLibfuzzerDriver::libfuzzer_print_crashing_input_)() = nullptr;
84 
AbstractLibfuzzerDriver(int * argc,char *** argv,const std::string & usage_string)85 AbstractLibfuzzerDriver::AbstractLibfuzzerDriver(
86     int *argc, char ***argv, const std::string &usage_string) {
87   gflags::SetUsageMessage(usage_string);
88   // Disable glog log prefixes to mimic libFuzzer output.
89   FLAGS_log_prefix = false;
90   google::InitGoogleLogging((*argv)[0]);
91   rules_jni_init((*argv)[0]);
92 
93   auto argv_start = *argv;
94   auto argv_end = *argv + *argc;
95 
96   if (std::find(argv_start, argv_end, "-use_value_profile=1"s) != argv_end) {
97     FLAGS_fake_pcs = true;
98   }
99 
100   // All libFuzzer flags start with a single dash, our arguments all start with
101   // a double dash. We can thus filter out the arguments meant for gflags by
102   // taking only those with a leading double dash.
103   std::vector<char *> our_args = {*argv_start};
104   std::copy_if(
105       argv_start, argv_end, std::back_inserter(our_args),
106       [](const auto arg) { return absl::StartsWith(std::string(arg), "--"); });
107   int our_argc = our_args.size();
108   char **our_argv = our_args.data();
109   // Let gflags consume its flags, but keep them in the argument list in case
110   // libFuzzer forwards the command line (e.g. with -jobs or -minimize_crash).
111   gflags::ParseCommandLineFlags(&our_argc, &our_argv, false);
112 
113   if (std::any_of(argv_start, argv_end, [](const std::string_view &arg) {
114         return absl::StartsWith(arg, "-fork=") ||
115                absl::StartsWith(arg, "-jobs=") ||
116                absl::StartsWith(arg, "-merge=");
117       })) {
118     if (!FLAGS_coverage_report.empty()) {
119       LOG(WARNING) << "WARN: --coverage_report does not support parallel "
120                       "fuzzing and has been disabled";
121       FLAGS_coverage_report = "";
122     }
123     if (FLAGS_id_sync_file.empty()) {
124       // Create an empty temporary file used for coverage ID synchronization and
125       // pass its path to the agent in every child process. This requires adding
126       // the argument to argv for it to be picked up by libFuzzer, which then
127       // forwards it to child processes.
128       FLAGS_id_sync_file = GetNewTempFilePath();
129       std::string new_arg =
130           absl::StrFormat("--id_sync_file=%s", FLAGS_id_sync_file);
131       // This argument can be accessed by libFuzzer at any (later) time and thus
132       // cannot be safely freed by us.
133       additional_arg = strdup(new_arg.c_str());
134       modified_argv = std::vector<char *>(argv_start, argv_end);
135       modified_argv.push_back(additional_arg);
136       // Terminate modified_argv.
137       modified_argv.push_back(nullptr);
138       // Modify argv and argc for libFuzzer. modified_argv must not be changed
139       // after this point.
140       *argc += 1;
141       *argv = modified_argv.data();
142       argv_start = *argv;
143       argv_end = *argv + *argc;
144     }
145     // Creates the file, truncating it if it exists.
146     std::ofstream touch_file(FLAGS_id_sync_file, std::ios_base::trunc);
147 
148     auto cleanup_fn = [] {
149       try {
150         std::filesystem::remove(std::filesystem::path(FLAGS_id_sync_file));
151       } catch (...) {
152         // We should not throw exceptions during shutdown.
153       }
154     };
155     std::atexit(cleanup_fn);
156   }
157 
158   initJvm(*argv_start);
159 }
160 
initJvm(const std::string & executable_path)161 void AbstractLibfuzzerDriver::initJvm(const std::string &executable_path) {
162   jvm_ = std::make_unique<jazzer::JVM>(executable_path);
163 }
164 
LibfuzzerDriver(int * argc,char *** argv)165 LibfuzzerDriver::LibfuzzerDriver(int *argc, char ***argv)
166     : AbstractLibfuzzerDriver(argc, argv, getUsageString()) {
167   // the FuzzTargetRunner can only be initialized after the fuzzer callbacks
168   // have been registered otherwise link errors would occur
169   runner_ = std::make_unique<jazzer::FuzzTargetRunner>(*jvm_);
170 }
171 
getUsageString()172 std::string LibfuzzerDriver::getUsageString() {
173   return R"(Test java fuzz targets using libFuzzer. Usage:
174   jazzer --cp=<java_class_path> --target_class=<fuzz_target_class> <libfuzzer_arguments...>)";
175 }
176 
TestOneInput(const uint8_t * data,const std::size_t size)177 RunResult LibfuzzerDriver::TestOneInput(const uint8_t *data,
178                                         const std::size_t size) {
179   // pass the fuzzer input to the java fuzz target
180   return runner_->Run(data, size);
181 }
182 
DumpReproducer(const uint8_t * data,std::size_t size)183 void LibfuzzerDriver::DumpReproducer(const uint8_t *data, std::size_t size) {
184   return runner_->DumpReproducer(data, size);
185 }
186 
187 }  // namespace jazzer
188