// 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 #include #include #include #include #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= --target_class= )"; const std::string kDriverClassName = "com/code_intelligence/jazzer/driver/Driver"; int StartLibFuzzer(std::unique_ptr jvm, std::vector 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(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 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(new jazzer::JVM(argv[0])), std::vector(argv, argv_end)); }