aboutsummaryrefslogtreecommitdiff
path: root/driver/libfuzzer_callbacks.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'driver/libfuzzer_callbacks.cpp')
-rw-r--r--driver/libfuzzer_callbacks.cpp411
1 files changed, 0 insertions, 411 deletions
diff --git a/driver/libfuzzer_callbacks.cpp b/driver/libfuzzer_callbacks.cpp
deleted file mode 100644
index 5b7813dd..00000000
--- a/driver/libfuzzer_callbacks.cpp
+++ /dev/null
@@ -1,411 +0,0 @@
-// 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 "libfuzzer_callbacks.h"
-
-#include <jni.h>
-
-#include <fstream>
-#include <iostream>
-#include <mutex>
-#include <utility>
-#include <vector>
-
-#include "absl/strings/match.h"
-#include "absl/strings/str_format.h"
-#include "absl/strings/str_split.h"
-#include "gflags/gflags.h"
-#include "glog/logging.h"
-#include "sanitizer_hooks_with_pc.h"
-
-DEFINE_bool(
- fake_pcs, false,
- "Supply synthetic Java program counters to libFuzzer trace hooks to "
- "make value profiling more effective. Enabled by default if "
- "-use_value_profile=1 is specified.");
-
-namespace {
-
-const char kLibfuzzerTraceDataFlowHooksClass[] =
- "com/code_intelligence/jazzer/runtime/"
- "TraceDataFlowNativeCallbacks";
-
-extern "C" {
-void __sanitizer_weak_hook_memcmp(void *caller_pc, const void *s1,
- const void *s2, std::size_t n, int result);
-void __sanitizer_weak_hook_compare_bytes(void *caller_pc, const void *s1,
- const void *s2, std::size_t n1,
- std::size_t n2, int result);
-void __sanitizer_weak_hook_strcmp(void *caller_pc, const char *s1,
- const char *s2, int result);
-void __sanitizer_weak_hook_strstr(void *caller_pc, const char *s1,
- const char *s2, const char *result);
-void __sanitizer_cov_trace_cmp4(uint32_t arg1, uint32_t arg2);
-void __sanitizer_cov_trace_cmp8(uint64_t arg1, uint64_t arg2);
-
-void __sanitizer_cov_trace_switch(uint64_t val, uint64_t *cases);
-
-void __sanitizer_cov_trace_div4(uint32_t val);
-void __sanitizer_cov_trace_div8(uint64_t val);
-
-void __sanitizer_cov_trace_gep(uintptr_t idx);
-}
-
-inline __attribute__((always_inline)) void *idToPc(jint id) {
- return reinterpret_cast<void *>(static_cast<uintptr_t>(id));
-}
-
-void JNICALL libfuzzerStringCompareCallback(JNIEnv &env, jclass cls, jstring s1,
- jstring s2, jint result, jint id) {
- const char *s1_native = env.GetStringUTFChars(s1, nullptr);
- if (env.ExceptionCheck()) env.ExceptionDescribe();
- std::size_t n1 = env.GetStringUTFLength(s1);
- if (env.ExceptionCheck()) env.ExceptionDescribe();
- const char *s2_native = env.GetStringUTFChars(s2, nullptr);
- if (env.ExceptionCheck()) env.ExceptionDescribe();
- std::size_t n2 = env.GetStringUTFLength(s2);
- if (env.ExceptionCheck()) env.ExceptionDescribe();
- __sanitizer_weak_hook_compare_bytes(idToPc(id), s1_native, s2_native, n1, n2,
- result);
- env.ReleaseStringUTFChars(s1, s1_native);
- if (env.ExceptionCheck()) env.ExceptionDescribe();
- env.ReleaseStringUTFChars(s2, s2_native);
- if (env.ExceptionCheck()) env.ExceptionDescribe();
-}
-
-void JNICALL libfuzzerStringContainCallback(JNIEnv &env, jclass cls, jstring s1,
- jstring s2, jint id) {
- const char *s1_native = env.GetStringUTFChars(s1, nullptr);
- if (env.ExceptionCheck()) env.ExceptionDescribe();
- const char *s2_native = env.GetStringUTFChars(s2, nullptr);
- if (env.ExceptionCheck()) env.ExceptionDescribe();
- // libFuzzer currently ignores the result, which allows us to simply pass a
- // valid but arbitrary pointer here instead of performing an actual strstr
- // operation.
- __sanitizer_weak_hook_strstr(idToPc(id), s1_native, s2_native, s1_native);
- env.ReleaseStringUTFChars(s1, s1_native);
- if (env.ExceptionCheck()) env.ExceptionDescribe();
- env.ReleaseStringUTFChars(s2, s2_native);
- if (env.ExceptionCheck()) env.ExceptionDescribe();
-}
-
-void JNICALL libfuzzerByteCompareCallback(JNIEnv &env, jclass cls,
- jbyteArray b1, jbyteArray b2,
- jint result, jint id) {
- jbyte *b1_native = env.GetByteArrayElements(b1, nullptr);
- if (env.ExceptionCheck()) env.ExceptionDescribe();
- jbyte *b2_native = env.GetByteArrayElements(b2, nullptr);
- if (env.ExceptionCheck()) env.ExceptionDescribe();
- jint b1_length = env.GetArrayLength(b1);
- if (env.ExceptionCheck()) env.ExceptionDescribe();
- jint b2_length = env.GetArrayLength(b2);
- if (env.ExceptionCheck()) env.ExceptionDescribe();
- __sanitizer_weak_hook_compare_bytes(idToPc(id), b1_native, b2_native,
- b1_length, b2_length, result);
- env.ReleaseByteArrayElements(b1, b1_native, JNI_ABORT);
- if (env.ExceptionCheck()) env.ExceptionDescribe();
- env.ReleaseByteArrayElements(b2, b2_native, JNI_ABORT);
- if (env.ExceptionCheck()) env.ExceptionDescribe();
-}
-
-void JNICALL libfuzzerLongCompareCallback(JNIEnv &env, jclass cls, jlong value1,
- jlong value2, jint id) {
- __sanitizer_cov_trace_cmp8(value1, value2);
-}
-
-void JNICALL libfuzzerLongCompareCallbackWithPc(JNIEnv &env, jclass cls,
- jlong value1, jlong value2,
- jint id) {
- __sanitizer_cov_trace_cmp8_with_pc(idToPc(id), value1, value2);
-}
-
-void JNICALL libfuzzerIntCompareCallback(JNIEnv &env, jclass cls, jint value1,
- jint value2, jint id) {
- __sanitizer_cov_trace_cmp4(value1, value2);
-}
-
-void JNICALL libfuzzerIntCompareCallbackWithPc(JNIEnv &env, jclass cls,
- jint value1, jint value2,
- jint id) {
- __sanitizer_cov_trace_cmp4_with_pc(idToPc(id), value1, value2);
-}
-
-void JNICALL libfuzzerSwitchCaseCallback(JNIEnv &env, jclass cls,
- jlong switch_value,
- jlongArray libfuzzer_case_values,
- jint id) {
- jlong *case_values = env.GetLongArrayElements(libfuzzer_case_values, nullptr);
- if (env.ExceptionCheck()) env.ExceptionDescribe();
- __sanitizer_cov_trace_switch(switch_value,
- reinterpret_cast<uint64_t *>(case_values));
- env.ReleaseLongArrayElements(libfuzzer_case_values, case_values, JNI_ABORT);
- if (env.ExceptionCheck()) env.ExceptionDescribe();
-}
-
-void JNICALL libfuzzerSwitchCaseCallbackWithPc(JNIEnv &env, jclass cls,
- jlong switch_value,
- jlongArray libfuzzer_case_values,
- jint id) {
- jlong *case_values = env.GetLongArrayElements(libfuzzer_case_values, nullptr);
- if (env.ExceptionCheck()) env.ExceptionDescribe();
- __sanitizer_cov_trace_switch_with_pc(
- idToPc(id), switch_value, reinterpret_cast<uint64_t *>(case_values));
- env.ReleaseLongArrayElements(libfuzzer_case_values, case_values, JNI_ABORT);
- if (env.ExceptionCheck()) env.ExceptionDescribe();
-}
-
-void JNICALL libfuzzerLongDivCallback(JNIEnv &env, jclass cls, jlong value,
- jint id) {
- __sanitizer_cov_trace_div8(value);
-}
-
-void JNICALL libfuzzerLongDivCallbackWithPc(JNIEnv &env, jclass cls,
- jlong value, jint id) {
- __sanitizer_cov_trace_div8_with_pc(idToPc(id), value);
-}
-
-void JNICALL libfuzzerIntDivCallback(JNIEnv &env, jclass cls, jint value,
- jint id) {
- __sanitizer_cov_trace_div4(value);
-}
-
-void JNICALL libfuzzerIntDivCallbackWithPc(JNIEnv &env, jclass cls, jint value,
- jint id) {
- __sanitizer_cov_trace_div4_with_pc(idToPc(id), value);
-}
-
-void JNICALL libfuzzerGepCallback(JNIEnv &env, jclass cls, jlong idx, jint id) {
- __sanitizer_cov_trace_gep(static_cast<uintptr_t>(idx));
-}
-
-void JNICALL libfuzzerGepCallbackWithPc(JNIEnv &env, jclass cls, jlong idx,
- jint id) {
- __sanitizer_cov_trace_gep_with_pc(idToPc(id), static_cast<uintptr_t>(idx));
-}
-
-void JNICALL libfuzzerPcIndirCallback(JNIEnv &env, jclass cls, jint caller_id,
- jint callee_id) {
- __sanitizer_cov_trace_pc_indir_with_pc(idToPc(caller_id),
- static_cast<uintptr_t>(callee_id));
-}
-
-bool is_using_native_libraries = false;
-std::once_flag ignore_list_flag;
-std::vector<std::pair<uintptr_t, uintptr_t>> ignore_for_interception_ranges;
-
-extern "C" [[maybe_unused]] bool __sanitizer_weak_is_relevant_pc(
- void *caller_pc) {
- // If the fuzz target is not using native libraries, calls to strcmp, memcmp,
- // etc. should never be intercepted. The values reported if they were at best
- // duplicate the values received from our bytecode instrumentation and at
- // worst pollute the table of recent compares with string internal to the JDK.
- if (!is_using_native_libraries) return false;
- // If the fuzz target is using native libraries, intercept calls only if they
- // don't originate from those address ranges that are known to belong to the
- // JDK.
- return std::none_of(ignore_for_interception_ranges.cbegin(),
- ignore_for_interception_ranges.cend(),
- [caller_pc](const auto &range) {
- uintptr_t start;
- uintptr_t end;
- std::tie(start, end) = range;
- auto address = reinterpret_cast<uintptr_t>(caller_pc);
- return start <= address && address <= end;
- });
-}
-
-/**
- * Adds the address ranges of executable segmentes of the library lib_name to
- * the ignorelist for C standard library function interception (strcmp, memcmp,
- * ...).
- */
-void ignoreLibraryForInterception(const std::string &lib_name) {
- const auto num_address_ranges = ignore_for_interception_ranges.size();
- std::ifstream loaded_libs("/proc/self/maps");
- if (!loaded_libs) {
- // This early exit is taken e.g. on macOS, where /proc does not exist.
- return;
- }
- std::string line;
- while (std::getline(loaded_libs, line)) {
- if (!absl::StrContains(line, lib_name)) continue;
- // clang-format off
- // A typical line looks as follows:
- // 7f15356c9000-7f1536367000 r-xp 0020d000 fd:01 19275673 /usr/lib/jvm/java-15-openjdk-amd64/lib/server/libjvm.so
- // clang-format on
- std::vector<std::string_view> parts =
- absl::StrSplit(line, ' ', absl::SkipEmpty());
- if (parts.size() != 6) {
- std::cout << "ERROR: Invalid format for /proc/self/maps\n"
- << line << std::endl;
- exit(1);
- }
- // Skip non-executable address rang"s.
- if (!absl::StrContains(parts[1], "x")) continue;
- std::string_view range_str = parts[0];
- std::vector<std::string> range = absl::StrSplit(range_str, "-");
- if (range.size() != 2) {
- std::cout
- << "ERROR: Unexpected address range format in /proc/self/maps line: "
- << range_str << std::endl;
- exit(1);
- }
- std::size_t pos;
- auto start = std::stoull(range[0], &pos, 16);
- if (pos != range[0].size()) {
- std::cout
- << "ERROR: Unexpected address range format in /proc/self/maps line: "
- << range_str << std::endl;
- exit(1);
- }
- auto end = std::stoull(range[1], &pos, 16);
- if (pos != range[0].size()) {
- std::cout
- << "ERROR: Unexpected address range format in /proc/self/maps line: "
- << range_str << std::endl;
- exit(1);
- }
- ignore_for_interception_ranges.emplace_back(start, end);
- }
- const auto num_code_segments =
- ignore_for_interception_ranges.size() - num_address_ranges;
- LOG(INFO) << "added " << num_code_segments
- << " code segment of native library " << lib_name
- << " to interceptor ignorelist";
-}
-
-const std::vector<std::string> kLibrariesToIgnoreForInterception = {
- // The driver executable itself can be treated just like a library.
- "jazzer_driver", "libinstrument.so", "libjava.so",
- "libjimage.so", "libjli.so", "libjvm.so",
- "libnet.so", "libverify.so", "libzip.so",
-};
-
-void JNICALL handleLibraryLoad(JNIEnv &env, jclass cls) {
- std::call_once(ignore_list_flag, [] {
- LOG(INFO)
- << "detected a native library load, enabling interception for libc "
- "functions";
- for (const auto &lib_name : kLibrariesToIgnoreForInterception)
- ignoreLibraryForInterception(lib_name);
- // Enable the ignore list after it has been populated since vector is not
- // thread-safe with respect to concurrent writes and reads.
- is_using_native_libraries = true;
- });
-}
-
-void registerCallback(JNIEnv &env, const char *java_hooks_class_name,
- const JNINativeMethod *methods, int num_methods) {
- auto java_hooks_class = env.FindClass(java_hooks_class_name);
- if (java_hooks_class == nullptr) {
- env.ExceptionDescribe();
- throw std::runtime_error(
- absl::StrFormat("could not find class %s", java_hooks_class_name));
- }
- LOG(INFO) << "registering hooks for class " << java_hooks_class_name;
- env.RegisterNatives(java_hooks_class, methods, num_methods);
- if (env.ExceptionCheck()) {
- env.ExceptionDescribe();
- throw std::runtime_error("could not register native callbacks");
- }
-}
-} // namespace
-
-namespace jazzer {
-
-bool registerFuzzerCallbacks(JNIEnv &env) {
- if (FLAGS_fake_pcs) {
- LOG(INFO) << "using callback variants with fake pcs";
- CalibrateTrampoline();
- }
- {
- JNINativeMethod string_methods[]{
- {(char *)"traceMemcmp", (char *)"([B[BII)V",
- (void *)&libfuzzerByteCompareCallback},
- {(char *)"traceStrcmp",
- (char *)"(Ljava/lang/String;Ljava/lang/String;II)V",
- (void *)&libfuzzerStringCompareCallback},
- {(char *)"traceStrstr",
- (char *)"(Ljava/lang/String;Ljava/lang/String;I)V",
- (void *)&libfuzzerStringContainCallback}};
-
- registerCallback(env, kLibfuzzerTraceDataFlowHooksClass, string_methods,
- sizeof(string_methods) / sizeof(string_methods[0]));
- }
-
- {
- JNINativeMethod cmp_methods[]{
- {(char *)"traceCmpLong", (char *)"(JJI)V",
- (void *)(FLAGS_fake_pcs ? &libfuzzerLongCompareCallbackWithPc
- : &libfuzzerLongCompareCallback)},
- {(char *)"traceCmpInt", (char *)"(III)V",
- (void *)(FLAGS_fake_pcs ? &libfuzzerIntCompareCallbackWithPc
- : &libfuzzerIntCompareCallback)},
- // libFuzzer internally treats const comparisons the same as
- // non-constant cmps.
- {(char *)"traceConstCmpInt", (char *)"(III)V",
- (void *)(FLAGS_fake_pcs ? &libfuzzerIntCompareCallbackWithPc
- : &libfuzzerIntCompareCallback)},
- {(char *)"traceSwitch", (char *)"(J[JI)V",
- (void *)(FLAGS_fake_pcs ? &libfuzzerSwitchCaseCallbackWithPc
- : &libfuzzerSwitchCaseCallback)}};
-
- registerCallback(env, kLibfuzzerTraceDataFlowHooksClass, cmp_methods,
- sizeof(cmp_methods) / sizeof(cmp_methods[0]));
- }
-
- {
- JNINativeMethod div_methods[]{
- {(char *)"traceDivLong", (char *)"(JI)V",
- (void *)(FLAGS_fake_pcs ? &libfuzzerLongDivCallbackWithPc
- : &libfuzzerLongDivCallback)},
- {(char *)"traceDivInt", (char *)"(II)V",
- (void *)(FLAGS_fake_pcs ? &libfuzzerIntDivCallbackWithPc
- : &libfuzzerIntDivCallback)}};
-
- registerCallback(env, kLibfuzzerTraceDataFlowHooksClass, div_methods,
- sizeof(div_methods) / sizeof(div_methods[0]));
- }
-
- {
- JNINativeMethod gep_methods[]{
- {(char *)"traceGep", (char *)"(JI)V",
- (void *)(FLAGS_fake_pcs ? &libfuzzerGepCallbackWithPc
- : &libfuzzerGepCallback)}};
-
- registerCallback(env, kLibfuzzerTraceDataFlowHooksClass, gep_methods,
- sizeof(gep_methods) / sizeof(gep_methods[0]));
- }
-
- {
- JNINativeMethod indir_methods[]{{(char *)"tracePcIndir", (char *)"(II)V",
- (void *)(&libfuzzerPcIndirCallback)}};
-
- registerCallback(env, kLibfuzzerTraceDataFlowHooksClass, indir_methods,
- sizeof(indir_methods) / sizeof(indir_methods[0]));
- }
-
- {
- JNINativeMethod native_methods[]{{(char *)"handleLibraryLoad",
- (char *)"()V",
- (void *)(&handleLibraryLoad)}};
-
- registerCallback(env, kLibfuzzerTraceDataFlowHooksClass, native_methods,
- sizeof(native_methods) / sizeof(native_methods[0]));
- }
-
- return env.ExceptionCheck();
-}
-
-} // namespace jazzer