aboutsummaryrefslogtreecommitdiff
path: root/driver/jazzer_main.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'driver/jazzer_main.cpp')
-rw-r--r--driver/jazzer_main.cpp151
1 files changed, 151 insertions, 0 deletions
diff --git a/driver/jazzer_main.cpp b/driver/jazzer_main.cpp
new file mode 100644
index 00000000..c72e111f
--- /dev/null
+++ b/driver/jazzer_main.cpp
@@ -0,0 +1,151 @@
+// Copyright 2021 Code Intelligence GmbH
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+/*
+ * Jazzer's native main function, which:
+ * 1. defines default settings for ASan and UBSan;
+ * 2. preprocesses the command-line arguments passed to libFuzzer;
+ * 3. starts a JVM;
+ * 4. passes control to the Java-part of the driver.
+ */
+
+#include <rules_jni.h>
+
+#include <algorithm>
+#include <iostream>
+#include <memory>
+#include <vector>
+
+#include "absl/strings/match.h"
+#include "gflags/gflags.h"
+#include "jvm_tooling.h"
+
+// Defined by glog
+DECLARE_bool(log_prefix);
+
+namespace {
+bool is_asan_active = false;
+}
+
+extern "C" {
+[[maybe_unused]] const char *__asan_default_options() {
+ is_asan_active = true;
+ // LeakSanitizer is not yet supported as it reports too many false positives
+ // due to how the JVM GC works.
+ // We use a distinguished exit code to recognize ASan crashes in tests.
+ // Also specify abort_on_error=0 explicitly since ASan aborts rather than
+ // exits on macOS by default, which would cause our exit code to be ignored.
+ return "abort_on_error=0,detect_leaks=0,exitcode=76";
+}
+
+[[maybe_unused]] const char *__ubsan_default_options() {
+ // We use a distinguished exit code to recognize UBSan crashes in tests.
+ // Also specify abort_on_error=0 explicitly since UBSan aborts rather than
+ // exits on macOS by default, which would cause our exit code to be ignored.
+ return "abort_on_error=0,exitcode=76";
+}
+}
+
+namespace {
+const std::string kUsageMessage =
+ R"(Test java fuzz targets using libFuzzer. Usage:
+ jazzer --cp=<java_class_path> --target_class=<fuzz_target_class> <libfuzzer_arguments...>)";
+const std::string kDriverClassName =
+ "com/code_intelligence/jazzer/driver/Driver";
+
+int StartLibFuzzer(std::unique_ptr<jazzer::JVM> jvm,
+ std::vector<std::string> argv) {
+ JNIEnv &env = jvm->GetEnv();
+ jclass runner = env.FindClass(kDriverClassName.c_str());
+ if (runner == nullptr) {
+ env.ExceptionDescribe();
+ return 1;
+ }
+ jmethodID startDriver = env.GetStaticMethodID(runner, "start", "([[B)I");
+ if (startDriver == nullptr) {
+ env.ExceptionDescribe();
+ return 1;
+ }
+ jclass byteArrayClass = env.FindClass("[B");
+ if (byteArrayClass == nullptr) {
+ env.ExceptionDescribe();
+ return 1;
+ }
+ jobjectArray args = env.NewObjectArray(argv.size(), byteArrayClass, nullptr);
+ if (args == nullptr) {
+ env.ExceptionDescribe();
+ return 1;
+ }
+ for (jsize i = 0; i < argv.size(); ++i) {
+ jint len = argv[i].size();
+ jbyteArray arg = env.NewByteArray(len);
+ if (arg == nullptr) {
+ env.ExceptionDescribe();
+ return 1;
+ }
+ // startDriver expects UTF-8 encoded strings that are not null-terminated.
+ env.SetByteArrayRegion(arg, 0, len,
+ reinterpret_cast<const jbyte *>(argv[i].data()));
+ if (env.ExceptionCheck()) {
+ env.ExceptionDescribe();
+ return 1;
+ }
+ env.SetObjectArrayElement(args, i, arg);
+ if (env.ExceptionCheck()) {
+ env.ExceptionDescribe();
+ return 1;
+ }
+ env.DeleteLocalRef(arg);
+ }
+ int res = env.CallStaticIntMethod(runner, startDriver, args);
+ if (env.ExceptionCheck()) {
+ env.ExceptionDescribe();
+ return 1;
+ }
+ env.DeleteLocalRef(args);
+ return res;
+}
+} // namespace
+
+int main(int argc, char **argv) {
+ gflags::SetUsageMessage(kUsageMessage);
+ rules_jni_init(argv[0]);
+
+ const auto argv_end = argv + argc;
+
+ {
+ // All libFuzzer flags start with a single dash, our arguments all start
+ // with a double dash. We can thus filter out the arguments meant for gflags
+ // by taking only those with a leading double dash.
+ std::vector<char *> our_args = {*argv};
+ std::copy_if(argv, argv_end, std::back_inserter(our_args),
+ [](const std::string &arg) {
+ return absl::StartsWith(std::string(arg), "--");
+ });
+ int our_argc = our_args.size();
+ char **our_argv = our_args.data();
+ // Let gflags consume its flags, but keep them in the argument list in case
+ // libFuzzer forwards the command line (e.g. with -jobs or -minimize_crash).
+ gflags::ParseCommandLineFlags(&our_argc, &our_argv, false);
+ }
+
+ if (is_asan_active) {
+ std::cerr << "WARN: Jazzer is not compatible with LeakSanitizer yet. Leaks "
+ "are not reported."
+ << std::endl;
+ }
+
+ return StartLibFuzzer(std::unique_ptr<jazzer::JVM>(new jazzer::JVM(argv[0])),
+ std::vector<std::string>(argv, argv_end));
+}