aboutsummaryrefslogtreecommitdiff
path: root/driver
diff options
context:
space:
mode:
authorFabian Meumertzheim <meumertzheim@code-intelligence.com>2021-04-20 16:18:23 +0200
committerFabian Meumertzheim <fabian@meumertzhe.im>2021-04-26 15:51:38 +0200
commit17d3abf666c87b66036abcee7554d6418cabcfe2 (patch)
treef845afeb860e41c42e9a75931d310000ce22f199 /driver
parentf1cbb0056b3c9076e2bd285bb5e707a26a514d9c (diff)
downloadjazzer-api-17d3abf666c87b66036abcee7554d6418cabcfe2.tar.gz
Add option to generate coverage report
The new --coverage_report option triggers a coverage report to be written on fuzzer exit. The report is generated with the JaCoCo analyzer. The information about observed coverage IDs is obtained from libFuzzer and combined with the coverage obtained during fuzzerInitialize as well as the current run.
Diffstat (limited to 'driver')
-rw-r--r--driver/coverage_tracker.cpp54
-rw-r--r--driver/coverage_tracker.h5
-rw-r--r--driver/fuzz_target_runner.cpp19
-rw-r--r--driver/libfuzzer_driver.cpp8
-rw-r--r--driver/libfuzzer_driver.h4
5 files changed, 87 insertions, 3 deletions
diff --git a/driver/coverage_tracker.cpp b/driver/coverage_tracker.cpp
index 9c85b6dc..cda352c8 100644
--- a/driver/coverage_tracker.cpp
+++ b/driver/coverage_tracker.cpp
@@ -25,10 +25,13 @@ extern "C" void __sanitizer_cov_8bit_counters_init(uint8_t *start,
uint8_t *end);
extern "C" void __sanitizer_cov_pcs_init(const uintptr_t *pcs_beg,
const uintptr_t *pcs_end);
+extern "C" size_t __sanitizer_cov_get_observed_pcs(uintptr_t **pc_entries);
constexpr auto kCoverageMapClass =
"com/code_intelligence/jazzer/runtime/CoverageMap";
constexpr auto kByteBufferClass = "java/nio/ByteBuffer";
+constexpr auto kCoverageRecorderClass =
+ "com/code_intelligence/jazzer/instrumentor/CoverageRecorder";
// The initial size of the Java coverage map (512 counters).
constexpr std::size_t kInitialCoverageCountersBufferSize = 1u << 9u;
@@ -45,7 +48,7 @@ void AssertNoException(JNIEnv &env) {
if (env.ExceptionCheck()) {
env.ExceptionDescribe();
throw std::runtime_error(
- "Java exception occured in CoverageTracker JNI code");
+ "Java exception occurred in CoverageTracker JNI code");
}
}
} // namespace
@@ -147,4 +150,53 @@ void CoverageTracker::Clear() {
}
uint8_t *CoverageTracker::GetCoverageCounters() { return counters_; }
+
+void CoverageTracker::RecordInitialCoverage(JNIEnv &env) {
+ jclass coverage_recorder = env.FindClass(kCoverageRecorderClass);
+ AssertNoException(env);
+ jmethodID coverage_recorder_update_covered_ids_with_coverage_map =
+ env.GetStaticMethodID(coverage_recorder,
+ "updateCoveredIdsWithCoverageMap", "()V");
+ AssertNoException(env);
+ env.CallStaticVoidMethod(
+ coverage_recorder,
+ coverage_recorder_update_covered_ids_with_coverage_map);
+ AssertNoException(env);
+}
+
+std::string CoverageTracker::ComputeCoverage(JNIEnv &env) {
+ uintptr_t *covered_pcs;
+ size_t num_covered_pcs = __sanitizer_cov_get_observed_pcs(&covered_pcs);
+ std::vector<jint> covered_edge_ids{};
+ covered_edge_ids.reserve(num_covered_pcs);
+ const uintptr_t first_pc = pc_entries_[0].PC;
+ std::for_each(covered_pcs, covered_pcs + num_covered_pcs,
+ [&covered_edge_ids, first_pc](const uintptr_t pc) {
+ jint edge_id =
+ (pc - first_pc) / sizeof(fake_instructions_[0]);
+ covered_edge_ids.push_back(edge_id);
+ });
+ delete[] covered_pcs;
+
+ jclass coverage_recorder = env.FindClass(kCoverageRecorderClass);
+ AssertNoException(env);
+ jmethodID coverage_recorder_compute_file_coverage = env.GetStaticMethodID(
+ coverage_recorder, "computeFileCoverage", "([I)Ljava/lang/String;");
+ AssertNoException(env);
+ jintArray covered_edge_ids_jni = env.NewIntArray(num_covered_pcs);
+ AssertNoException(env);
+ env.SetIntArrayRegion(covered_edge_ids_jni, 0, num_covered_pcs,
+ covered_edge_ids.data());
+ AssertNoException(env);
+ auto file_coverage_jni = (jstring)(env.CallStaticObjectMethod(
+ coverage_recorder, coverage_recorder_compute_file_coverage,
+ covered_edge_ids_jni));
+ AssertNoException(env);
+ auto file_coverage_cstr = env.GetStringUTFChars(file_coverage_jni, nullptr);
+ AssertNoException(env);
+ std::string file_coverage(file_coverage_cstr);
+ env.ReleaseStringUTFChars(file_coverage_jni, file_coverage_cstr);
+ AssertNoException(env);
+ return file_coverage;
+}
} // namespace jazzer
diff --git a/driver/coverage_tracker.h b/driver/coverage_tracker.h
index 79d8112d..55d9db60 100644
--- a/driver/coverage_tracker.h
+++ b/driver/coverage_tracker.h
@@ -16,6 +16,8 @@
#pragma once
+#include <string>
+
#include "jvm_tooling.h"
#include "third_party/jni/jni.h"
@@ -47,5 +49,8 @@ class CoverageTracker : public ExceptionPrinter {
// Returns the address of the coverage counters array.
static uint8_t *GetCoverageCounters();
+
+ static void RecordInitialCoverage(JNIEnv &env);
+ static std::string ComputeCoverage(JNIEnv &env);
};
} // namespace jazzer
diff --git a/driver/fuzz_target_runner.cpp b/driver/fuzz_target_runner.cpp
index 66405380..fd87d0e0 100644
--- a/driver/fuzz_target_runner.cpp
+++ b/driver/fuzz_target_runner.cpp
@@ -25,6 +25,7 @@
#include "absl/strings/str_format.h"
#include "absl/strings/str_split.h"
#include "absl/strings/substitute.h"
+#include "coverage_tracker.h"
#include "fuzzed_data_provider.h"
#include "gflags/gflags.h"
#include "glog/logging.h"
@@ -53,6 +54,11 @@ DEFINE_string(
DEFINE_string(reproducer_path, ".",
"Path at which fuzzing reproducers are stored. Defaults to the "
"current directory.");
+DEFINE_string(coverage_report, "",
+ "Path at which a coverage report is stored when the fuzzer "
+ "exits. If left empty, no report is generated (default)");
+
+DECLARE_bool(hooks);
constexpr auto kManifestUtilsClass =
"com/code_intelligence/jazzer/runtime/ManifestUtils";
@@ -165,6 +171,9 @@ FuzzTargetRunner::FuzzTargetRunner(
std::exit(1);
}
+ if (FLAGS_hooks && !FLAGS_coverage_report.empty()) {
+ CoverageTracker::RecordInitialCoverage(env);
+ }
SetUpFuzzedDataProvider(jvm_);
// Parse a comma-separated list of hex dedup tokens.
@@ -184,6 +193,16 @@ FuzzTargetRunner::FuzzTargetRunner(
}
FuzzTargetRunner::~FuzzTargetRunner() {
+ if (FLAGS_hooks && !FLAGS_coverage_report.empty()) {
+ std::string report = CoverageTracker::ComputeCoverage(jvm_.GetEnv());
+ std::ofstream report_file(FLAGS_coverage_report);
+ if (report_file) {
+ report_file << report << std::flush;
+ } else {
+ LOG(ERROR) << "Failed to write coverage report to "
+ << FLAGS_coverage_report;
+ }
+ }
if (fuzzer_tear_down_ != nullptr) {
std::cerr << "calling fuzzer teardown function" << std::endl;
jvm_.GetEnv().CallStaticVoidMethod(jclass_, fuzzer_tear_down_);
diff --git a/driver/libfuzzer_driver.cpp b/driver/libfuzzer_driver.cpp
index d69ca3d4..4dfb53cd 100644
--- a/driver/libfuzzer_driver.cpp
+++ b/driver/libfuzzer_driver.cpp
@@ -50,6 +50,9 @@ DECLARE_bool(fake_pcs);
// Defined in jvm_tooling.cpp
DECLARE_string(id_sync_file);
+// Defined in fuzz_target_runner.cpp
+DECLARE_string(coverage_report);
+
// 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(
@@ -128,6 +131,11 @@ AbstractLibfuzzerDriver::AbstractLibfuzzerDriver(
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
diff --git a/driver/libfuzzer_driver.h b/driver/libfuzzer_driver.h
index 22625c56..f3dd9a49 100644
--- a/driver/libfuzzer_driver.h
+++ b/driver/libfuzzer_driver.h
@@ -63,9 +63,9 @@ class LibfuzzerDriver : public AbstractLibfuzzerDriver {
public:
LibfuzzerDriver(int *argc, char ***argv);
- virtual RunResult TestOneInput(const uint8_t *data, std::size_t size);
+ RunResult TestOneInput(const uint8_t *data, std::size_t size) override;
- virtual ~LibfuzzerDriver() = default;
+ ~LibfuzzerDriver() override = default;
void DumpReproducer(const uint8_t *data, std::size_t size);