aboutsummaryrefslogtreecommitdiff
path: root/driver/jazzer_main.cpp
blob: c72e111fcae3d26c943b09c28235db6fd42ccdf9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
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));
}