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 | |
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')
-rw-r--r-- | driver/jvm_tooling.cpp | 59 | ||||
-rw-r--r-- | driver/jvm_tooling_test.cpp | 13 |
2 files changed, 59 insertions, 13 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); |