aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFabian Meumertzheim <fabian@meumertzhe.im>2022-08-11 10:00:30 +0200
committerFabian Meumertzheim <fabian@meumertzhe.im>2022-08-15 22:27:22 +0200
commit5a2137e1628012bc0911bb3a22b9022d7128ca24 (patch)
tree54d0313bee03bf948944a8059dfc88f077c3a54b
parent07ce6176cbd19b3bdd3af413c62b577bd619b9c8 (diff)
downloadjazzer-api-5a2137e1628012bc0911bb3a22b9022d7128ca24.tar.gz
all: Handle argument pre-processing in the Java driver
-rw-r--r--agent/src/main/java/com/code_intelligence/jazzer/agent/Agent.kt6
-rw-r--r--agent/src/main/java/com/code_intelligence/jazzer/agent/BUILD.bazel1
-rw-r--r--driver/BUILD.bazel1
-rw-r--r--driver/fuzzed_data_provider_test.cpp2
-rw-r--r--driver/jazzer_main.cpp102
-rw-r--r--driver/jvm_tooling.cpp16
-rw-r--r--driver/jvm_tooling.h2
-rw-r--r--driver/jvm_tooling_test.cpp2
-rw-r--r--driver/src/main/java/com/code_intelligence/jazzer/driver/BUILD.bazel5
-rw-r--r--driver/src/main/java/com/code_intelligence/jazzer/driver/Driver.java60
-rw-r--r--driver/src/main/java/com/code_intelligence/jazzer/driver/Opt.java1
11 files changed, 78 insertions, 120 deletions
diff --git a/agent/src/main/java/com/code_intelligence/jazzer/agent/Agent.kt b/agent/src/main/java/com/code_intelligence/jazzer/agent/Agent.kt
index d3b4c4dd..3690c5cf 100644
--- a/agent/src/main/java/com/code_intelligence/jazzer/agent/Agent.kt
+++ b/agent/src/main/java/com/code_intelligence/jazzer/agent/Agent.kt
@@ -16,6 +16,7 @@
package com.code_intelligence.jazzer.agent
+import com.code_intelligence.jazzer.driver.Opt
import com.code_intelligence.jazzer.instrumentor.CoverageRecorder
import com.code_intelligence.jazzer.instrumentor.Hooks
import com.code_intelligence.jazzer.instrumentor.InstrumentationType
@@ -43,7 +44,6 @@ private val KNOWN_ARGUMENTS = listOf(
"trace",
"custom_hooks",
"disabled_hooks",
- "id_sync_file",
"dump_classes_dir",
)
@@ -140,8 +140,8 @@ fun premain(agentArgs: String?, instrumentation: Instrumentation) {
}
}
}.toSet()
- val idSyncFile = argumentMap["id_sync_file"]?.let {
- Paths.get(it.single()).also { path ->
+ val idSyncFile = Opt.idSyncFile.takeUnless { it.isEmpty() }?.let {
+ Paths.get(it).also { path ->
println("INFO: Synchronizing coverage IDs in ${path.toAbsolutePath()}")
}
}
diff --git a/agent/src/main/java/com/code_intelligence/jazzer/agent/BUILD.bazel b/agent/src/main/java/com/code_intelligence/jazzer/agent/BUILD.bazel
index 4559dbba..84dd4c19 100644
--- a/agent/src/main/java/com/code_intelligence/jazzer/agent/BUILD.bazel
+++ b/agent/src/main/java/com/code_intelligence/jazzer/agent/BUILD.bazel
@@ -12,5 +12,6 @@ kt_jvm_library(
"//agent/src/main/java/com/code_intelligence/jazzer/instrumentor",
"//agent/src/main/java/com/code_intelligence/jazzer/runtime",
"//agent/src/main/java/com/code_intelligence/jazzer/runtime:signal_handler",
+ "//driver/src/main/java/com/code_intelligence/jazzer/driver:opt",
],
)
diff --git a/driver/BUILD.bazel b/driver/BUILD.bazel
index 1d74678d..6de3bf75 100644
--- a/driver/BUILD.bazel
+++ b/driver/BUILD.bazel
@@ -112,7 +112,6 @@ cc_library(
deps = [
":jvm_tooling_lib",
"@com_google_absl//absl/strings",
- "@com_google_absl//absl/strings:str_format",
"@com_google_glog//:glog",
"@fmeum_rules_jni//jni:libjvm",
"@jazzer_com_github_gflags_gflags//:gflags",
diff --git a/driver/fuzzed_data_provider_test.cpp b/driver/fuzzed_data_provider_test.cpp
index 438a90e8..c0a12d70 100644
--- a/driver/fuzzed_data_provider_test.cpp
+++ b/driver/fuzzed_data_provider_test.cpp
@@ -115,7 +115,7 @@ class FuzzedDataProviderTest : public ::testing::Test {
Runfiles* runfiles = Runfiles::CreateForTest();
FLAGS_cp = runfiles->Rlocation(FLAGS_cp);
- jvm_ = std::make_unique<JVM>("test_executable", "1234");
+ jvm_ = std::make_unique<JVM>("test_executable");
}
static void TearDownTestCase() { jvm_.reset(nullptr); }
diff --git a/driver/jazzer_main.cpp b/driver/jazzer_main.cpp
index ce8201a3..0697bd3e 100644
--- a/driver/jazzer_main.cpp
+++ b/driver/jazzer_main.cpp
@@ -24,15 +24,11 @@
#include <algorithm>
#include <filesystem>
-#include <fstream>
#include <iostream>
-#include <random>
#include <string>
#include <vector>
#include "absl/strings/match.h"
-#include "absl/strings/str_format.h"
-#include "absl/strings/strip.h"
#include "gflags/gflags.h"
#include "glog/logging.h"
#include "jvm_tooling.h"
@@ -42,15 +38,6 @@ using namespace std::string_literals;
// Defined by glog
DECLARE_bool(log_prefix);
-// Defined in jvm_tooling.cpp
-DECLARE_string(id_sync_file);
-
-// Defined in fuzz_target_runner.cpp
-DECLARE_string(coverage_report);
-
-// Defined in fuzz_target_runner.cpp
-DECLARE_string(coverage_dump);
-
namespace {
bool is_asan_active = false;
}
@@ -81,22 +68,6 @@ const std::string kUsageMessage =
const std::string kDriverClassName =
"com/code_intelligence/jazzer/driver/Driver";
-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();
-}
-
int StartLibFuzzer(std::unique_ptr<jazzer::JVM> jvm,
std::vector<std::string> argv) {
JNIEnv &env = jvm->GetEnv();
@@ -176,81 +147,12 @@ int main(int argc, char **argv) {
gflags::ParseCommandLineFlags(&our_argc, &our_argv, false);
}
- // The potentially modified command line arguments passed to libFuzzer at the
- // end of this function.
- std::vector<std::string> modified_argv =
- std::vector<std::string>(argv, argv_end);
-
- bool spawns_subprocesses = false;
- if (std::any_of(argv, argv_end, [](std::string_view arg) {
- return absl::StartsWith(arg, "-fork=") ||
- absl::StartsWith(arg, "-jobs=") ||
- absl::StartsWith(arg, "-merge=");
- })) {
- spawns_subprocesses = true;
- if (!FLAGS_coverage_report.empty()) {
- LOG(WARNING) << "WARN: --coverage_report does not support parallel "
- "fuzzing and has been disabled";
- FLAGS_coverage_report = "";
- }
- if (!FLAGS_coverage_dump.empty()) {
- LOG(WARNING) << "WARN: --coverage_dump does not support parallel "
- "fuzzing and has been disabled";
- FLAGS_coverage_dump = "";
- }
- 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();
- modified_argv.emplace_back(
- absl::StrFormat("--id_sync_file=%s", FLAGS_id_sync_file));
- }
- // 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);
- }
-
- std::string seed;
- // Search for the last occurence of a "-seed" argument as that is the one that
- // is used by libFuzzer.
- auto seed_pos = std::find_if(
- std::reverse_iterator(argv_end), std::reverse_iterator(argv),
- [](std::string_view arg) { return absl::StartsWith(arg, "-seed="); });
- if (seed_pos != std::reverse_iterator(argv)) {
- // An explicit seed has been provided on the command-line, record its value
- // so that it can be forwarded to the agent.
- seed = absl::StripPrefix(*seed_pos, "-seed=");
- } else {
- // No explicit seed has been set. Since Jazzer hooks might still want to use
- // a seed and we have to ensure that a fuzzing run can be reproduced by
- // setting the seed printed by libFuzzer, we generate a seed for it here so
- // that the two stay in sync.
- unsigned int random_seed = std::random_device()();
- seed = std::to_string(random_seed);
- // Only add the -seed argument to the command line if not running in a mode
- // that spawns subprocesses. These would inherit the same seed, which might
- // make them less effective.
- if (!spawns_subprocesses) {
- modified_argv.emplace_back("-seed=" + seed);
- }
- }
-
if (is_asan_active) {
std::cerr << "WARN: Jazzer is not compatible with LeakSanitizer yet. Leaks "
"are not reported."
<< std::endl;
}
- return StartLibFuzzer(std::make_unique<jazzer::JVM>(argv[0], seed),
- modified_argv);
+ return StartLibFuzzer(std::make_unique<jazzer::JVM>(argv[0]),
+ std::vector<std::string>(argv, argv_end));
}
diff --git a/driver/jvm_tooling.cpp b/driver/jvm_tooling.cpp
index 54eb7d7c..6ac50405 100644
--- a/driver/jvm_tooling.cpp
+++ b/driver/jvm_tooling.cpp
@@ -204,7 +204,6 @@ std::string agentArgsFromFlags() {
{"custom_hook_includes", FLAGS_custom_hook_includes},
{"custom_hook_excludes", FLAGS_custom_hook_excludes},
{"trace", FLAGS_trace},
- {"id_sync_file", FLAGS_id_sync_file},
{"dump_classes_dir", FLAGS_dump_classes_dir},
}) {
if (!flag_pair.second.empty()) {
@@ -214,7 +213,7 @@ std::string agentArgsFromFlags() {
return absl::StrJoin(args, ",");
}
-std::vector<std::string> fuzzTargetRunnerFlagsAsDefines() {
+std::vector<std::string> optsAsDefines() {
return {
absl::StrFormat("-Djazzer.target_class=%s", FLAGS_target_class),
absl::StrFormat("-Djazzer.target_args=%s", FLAGS_target_args),
@@ -228,6 +227,7 @@ std::vector<std::string> fuzzTargetRunnerFlagsAsDefines() {
absl::StrFormat("-Djazzer.autofuzz_ignore=%s", FLAGS_autofuzz_ignore),
absl::StrFormat("-Djazzer.hooks=%s", FLAGS_hooks ? "true" : "false"),
absl::StrFormat("-Djazzer.agent_args=%s", agentArgsFromFlags()),
+ absl::StrFormat("-Djazzer.id_sync_file=%s", FLAGS_id_sync_file),
};
}
@@ -262,7 +262,7 @@ std::vector<std::string> splitEscaped(const std::string &str) {
namespace jazzer {
-JVM::JVM(std::string_view executable_path, std::string_view seed) {
+JVM::JVM(std::string_view executable_path) {
// combine class path from command line flags and JAVA_FUZZER_CLASSPATH env
// variable
std::string class_path = absl::StrFormat("-Djava.class.path=%s", FLAGS_cp);
@@ -289,15 +289,9 @@ JVM::JVM(std::string_view executable_path, std::string_view seed) {
options.push_back(JavaVMOption{.optionString = (char *)"-XX:+UseParallelGC"});
options.push_back(
JavaVMOption{.optionString = (char *)"-XX:+CriticalJNINatives"});
- // Forward libFuzzer's random seed so that Jazzer hooks can base their
- // mutations on it.
- std::string seed_property = absl::StrFormat("-Djazzer.seed=%s", seed);
- options.push_back(
- JavaVMOption{.optionString = const_cast<char *>(seed_property.c_str())});
- std::vector<std::string> fuzz_target_runner_defines =
- fuzzTargetRunnerFlagsAsDefines();
- for (const auto &define : fuzz_target_runner_defines) {
+ std::vector<std::string> opt_defines = optsAsDefines();
+ for (const auto &define : opt_defines) {
options.push_back(
JavaVMOption{.optionString = const_cast<char *>(define.c_str())});
}
diff --git a/driver/jvm_tooling.h b/driver/jvm_tooling.h
index 80595f7a..7edb2c31 100644
--- a/driver/jvm_tooling.h
+++ b/driver/jvm_tooling.h
@@ -35,7 +35,7 @@ class JVM {
public:
// Creates a JVM instance with default options + options that were provided as
// command line flags.
- explicit JVM(std::string_view executable_path, std::string_view seed);
+ explicit JVM(std::string_view executable_path);
// Destroy the running JVM instance.
~JVM();
diff --git a/driver/jvm_tooling_test.cpp b/driver/jvm_tooling_test.cpp
index 527ff4fb..916cba6c 100644
--- a/driver/jvm_tooling_test.cpp
+++ b/driver/jvm_tooling_test.cpp
@@ -43,7 +43,7 @@ class JvmToolingTest : public ::testing::Test {
Runfiles *runfiles = Runfiles::CreateForTest();
FLAGS_cp = runfiles->Rlocation(FLAGS_cp);
- jvm_ = std::make_unique<JVM>("test_executable", "1234");
+ jvm_ = std::make_unique<JVM>("test_executable");
}
static void TearDownTestCase() { jvm_.reset(nullptr); }
diff --git a/driver/src/main/java/com/code_intelligence/jazzer/driver/BUILD.bazel b/driver/src/main/java/com/code_intelligence/jazzer/driver/BUILD.bazel
index c6588c4b..1a184d98 100644
--- a/driver/src/main/java/com/code_intelligence/jazzer/driver/BUILD.bazel
+++ b/driver/src/main/java/com/code_intelligence/jazzer/driver/BUILD.bazel
@@ -46,7 +46,10 @@ java_library(
java_library(
name = "opt",
srcs = ["Opt.java"],
- visibility = ["//driver/src/test/java/com/code_intelligence/jazzer/driver:__pkg__"],
+ visibility = [
+ "//agent/src/main/java/com/code_intelligence/jazzer:__subpackages__",
+ "//driver/src/test/java/com/code_intelligence/jazzer/driver:__pkg__",
+ ],
)
java_library(
diff --git a/driver/src/main/java/com/code_intelligence/jazzer/driver/Driver.java b/driver/src/main/java/com/code_intelligence/jazzer/driver/Driver.java
index a4521434..5dd05d36 100644
--- a/driver/src/main/java/com/code_intelligence/jazzer/driver/Driver.java
+++ b/driver/src/main/java/com/code_intelligence/jazzer/driver/Driver.java
@@ -19,15 +19,73 @@ package com.code_intelligence.jazzer.driver;
import static java.lang.System.err;
import com.code_intelligence.jazzer.agent.Agent;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.security.SecureRandom;
import java.util.List;
import net.bytebuddy.agent.ByteBuddyAgent;
public class Driver {
// Accessed from jazzer_main.cpp.
@SuppressWarnings("unused")
- private static int start(byte[][] nativeArgs) {
+ private static int start(byte[][] nativeArgs) throws IOException {
List<String> args = Utils.fromNativeArgs(nativeArgs);
+ final boolean spawnsSubprocesses = args.stream().anyMatch(
+ arg -> arg.startsWith("-fork=") || arg.startsWith("-jobs=") || arg.startsWith("-merge="));
+ if (spawnsSubprocesses) {
+ if (!System.getProperty("jazzer.coverage_report", "").isEmpty()) {
+ err.println(
+ "WARN: --coverage_report does not support parallel fuzzing and has been disabled");
+ System.clearProperty("jazzer.coverage_report");
+ }
+ if (!System.getProperty("jazzer.coverage_dump", "").isEmpty()) {
+ err.println(
+ "WARN: --coverage_dump does not support parallel fuzzing and has been disabled");
+ System.clearProperty("jazzer.coverage_dump");
+ }
+
+ String idSyncFileArg = System.getProperty("jazzer.id_sync_file", "");
+ Path idSyncFile;
+ if (idSyncFileArg.isEmpty()) {
+ // 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.
+ idSyncFile = Files.createTempFile("jazzer-", "");
+ args.add("--id_sync_file=" + idSyncFile.toAbsolutePath());
+ } else {
+ // Creates the file, truncating it if it exists.
+ idSyncFile = Files.write(Paths.get(idSyncFileArg), new byte[] {});
+ }
+ // This wouldn't run in case we exit the process with _Exit, but the parent process of a -fork
+ // run is expected to exit with a regular exit(0), which does cause JVM shutdown hooks to run:
+ // https://github.com/llvm/llvm-project/blob/940e178c0018b32af2f1478d331fc41a92a7dac7/compiler-rt/lib/fuzzer/FuzzerFork.cpp#L491
+ idSyncFile.toFile().deleteOnExit();
+ }
+
+ // Jazzer's hooks use deterministic randomness and thus require a seed. Search for the last
+ // occurrence of a "-seed" argument as that is the one that is used by libFuzzer. If none is
+ // set, generate one and pass it to libFuzzer so that a fuzzing run can be reproduced simply by
+ // setting the seed printed by libFuzzer.
+ String seed =
+ args.stream()
+ .reduce(
+ (prev, cur) -> cur.startsWith("-seed=") ? cur.substring("-seed=".length()) : prev)
+ .orElseGet(() -> {
+ String newSeed = Integer.toUnsignedString(new SecureRandom().nextInt());
+ // Only add the -seed argument to the command line if not running in a mode
+ // that spawns subprocesses. These would inherit the same seed, which might
+ // make them less effective.
+ if (spawnsSubprocesses) {
+ args.add("-seed=" + newSeed);
+ }
+ return newSeed;
+ });
+ System.setProperty("jazzer.seed", seed);
+
// Do *not* modify system properties beyond this point - initializing Opt parses them as a side
// effect.
diff --git a/driver/src/main/java/com/code_intelligence/jazzer/driver/Opt.java b/driver/src/main/java/com/code_intelligence/jazzer/driver/Opt.java
index e5b98f61..509e5084 100644
--- a/driver/src/main/java/com/code_intelligence/jazzer/driver/Opt.java
+++ b/driver/src/main/java/com/code_intelligence/jazzer/driver/Opt.java
@@ -43,6 +43,7 @@ public final class Opt {
// Default to false if hooks is false to mimic the original behavior of the native fuzz target
// runner, but still support hooks = false && dedup = true.
public static boolean dedup = boolSetting("dedup", hooks);
+ public static final String idSyncFile = stringSetting("id_sync_file", null);
public static final Set<Long> ignore =
Collections.unmodifiableSet(stringListSetting("ignore", ',')
.stream()