diff options
author | Fabian Meumertzheim <fabian@meumertzhe.im> | 2022-08-11 10:00:30 +0200 |
---|---|---|
committer | Fabian Meumertzheim <fabian@meumertzhe.im> | 2022-08-15 22:27:22 +0200 |
commit | 5a2137e1628012bc0911bb3a22b9022d7128ca24 (patch) | |
tree | 54d0313bee03bf948944a8059dfc88f077c3a54b | |
parent | 07ce6176cbd19b3bdd3af413c62b577bd619b9c8 (diff) | |
download | jazzer-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.kt | 6 | ||||
-rw-r--r-- | agent/src/main/java/com/code_intelligence/jazzer/agent/BUILD.bazel | 1 | ||||
-rw-r--r-- | driver/BUILD.bazel | 1 | ||||
-rw-r--r-- | driver/fuzzed_data_provider_test.cpp | 2 | ||||
-rw-r--r-- | driver/jazzer_main.cpp | 102 | ||||
-rw-r--r-- | driver/jvm_tooling.cpp | 16 | ||||
-rw-r--r-- | driver/jvm_tooling.h | 2 | ||||
-rw-r--r-- | driver/jvm_tooling_test.cpp | 2 | ||||
-rw-r--r-- | driver/src/main/java/com/code_intelligence/jazzer/driver/BUILD.bazel | 5 | ||||
-rw-r--r-- | driver/src/main/java/com/code_intelligence/jazzer/driver/Driver.java | 60 | ||||
-rw-r--r-- | driver/src/main/java/com/code_intelligence/jazzer/driver/Opt.java | 1 |
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() |