aboutsummaryrefslogtreecommitdiff
path: root/driver/libfuzzer_driver.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'driver/libfuzzer_driver.cpp')
-rw-r--r--driver/libfuzzer_driver.cpp187
1 files changed, 187 insertions, 0 deletions
diff --git a/driver/libfuzzer_driver.cpp b/driver/libfuzzer_driver.cpp
new file mode 100644
index 00000000..57beef58
--- /dev/null
+++ b/driver/libfuzzer_driver.cpp
@@ -0,0 +1,187 @@
+// Copyright 2021 Code Intelligence GmbH
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "libfuzzer_driver.h"
+
+#include <rules_jni.h>
+
+#include <algorithm>
+#include <filesystem>
+#include <fstream>
+#include <random>
+#include <string>
+#include <vector>
+
+#include "absl/strings/match.h"
+#include "absl/strings/str_format.h"
+#include "fuzz_target_runner.h"
+#include "gflags/gflags.h"
+#include "glog/logging.h"
+#include "jvm_tooling.h"
+
+using namespace std::string_literals;
+
+// Defined by glog
+DECLARE_bool(log_prefix);
+
+// Defined in libfuzzer_callbacks.cpp
+DECLARE_bool(fake_pcs);
+
+// Defined in jvm_tooling.cpp
+DECLARE_string(id_sync_file);
+
+// Defined in fuzz_target_runner.cpp
+DECLARE_string(coverage_report);
+
+// This symbol is defined by sanitizers if linked into Jazzer or in
+// sanitizer_symbols.cpp if no sanitizer is used.
+extern "C" void __sanitizer_set_death_callback(void (*)());
+
+// We apply a patch to libFuzzer to make it call this function instead of
+// __sanitizer_set_death_callback to pass us the death callback.
+extern "C" [[maybe_unused]] void __jazzer_set_death_callback(
+ void (*callback)()) {
+ jazzer::AbstractLibfuzzerDriver::libfuzzer_print_crashing_input_ = callback;
+ __sanitizer_set_death_callback(callback);
+}
+
+namespace {
+char *additional_arg;
+std::vector<char *> modified_argv;
+
+std::string GetNewTempFilePath() {
+ auto temp_dir = std::filesystem::temp_directory_path();
+
+ std::string temp_filename_suffix(32, '\0');
+ std::random_device rng;
+ std::uniform_int_distribution<short> dist(0, 'z' - 'a');
+ std::generate_n(temp_filename_suffix.begin(), temp_filename_suffix.length(),
+ [&rng, &dist] { return static_cast<char>('a' + dist(rng)); });
+
+ auto temp_path = temp_dir / ("jazzer-" + temp_filename_suffix);
+ if (std::filesystem::exists(temp_path))
+ throw std::runtime_error("Random temp file path exists: " +
+ temp_path.string());
+ return temp_path.string();
+}
+} // namespace
+
+namespace jazzer {
+// A libFuzzer-registered callback that outputs the crashing input, but does
+// not include a stack trace.
+void (*AbstractLibfuzzerDriver::libfuzzer_print_crashing_input_)() = nullptr;
+
+AbstractLibfuzzerDriver::AbstractLibfuzzerDriver(
+ int *argc, char ***argv, const std::string &usage_string) {
+ gflags::SetUsageMessage(usage_string);
+ // Disable glog log prefixes to mimic libFuzzer output.
+ FLAGS_log_prefix = false;
+ google::InitGoogleLogging((*argv)[0]);
+ rules_jni_init((*argv)[0]);
+
+ auto argv_start = *argv;
+ auto argv_end = *argv + *argc;
+
+ if (std::find(argv_start, argv_end, "-use_value_profile=1"s) != argv_end) {
+ FLAGS_fake_pcs = true;
+ }
+
+ // All libFuzzer flags start with a single dash, our arguments all start with
+ // a double dash. We can thus filter out the arguments meant for gflags by
+ // taking only those with a leading double dash.
+ std::vector<char *> our_args = {*argv_start};
+ std::copy_if(
+ argv_start, argv_end, std::back_inserter(our_args),
+ [](const auto arg) { return absl::StartsWith(std::string(arg), "--"); });
+ int our_argc = our_args.size();
+ char **our_argv = our_args.data();
+ // Let gflags consume its flags, but keep them in the argument list in case
+ // libFuzzer forwards the command line (e.g. with -jobs or -minimize_crash).
+ gflags::ParseCommandLineFlags(&our_argc, &our_argv, false);
+
+ if (std::any_of(argv_start, argv_end, [](const std::string_view &arg) {
+ return absl::StartsWith(arg, "-fork=") ||
+ absl::StartsWith(arg, "-jobs=") ||
+ absl::StartsWith(arg, "-merge=");
+ })) {
+ if (!FLAGS_coverage_report.empty()) {
+ LOG(WARNING) << "WARN: --coverage_report does not support parallel "
+ "fuzzing and has been disabled";
+ FLAGS_coverage_report = "";
+ }
+ if (FLAGS_id_sync_file.empty()) {
+ // Create an empty temporary file used for coverage ID synchronization and
+ // pass its path to the agent in every child process. This requires adding
+ // the argument to argv for it to be picked up by libFuzzer, which then
+ // forwards it to child processes.
+ FLAGS_id_sync_file = GetNewTempFilePath();
+ std::string new_arg =
+ absl::StrFormat("--id_sync_file=%s", FLAGS_id_sync_file);
+ // This argument can be accessed by libFuzzer at any (later) time and thus
+ // cannot be safely freed by us.
+ additional_arg = strdup(new_arg.c_str());
+ modified_argv = std::vector<char *>(argv_start, argv_end);
+ modified_argv.push_back(additional_arg);
+ // Terminate modified_argv.
+ modified_argv.push_back(nullptr);
+ // Modify argv and argc for libFuzzer. modified_argv must not be changed
+ // after this point.
+ *argc += 1;
+ *argv = modified_argv.data();
+ argv_start = *argv;
+ argv_end = *argv + *argc;
+ }
+ // Creates the file, truncating it if it exists.
+ std::ofstream touch_file(FLAGS_id_sync_file, std::ios_base::trunc);
+
+ auto cleanup_fn = [] {
+ try {
+ std::filesystem::remove(std::filesystem::path(FLAGS_id_sync_file));
+ } catch (...) {
+ // We should not throw exceptions during shutdown.
+ }
+ };
+ std::atexit(cleanup_fn);
+ }
+
+ initJvm(*argv_start);
+}
+
+void AbstractLibfuzzerDriver::initJvm(const std::string &executable_path) {
+ jvm_ = std::make_unique<jazzer::JVM>(executable_path);
+}
+
+LibfuzzerDriver::LibfuzzerDriver(int *argc, char ***argv)
+ : AbstractLibfuzzerDriver(argc, argv, getUsageString()) {
+ // the FuzzTargetRunner can only be initialized after the fuzzer callbacks
+ // have been registered otherwise link errors would occur
+ runner_ = std::make_unique<jazzer::FuzzTargetRunner>(*jvm_);
+}
+
+std::string LibfuzzerDriver::getUsageString() {
+ return R"(Test java fuzz targets using libFuzzer. Usage:
+ jazzer --cp=<java_class_path> --target_class=<fuzz_target_class> <libfuzzer_arguments...>)";
+}
+
+RunResult LibfuzzerDriver::TestOneInput(const uint8_t *data,
+ const std::size_t size) {
+ // pass the fuzzer input to the java fuzz target
+ return runner_->Run(data, size);
+}
+
+void LibfuzzerDriver::DumpReproducer(const uint8_t *data, std::size_t size) {
+ return runner_->DumpReproducer(data, size);
+}
+
+} // namespace jazzer