diff options
-rw-r--r-- | driver/jvm_tooling.cpp | 59 | ||||
-rw-r--r-- | driver/jvm_tooling_test.cpp | 13 | ||||
-rw-r--r-- | examples/BUILD.bazel | 6 | ||||
-rw-r--r-- | repositories.bzl | 6 |
4 files changed, 66 insertions, 18 deletions
diff --git a/driver/jvm_tooling.cpp b/driver/jvm_tooling.cpp index ece2c686..178eec02 100644 --- a/driver/jvm_tooling.cpp +++ b/driver/jvm_tooling.cpp @@ -22,6 +22,7 @@ #include "absl/strings/str_format.h" #include "absl/strings/str_join.h" +#include "absl/strings/str_replace.h" #include "absl/strings/str_split.h" #include "coverage_tracker.h" #include "gflags/gflags.h" @@ -32,14 +33,16 @@ #include "utils.h" DEFINE_string(cp, ".", - "the jvm class path which should include the fuzz target class, " - "instrumentor " - "runtime and further dependencies separated by a colon \":\""); + "the classpath to use for fuzzing. Behaves analogously to java's " + "-cp (separator is ':' on Linux/macOS and ';' on Windows, escape " + "it with '\\')."); DEFINE_string(jvm_args, "", - "arguments passed to the jvm separated by semicolon \";\""); + "arguments passed to the JVM (separator is ':' on Linux/macOS " + "and ';' on Windows, escape it with '\\')"); DEFINE_string(additional_jvm_args, "", - "additional arguments passed to the jvm separated by semicolon " - "\";\". Use this option to set further JVM args that should not " + "additional arguments passed to the JVM (separator is ':' on " + "Linux/macOS and ';' on Windows). Use this option to set further " + "JVM args that should not " "interfere with those provided via --jvm_args."); DEFINE_string(agent_path, "", "location of the fuzzing instrumentation agent"); @@ -85,6 +88,12 @@ DEFINE_bool(hooks, true, "coverage information will be processed. This can be useful for " "running a regression test on non-instrumented bytecode."); +#ifdef _WIN32 +#define ARG_SEPARATOR ";" +#else +#define ARG_SEPARATOR ":" +#endif + // Called by the agent when // com.code_intelligence.jazzer.instrumentor.ClassInstrumentor is initialized. // This only happens when FLAGS_hooks is true. @@ -211,16 +220,44 @@ std::string agentArgsFromFlags() { return absl::StrJoin(args, ","); } +// Splits a string at the ARG_SEPARATOR unless it is escaped with a backslash. +// Backslash itself can be escaped with another backslash. +std::vector<std::string> splitEscaped(const std::string &str) { + // Protect \\ and \<separator> against splitting. + const std::string BACKSLASH_BACKSLASH_REPLACEMENT = + "%%JAZZER_BACKSLASH_BACKSLASH_REPLACEMENT%%"; + const std::string BACKSLASH_SEPARATOR_REPLACEMENT = + "%%JAZZER_BACKSLASH_SEPARATOR_REPLACEMENT%%"; + std::string protected_str = + absl::StrReplaceAll(str, {{"\\\\", BACKSLASH_BACKSLASH_REPLACEMENT}}); + protected_str = absl::StrReplaceAll( + protected_str, {{"\\" ARG_SEPARATOR, BACKSLASH_SEPARATOR_REPLACEMENT}}); + + std::vector<std::string> parts = absl::StrSplit(protected_str, ARG_SEPARATOR); + std::transform(parts.begin(), parts.end(), parts.begin(), + [&BACKSLASH_SEPARATOR_REPLACEMENT, + &BACKSLASH_BACKSLASH_REPLACEMENT](const std::string &part) { + return absl::StrReplaceAll( + part, + { + {BACKSLASH_SEPARATOR_REPLACEMENT, ARG_SEPARATOR}, + {BACKSLASH_BACKSLASH_REPLACEMENT, "\\"}, + }); + }); + + return parts; +} + JVM::JVM(const std::string &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); const auto class_path_from_env = std::getenv("JAVA_FUZZER_CLASSPATH"); if (class_path_from_env) { - class_path += absl::StrFormat(":%s", class_path_from_env); + class_path += absl::StrFormat(ARG_SEPARATOR "%s", class_path_from_env); } - class_path += - absl::StrFormat(":%s", getInstrumentorAgentPath(executable_path)); + class_path += absl::StrFormat(ARG_SEPARATOR "%s", + getInstrumentorAgentPath(executable_path)); LOG(INFO) << "got class path " << class_path; std::vector<JavaVMOption> options; @@ -240,7 +277,7 @@ JVM::JVM(const std::string &executable_path) { // add additional jvm options set through command line flags std::vector<std::string> jvm_args; if (!FLAGS_jvm_args.empty()) { - jvm_args = absl::StrSplit(FLAGS_jvm_args, ';'); + jvm_args = splitEscaped(FLAGS_jvm_args); } for (const auto &arg : jvm_args) { options.push_back( @@ -248,7 +285,7 @@ JVM::JVM(const std::string &executable_path) { } std::vector<std::string> additional_jvm_args; if (!FLAGS_additional_jvm_args.empty()) { - additional_jvm_args = absl::StrSplit(FLAGS_additional_jvm_args, ';'); + additional_jvm_args = splitEscaped(FLAGS_additional_jvm_args); } for (const auto &arg : additional_jvm_args) { options.push_back( diff --git a/driver/jvm_tooling_test.cpp b/driver/jvm_tooling_test.cpp index 9d13f91f..f2e8c66a 100644 --- a/driver/jvm_tooling_test.cpp +++ b/driver/jvm_tooling_test.cpp @@ -27,6 +27,12 @@ DECLARE_string(target_args); DECLARE_string(agent_path); DECLARE_string(instrumentation_excludes); +#ifdef _WIN32 +#define ARG_SEPARATOR ";" +#else +#define ARG_SEPARATOR ":" +#endif + namespace jazzer { std::vector<std::string> splitOnSpace(const std::string &s); @@ -42,7 +48,8 @@ class JvmToolingTest : public ::testing::Test { // process, so we set up a single JVM instance for this test binary which gets // destroyed after all tests in this test suite have finished. static void SetUpTestCase() { - FLAGS_jvm_args = "-Denv1=val1;-Denv2=val2"; + FLAGS_jvm_args = + "-Denv1=va\\" ARG_SEPARATOR "l1\\\\" ARG_SEPARATOR "-Denv2=val2"; FLAGS_instrumentation_excludes = "**"; using ::bazel::tools::cpp::runfiles::Runfiles; Runfiles *runfiles = Runfiles::CreateForTest(); @@ -79,7 +86,9 @@ TEST_F(JvmToolingTest, JniProperties) { auto &env = jvm_->GetEnv(); for (const auto &el : std::vector<std::pair<std::string, std::string>>{ - {"not set property", ""}, {"env1", "val1"}, {"env2", "val2"}}) { + {"not set property", ""}, + {"env1", "va" ARG_SEPARATOR "l1\\"}, + {"env2", "val2"}}) { jstring str = env.NewStringUTF(el.first.c_str()); auto ret = (jstring)env.CallStaticObjectMethod(property_printer_class, method_id, str); diff --git a/examples/BUILD.bazel b/examples/BUILD.bazel index 060b0b6c..194b4b5e 100644 --- a/examples/BUILD.bazel +++ b/examples/BUILD.bazel @@ -97,9 +97,11 @@ java_fuzz_target_test( ], fuzzer_args = [ "-fork=5", - "--jvm_args=-Dfoo=foo;-Dbar=bar", "--additional_jvm_args=-Dbaz=baz", - ], + ] + select({ + "@platforms//os:windows": ["--jvm_args=-Dfoo=foo;-Dbar=bar"], + "//conditions:default": ["--jvm_args=-Dfoo=foo:-Dbar=bar"], + }), target_class = "com.example.JpegImageParserFuzzer", # The exit codes of the forked libFuzzer processes are not picked up correctly. target_compatible_with = SKIP_ON_MACOS, diff --git a/repositories.bzl b/repositories.bzl index 58ef4dd5..01861cc2 100644 --- a/repositories.bzl +++ b/repositories.bzl @@ -140,7 +140,7 @@ def jazzer_dependencies(): http_archive, name = "jazzer_libfuzzer", build_file = "@jazzer//third_party:libFuzzer.BUILD", - sha256 = "1c6fd44cf6ea5f70f79fba94a86ff3cd010b9e25f2166b7d55143acd21ce6f08", - strip_prefix = "llvm-project-jazzer-b9c07e9c1e75857901808c13101c909104c413a8/compiler-rt/lib/fuzzer", - url = "https://github.com/CodeIntelligenceTesting/llvm-project-jazzer/archive/b9c07e9c1e75857901808c13101c909104c413a8.tar.gz", + sha256 = "562a00f14b634c376b1cf7dafa0192379c7d88ffbb1d7cb7a1599b302a648b69", + strip_prefix = "llvm-project-jazzer-4cace44fb775207a37ad35583b3ea0cc067f111b/compiler-rt/lib/fuzzer", + url = "https://github.com/CodeIntelligenceTesting/llvm-project-jazzer/archive/4cace44fb775207a37ad35583b3ea0cc067f111b.tar.gz", ) |