aboutsummaryrefslogtreecommitdiff
path: root/driver/jvm_tooling.cpp
diff options
context:
space:
mode:
authorFabian Meumertzheim <fabian@meumertzhe.im>2021-10-15 16:34:06 +0200
committerFabian Meumertzheim <fabian@meumertzhe.im>2021-10-18 22:31:42 +0200
commit24069c388579f54ec9872e61efa44f5e6065f838 (patch)
tree606965512f8c1632f7f7c74d23f92f4d2b98daf9 /driver/jvm_tooling.cpp
parent818a5033a26d45c2ba4114efc9f4bff4b222edc1 (diff)
downloadjazzer-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.cpp59
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(