// Copyright 2022 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. /* * Dynamically exported definitions of fuzzer hooks and libc functions that * forward to the symbols provided by the Jazzer driver JNI library once it has * been loaded. * * Native libraries instrumented for fuzzing include references to fuzzer hooks * that are resolved by the dynamic linker. Sanitizers such as ASan provide weak * definitions of these symbols, but the dynamic linker doesn't distinguish * between weak and strong symbols and thus wouldn't ever resolve them against * the strong definitions provided by the Jazzer driver JNI library. * Furthermore, libc functions can only be overridden in the native driver * executable, which is the only binary that comes before the actual libc in the * dynamic linker search order. */ #define _GNU_SOURCE // for RTLD_NEXT #include #include #include #include #define GET_CALLER_PC() __builtin_return_address(0) #define LIKELY(x) __builtin_expect(!!(x), 1) #define UNLIKELY(x) __builtin_expect(!!(x), 0) typedef int (*bcmp_t)(const void *, const void *, size_t); static _Atomic bcmp_t bcmp_real; typedef void (*bcmp_hook_t)(void *, const void *, const void *, size_t, int); static _Atomic bcmp_hook_t bcmp_hook; typedef int (*memcmp_t)(const void *, const void *, size_t); static _Atomic memcmp_t memcmp_real; typedef void (*memcmp_hook_t)(void *, const void *, const void *, size_t, int); static _Atomic memcmp_hook_t memcmp_hook; typedef int (*strncmp_t)(const char *, const char *, size_t); static _Atomic strncmp_t strncmp_real; typedef void (*strncmp_hook_t)(void *, const char *, const char *, size_t, int); static _Atomic strncmp_hook_t strncmp_hook; typedef int (*strcmp_t)(const char *, const char *); static _Atomic strcmp_t strcmp_real; typedef void (*strcmp_hook_t)(void *, const char *, const char *, int); static _Atomic strcmp_hook_t strcmp_hook; typedef int (*strncasecmp_t)(const char *, const char *, size_t); static _Atomic strncasecmp_t strncasecmp_real; typedef void (*strncasecmp_hook_t)(void *, const char *, const char *, size_t, int); static _Atomic strncasecmp_hook_t strncasecmp_hook; typedef int (*strcasecmp_t)(const char *, const char *); static _Atomic strcasecmp_t strcasecmp_real; typedef void (*strcasecmp_hook_t)(void *, const char *, const char *, int); static _Atomic strcasecmp_hook_t strcasecmp_hook; typedef char *(*strstr_t)(const char *, const char *); static _Atomic strstr_t strstr_real; typedef void (*strstr_hook_t)(void *, const char *, const char *, char *); static _Atomic strstr_hook_t strstr_hook; typedef char *(*strcasestr_t)(const char *, const char *); static _Atomic strcasestr_t strcasestr_real; typedef void (*strcasestr_hook_t)(void *, const char *, const char *, char *); static _Atomic strcasestr_hook_t strcasestr_hook; typedef void *(*memmem_t)(const void *, size_t, const void *, size_t); static _Atomic memmem_t memmem_real; typedef void (*memmem_hook_t)(void *, const void *, size_t, const void *, size_t, void *); static _Atomic memmem_hook_t memmem_hook; typedef void (*cov_8bit_counters_init_t)(uint8_t *, uint8_t *); static _Atomic cov_8bit_counters_init_t cov_8bit_counters_init; typedef void (*cov_pcs_init_t)(const uintptr_t *, const uintptr_t *); static _Atomic cov_pcs_init_t cov_pcs_init; typedef void (*trace_cmp1_t)(void *, uint8_t, uint8_t); static _Atomic trace_cmp1_t trace_cmp1_with_pc; typedef void (*trace_cmp2_t)(void *, uint16_t, uint16_t); static _Atomic trace_cmp2_t trace_cmp2_with_pc; typedef void (*trace_cmp4_t)(void *, uint32_t, uint32_t); static _Atomic trace_cmp4_t trace_cmp4_with_pc; typedef void (*trace_cmp8_t)(void *, uint64_t, uint64_t); static _Atomic trace_cmp8_t trace_cmp8_with_pc; typedef void (*trace_const_cmp1_t)(void *, uint8_t, uint8_t); static _Atomic trace_const_cmp1_t trace_const_cmp1_with_pc; typedef void (*trace_const_cmp2_t)(void *, uint16_t, uint16_t); static _Atomic trace_const_cmp2_t trace_const_cmp2_with_pc; typedef void (*trace_const_cmp4_t)(void *, uint32_t, uint32_t); static _Atomic trace_const_cmp4_t trace_const_cmp4_with_pc; typedef void (*trace_const_cmp8_t)(void *, uint64_t, uint64_t); static _Atomic trace_const_cmp8_t trace_const_cmp8_with_pc; typedef void (*trace_switch_t)(void *, uint64_t, uint64_t *); static _Atomic trace_switch_t trace_switch_with_pc; typedef void (*trace_div4_t)(void *, uint32_t); static _Atomic trace_div4_t trace_div4_with_pc; typedef void (*trace_div8_t)(void *, uint64_t); static _Atomic trace_div8_t trace_div8_with_pc; typedef void (*trace_gep_t)(void *, uintptr_t); static _Atomic trace_gep_t trace_gep_with_pc; typedef void (*trace_pc_indir_t)(void *, uintptr_t); static _Atomic trace_pc_indir_t trace_pc_indir_with_pc; __attribute__((visibility("default"))) void jazzer_initialize_native_hooks( void *handle) { atomic_store(&bcmp_hook, dlsym(handle, "__sanitizer_weak_hook_bcmp")); atomic_store(&memcmp_hook, dlsym(handle, "__sanitizer_weak_hook_memcmp")); atomic_store(&strncmp_hook, dlsym(handle, "__sanitizer_weak_hook_strncmp")); atomic_store(&strcmp_hook, dlsym(handle, "__sanitizer_weak_hook_strcmp")); atomic_store(&strncasecmp_hook, dlsym(handle, "__sanitizer_weak_hook_strncasecmp")); atomic_store(&strcasecmp_hook, dlsym(handle, "__sanitizer_weak_hook_strcasecmp")); atomic_store(&strstr_hook, dlsym(handle, "__sanitizer_weak_hook_strstr")); atomic_store(&strcasestr_hook, dlsym(handle, "__sanitizer_weak_hook_strcasestr")); atomic_store(&memmem_hook, dlsym(handle, "__sanitizer_weak_hook_memmem")); atomic_store(&cov_8bit_counters_init, dlsym(handle, "__sanitizer_cov_8bit_counters_init")); atomic_store(&cov_pcs_init, dlsym(handle, "__sanitizer_cov_pcs_init")); atomic_store(&trace_cmp1_with_pc, dlsym(handle, "__sanitizer_cov_trace_cmp1_with_pc")); atomic_store(&trace_cmp2_with_pc, dlsym(handle, "__sanitizer_cov_trace_cmp2_with_pc")); atomic_store(&trace_cmp4_with_pc, dlsym(handle, "__sanitizer_cov_trace_cmp4_with_pc")); atomic_store(&trace_cmp8_with_pc, dlsym(handle, "__sanitizer_cov_trace_cmp8_with_pc")); atomic_store(&trace_const_cmp1_with_pc, dlsym(handle, "__sanitizer_cov_trace_const_cmp1_with_pc")); atomic_store(&trace_const_cmp2_with_pc, dlsym(handle, "__sanitizer_cov_trace_const_cmp2_with_pc")); atomic_store(&trace_const_cmp4_with_pc, dlsym(handle, "__sanitizer_cov_trace_const_cmp4_with_pc")); atomic_store(&trace_const_cmp8_with_pc, dlsym(handle, "__sanitizer_cov_trace_const_cmp8_with_pc")); atomic_store(&trace_switch_with_pc, dlsym(handle, "__sanitizer_cov_trace_switch_with_pc")); atomic_store(&trace_div4_with_pc, dlsym(handle, "__sanitizer_cov_trace_div4_with_pc")); atomic_store(&trace_div8_with_pc, dlsym(handle, "__sanitizer_cov_trace_div8_with_pc")); atomic_store(&trace_gep_with_pc, dlsym(handle, "__sanitizer_cov_trace_gep_with_pc")); atomic_store(&trace_pc_indir_with_pc, dlsym(handle, "__sanitizer_cov_trace_pc_indir_with_pc")); } // Alternate definitions for libc functions mimicking those that libFuzzer would // provide if it were part of the native driver executable. All these functions // invoke the real libc function loaded from the next library in search order // (usually libc itself). // Function pointers have to be loaded and stored atomically even if libc // functions are invoked from different threads, but we do not need any // synchronization guarantees - in the worst case, we will non-deterministically // lose a few hook invocations. __attribute__((visibility("default"))) int bcmp(const void *s1, const void *s2, size_t n) { bcmp_t bcmp_real_local = atomic_load_explicit(&bcmp_real, memory_order_relaxed); if (UNLIKELY(bcmp_real_local == NULL)) { bcmp_real_local = dlsym(RTLD_NEXT, "bcmp"); atomic_store_explicit(&bcmp_real, bcmp_real_local, memory_order_relaxed); } int result = bcmp_real_local(s1, s2, n); bcmp_hook_t hook = atomic_load_explicit(&bcmp_hook, memory_order_relaxed); if (LIKELY(hook != NULL)) { hook(GET_CALLER_PC(), s1, s2, n, result); } return result; } __attribute__((visibility("default"))) int memcmp(const void *s1, const void *s2, size_t n) { memcmp_t memcmp_real_local = atomic_load_explicit(&memcmp_real, memory_order_relaxed); if (UNLIKELY(memcmp_real_local == NULL)) { memcmp_real_local = dlsym(RTLD_NEXT, "memcmp"); atomic_store_explicit(&memcmp_real, memcmp_real_local, memory_order_relaxed); } int result = memcmp_real_local(s1, s2, n); memcmp_hook_t hook = atomic_load_explicit(&memcmp_hook, memory_order_relaxed); if (LIKELY(hook != NULL)) { hook(GET_CALLER_PC(), s1, s2, n, result); } return result; } __attribute__((visibility("default"))) int strncmp(const char *s1, const char *s2, size_t n) { strncmp_t strncmp_real_local = atomic_load_explicit(&strncmp_real, memory_order_relaxed); if (UNLIKELY(strncmp_real_local == NULL)) { strncmp_real_local = dlsym(RTLD_NEXT, "strncmp"); atomic_store_explicit(&strncmp_real, strncmp_real_local, memory_order_relaxed); } int result = strncmp_real_local(s1, s2, n); strncmp_hook_t hook = atomic_load_explicit(&strncmp_hook, memory_order_relaxed); if (LIKELY(hook != NULL)) { hook(GET_CALLER_PC(), s1, s2, n, result); } return result; } __attribute__((visibility("default"))) int strncasecmp(const char *s1, const char *s2, size_t n) { strncasecmp_t strncasecmp_real_local = atomic_load_explicit(&strncasecmp_real, memory_order_relaxed); if (UNLIKELY(strncasecmp_real_local == NULL)) { strncasecmp_real_local = dlsym(RTLD_NEXT, "strncasecmp"); atomic_store_explicit(&strncasecmp_real, strncasecmp_real_local, memory_order_relaxed); } int result = strncasecmp_real_local(s1, s2, n); strncasecmp_hook_t hook = atomic_load_explicit(&strncasecmp_hook, memory_order_relaxed); if (LIKELY(hook != NULL)) { hook(GET_CALLER_PC(), s1, s2, n, result); } return result; } __attribute__((visibility("default"))) int strcmp(const char *s1, const char *s2) { strcmp_t strcmp_real_local = atomic_load_explicit(&strcmp_real, memory_order_relaxed); if (UNLIKELY(strcmp_real_local == NULL)) { strcmp_real_local = dlsym(RTLD_NEXT, "strcmp"); atomic_store_explicit(&strcmp_real, strcmp_real_local, memory_order_relaxed); } int result = strcmp_real_local(s1, s2); strcmp_hook_t hook = atomic_load_explicit(&strcmp_hook, memory_order_relaxed); if (LIKELY(hook != NULL)) { hook(GET_CALLER_PC(), s1, s2, result); } return result; } __attribute__((visibility("default"))) int strcasecmp(const char *s1, const char *s2) { strcasecmp_t strcasecmp_real_local = atomic_load_explicit(&strcasecmp_real, memory_order_relaxed); if (UNLIKELY(strcasecmp_real_local == NULL)) { strcasecmp_real_local = dlsym(RTLD_NEXT, "strcasecmp"); atomic_store_explicit(&strcasecmp_real, strcasecmp_real_local, memory_order_relaxed); } int result = strcasecmp_real_local(s1, s2); strcasecmp_hook_t hook = atomic_load_explicit(&strcasecmp_hook, memory_order_relaxed); if (LIKELY(hook != NULL)) { hook(GET_CALLER_PC(), s1, s2, result); } return result; } __attribute__((visibility("default"))) char *strstr(const char *s1, const char *s2) { strstr_t strstr_real_local = atomic_load_explicit(&strstr_real, memory_order_relaxed); if (UNLIKELY(strstr_real_local == NULL)) { strstr_real_local = dlsym(RTLD_NEXT, "strstr"); atomic_store_explicit(&strstr_real, strstr_real_local, memory_order_relaxed); } char *result = strstr_real_local(s1, s2); strstr_hook_t hook = atomic_load_explicit(&strstr_hook, memory_order_relaxed); if (LIKELY(hook != NULL)) { hook(GET_CALLER_PC(), s1, s2, result); } return result; } __attribute__((visibility("default"))) char *strcasestr(const char *s1, const char *s2) { strcasestr_t strcasestr_real_local = atomic_load_explicit(&strcasestr_real, memory_order_relaxed); if (UNLIKELY(strcasestr_real_local == NULL)) { strcasestr_real_local = dlsym(RTLD_NEXT, "strcasestr"); atomic_store_explicit(&strcasestr_real, strcasestr_real_local, memory_order_relaxed); } char *result = strcasestr_real_local(s1, s2); strcasestr_hook_t hook = atomic_load_explicit(&strcasestr_hook, memory_order_relaxed); if (LIKELY(hook != NULL)) { hook(GET_CALLER_PC(), s1, s2, result); } return result; } __attribute__((visibility("default"))) void *memmem(const void *s1, size_t n1, const void *s2, size_t n2) { memmem_t memmem_real_local = atomic_load_explicit(&memmem_real, memory_order_relaxed); if (UNLIKELY(memmem_real_local == NULL)) { memmem_real_local = dlsym(RTLD_NEXT, "memmem"); atomic_store_explicit(&memmem_real, memmem_real_local, memory_order_relaxed); } void *result = memmem_real_local(s1, n1, s2, n2); memmem_hook_t hook = atomic_load_explicit(&memmem_hook, memory_order_relaxed); if (LIKELY(hook != NULL)) { hook(GET_CALLER_PC(), s1, n1, s2, n2, result); } return result; } // The __sanitizer_cov_trace_* family of functions is only invoked from code // compiled with -fsanitize=fuzzer. We can assume that the Jazzer JNI library // has been loaded before any such code, which necessarily belongs to the fuzz // target, is executed and thus don't need NULL checks. __attribute__((visibility("default"))) void __sanitizer_cov_trace_cmp1( uint8_t arg1, uint8_t arg2) { trace_cmp1_t hook = atomic_load_explicit(&trace_cmp1_with_pc, memory_order_relaxed); hook(GET_CALLER_PC(), arg1, arg2); } __attribute__((visibility("default"))) void __sanitizer_cov_trace_cmp2( uint16_t arg1, uint16_t arg2) { trace_cmp2_t hook = atomic_load_explicit(&trace_cmp2_with_pc, memory_order_relaxed); hook(GET_CALLER_PC(), arg1, arg2); } __attribute__((visibility("default"))) void __sanitizer_cov_trace_cmp4( uint32_t arg1, uint32_t arg2) { trace_cmp4_t hook = atomic_load_explicit(&trace_cmp4_with_pc, memory_order_relaxed); hook(GET_CALLER_PC(), arg1, arg2); } __attribute__((visibility("default"))) void __sanitizer_cov_trace_cmp8( uint64_t arg1, uint64_t arg2) { trace_cmp8_t hook = atomic_load_explicit(&trace_cmp8_with_pc, memory_order_relaxed); hook(GET_CALLER_PC(), arg1, arg2); } __attribute__((visibility("default"))) void __sanitizer_cov_trace_const_cmp1( uint8_t arg1, uint8_t arg2) { trace_const_cmp1_t hook = atomic_load_explicit(&trace_const_cmp1_with_pc, memory_order_relaxed); hook(GET_CALLER_PC(), arg1, arg2); } __attribute__((visibility("default"))) void __sanitizer_cov_trace_const_cmp2( uint16_t arg1, uint16_t arg2) { trace_const_cmp2_t hook = atomic_load_explicit(&trace_const_cmp2_with_pc, memory_order_relaxed); hook(GET_CALLER_PC(), arg1, arg2); } __attribute__((visibility("default"))) void __sanitizer_cov_trace_const_cmp4( uint32_t arg1, uint32_t arg2) { trace_const_cmp4_t hook = atomic_load_explicit(&trace_const_cmp4_with_pc, memory_order_relaxed); hook(GET_CALLER_PC(), arg1, arg2); } __attribute__((visibility("default"))) void __sanitizer_cov_trace_const_cmp8( uint64_t arg1, uint64_t arg2) { trace_const_cmp8_t hook = atomic_load_explicit(&trace_const_cmp8_with_pc, memory_order_relaxed); hook(GET_CALLER_PC(), arg1, arg2); } __attribute__((visibility("default"))) void __sanitizer_cov_trace_switch( uint64_t val, uint64_t *cases) { trace_switch_t hook = atomic_load_explicit(&trace_switch_with_pc, memory_order_relaxed); hook(GET_CALLER_PC(), val, cases); } __attribute__((visibility("default"))) void __sanitizer_cov_trace_div4( uint32_t val) { trace_div4_t hook = atomic_load_explicit(&trace_div4_with_pc, memory_order_relaxed); hook(GET_CALLER_PC(), val); } __attribute__((visibility("default"))) void __sanitizer_cov_trace_div8( uint64_t val) { trace_div8_t hook = atomic_load_explicit(&trace_div8_with_pc, memory_order_relaxed); hook(GET_CALLER_PC(), val); } __attribute__((visibility("default"))) void __sanitizer_cov_trace_gep( uintptr_t idx) { trace_gep_t hook = atomic_load_explicit(&trace_gep_with_pc, memory_order_relaxed); hook(GET_CALLER_PC(), idx); } __attribute__((visibility("default"))) void __sanitizer_cov_trace_pc_indir( uintptr_t callee) { trace_pc_indir_t hook = atomic_load_explicit(&trace_pc_indir_with_pc, memory_order_relaxed); hook(GET_CALLER_PC(), callee); } __attribute__((visibility("default"))) void __sanitizer_cov_8bit_counters_init( uint8_t *start, uint8_t *end) { cov_8bit_counters_init_t init = atomic_load_explicit(&cov_8bit_counters_init, memory_order_relaxed); init(start, end); } __attribute__((visibility("default"))) void __sanitizer_cov_pcs_init( const uintptr_t *pcs_beg, const uintptr_t *pcs_end) { cov_pcs_init_t init = atomic_load_explicit(&cov_pcs_init, memory_order_relaxed); init(pcs_beg, pcs_end); } // The __sanitizer_weak_hook_* family of functions can be invoked early on macOS // and thus requires NULL checks. __attribute__((visibility("default"))) void __sanitizer_weak_hook_memcmp( void *called_pc, const void *s1, const void *s2, size_t n, int result) { memcmp_hook_t hook = atomic_load_explicit(&memcmp_hook, memory_order_relaxed); if (LIKELY(hook != NULL)) { hook(called_pc, s1, s2, n, result); } } __attribute__((visibility("default"))) void __sanitizer_weak_hook_strncmp( void *called_pc, const void *s1, const void *s2, size_t n, int result) { strncmp_hook_t hook = atomic_load_explicit(&strncmp_hook, memory_order_relaxed); if (LIKELY(hook != NULL)) { hook(called_pc, s1, s2, n, result); } } __attribute__((visibility("default"))) void __sanitizer_weak_hook_strcmp( void *called_pc, const void *s1, const void *s2, int result) { strcmp_hook_t hook = atomic_load_explicit(&strcmp_hook, memory_order_relaxed); if (LIKELY(hook != NULL)) { hook(called_pc, s1, s2, result); } } __attribute__((visibility("default"))) void __sanitizer_weak_hook_strncasecmp( void *called_pc, const void *s1, const void *s2, size_t n, int result) { strncasecmp_hook_t hook = atomic_load_explicit(&strncasecmp_hook, memory_order_relaxed); if (LIKELY(hook != NULL)) { hook(called_pc, s1, s2, n, result); } } __attribute__((visibility("default"))) void __sanitizer_weak_hook_strcasecmp( void *called_pc, const void *s1, const void *s2, int result) { strcasecmp_hook_t hook = atomic_load_explicit(&strcasecmp_hook, memory_order_relaxed); if (LIKELY(hook != NULL)) { hook(called_pc, s1, s2, result); } } __attribute__((visibility("default"))) void __sanitizer_weak_hook_strstr( void *called_pc, const void *s1, const void *s2, char *result) { strstr_hook_t hook = atomic_load_explicit(&strstr_hook, memory_order_relaxed); if (LIKELY(hook != NULL)) { hook(called_pc, s1, s2, result); } } __attribute__((visibility("default"))) void __sanitizer_weak_hook_strcasestr( void *called_pc, const void *s1, const void *s2, char *result) { strcasestr_hook_t hook = atomic_load_explicit(&strstr_hook, memory_order_relaxed); hook(called_pc, s1, s2, result); } __attribute__((visibility("default"))) void __sanitizer_weak_hook_memmem( void *called_pc, const void *s1, size_t len1, const void *s2, size_t len2, void *result) { memmem_hook_t hook = atomic_load_explicit(&memmem_hook, memory_order_relaxed); hook(called_pc, s1, len1, s2, len2, result); }