aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--driver/jvm_tooling.cpp59
-rw-r--r--driver/jvm_tooling_test.cpp13
-rw-r--r--examples/BUILD.bazel6
-rw-r--r--repositories.bzl6
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",
)