aboutsummaryrefslogtreecommitdiff
path: root/driver/libfuzzer_fuzz_target.cpp
blob: 15c7d3584406653918d20f547e6dc5aed21ce53d (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
// 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.

#include <iostream>

#include "com_code_intelligence_jazzer_driver_FuzzTargetRunner.h"
#include "libfuzzer_driver.h"

namespace {
bool is_asan_active = false;
}

extern "C" {
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";
}

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 {
using Driver = jazzer::LibfuzzerDriver;

std::unique_ptr<Driver> gLibfuzzerDriver;
}  // namespace

extern "C" void driver_cleanup() {
  // Free the libfuzzer driver which triggers a clean JVM shutdown.
  gLibfuzzerDriver.reset(nullptr);
}

// This symbol is defined by sanitizers if linked into Jazzer or in
// sanitizer_symbols.cpp if no sanitizer is used.
extern "C" void __sanitizer_set_death_callback(void (*)());

// We apply a patch to libFuzzer to make it call this function instead of
// __sanitizer_set_death_callback to pass us the death callback.
extern "C" [[maybe_unused]] void __jazzer_set_death_callback(
    void (*callback)()) {
  jazzer::AbstractLibfuzzerDriver::libfuzzer_print_crashing_input_ = callback;
  __sanitizer_set_death_callback([]() {
    jazzer::DumpJvmStackTraces();
    jazzer::AbstractLibfuzzerDriver::libfuzzer_print_crashing_input_();
    // Ideally, we would be able to call driver_cleanup here to perform a
    // graceful shutdown of the JVM. However, doing this directly results in a
    // nested bug report by ASan or UBSan, likely because something about the
    // stack/thread context in which they generate reports is incompatible with
    // the JVM shutdown process. use_sigaltstack=0 does not help though, so this
    // might be on us. The alternative of calling driver_cleanup in a new thread
    // and joining on it results in an endless wait in DestroyJavaVM, even when
    // the main thread is detached beforehand - it is not clear why.
  });
}

void Java_com_code_1intelligence_jazzer_driver_FuzzTargetRunner_printCrashingInput(
    JNIEnv *, jclass) {
  jazzer::AbstractLibfuzzerDriver::libfuzzer_print_crashing_input_();
}

void Java_com_code_1intelligence_jazzer_driver_FuzzTargetRunner__1Exit(
    JNIEnv *, jclass, jint exit_code) {
  _Exit(exit_code);
}

// Entry point called by libfuzzer before any LLVMFuzzerTestOneInput(...)
// invocations.
extern "C" int LLVMFuzzerInitialize(int *argc, char ***argv) {
  if (is_asan_active) {
    std::cerr << "WARN: Jazzer is not compatible with LeakSanitizer yet. Leaks "
                 "are not reported."
              << std::endl;
  }
  gLibfuzzerDriver = std::make_unique<Driver>(argc, argv);
  std::atexit(&driver_cleanup);
  return 0;
}

// Called by the fuzzer for every fuzzing input.
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, const size_t size) {
  return gLibfuzzerDriver->TestOneInput(data, size);
}