diff options
author | Fabian Meumertzheim <fabian@meumertzhe.im> | 2021-10-15 16:34:06 +0200 |
---|---|---|
committer | Fabian Meumertzheim <fabian@meumertzhe.im> | 2021-10-18 22:31:42 +0200 |
commit | 24069c388579f54ec9872e61efa44f5e6065f838 (patch) | |
tree | 606965512f8c1632f7f7c74d23f92f4d2b98daf9 /driver/jvm_tooling.cpp | |
parent | 818a5033a26d45c2ba4114efc9f4bff4b222edc1 (diff) | |
download | jazzer-api-24069c388579f54ec9872e61efa44f5e6065f838.tar.gz |
BREAKING: Use OS-specific classpath separator to split jvm_args
This allows us to get rid of the libFuzzer patch to quote argv, which
breaks Windows and would have to be turned into a conditional patch
otherwise.
With this change, all argument separators use the same syntax as the
java executable on any given platform.
Collisions of ':' as an argument separator and a classpath separator
should be rare as we have our own argument to pass in the classpath.
This commit implements a rudimentary escaping mechanism to handle the
cases where this should be an issue:
* \<separator> results in a literal <separator> and no split
* \\ results in a literal backlash not interpreted as an escape
character for a subsequent <separator>
This is a breaking change, but we should rather break this now that we
haven't committed to a version 1.0 yet. The only breakage I am aware of
is easily fixed:
https://github.com/google/oss-fuzz/blob/0c1d5231de61b4166281b4d5685aa66e9f4948a8/projects/kryo/build.sh#L48
Diffstat (limited to 'driver/jvm_tooling.cpp')
-rw-r--r-- | driver/jvm_tooling.cpp | 59 |
1 files changed, 48 insertions, 11 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( |