diff options
Diffstat (limited to 'driver/libfuzzer_callbacks.cpp')
-rw-r--r-- | driver/libfuzzer_callbacks.cpp | 411 |
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 |