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