1//===-- FuzzerInterceptors.cpp --------------------------------------------===// 2// 3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4// See https://llvm.org/LICENSE.txt for license information. 5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6// 7//===----------------------------------------------------------------------===// 8// Intercept certain libc functions to aid fuzzing. 9// Linked only when other RTs that define their own interceptors are not linked. 10//===----------------------------------------------------------------------===// 11 12#include "FuzzerPlatform.h" 13 14#if LIBFUZZER_LINUX 15 16#define GET_CALLER_PC() __builtin_return_address(0) 17 18#define PTR_TO_REAL(x) real_##x 19#define REAL(x) __interception::PTR_TO_REAL(x) 20#define FUNC_TYPE(x) x##_type 21#define DEFINE_REAL(ret_type, func, ...) \ 22 typedef ret_type (*FUNC_TYPE(func))(__VA_ARGS__); \ 23 namespace __interception { \ 24 FUNC_TYPE(func) PTR_TO_REAL(func); \ 25 } 26 27#include <cassert> 28#include <cstddef> // for size_t 29#include <cstdint> 30#include <dlfcn.h> // for dlsym() 31 32static void *getFuncAddr(const char *name, uintptr_t wrapper_addr) { 33 void *addr = dlsym(RTLD_NEXT, name); 34 if (!addr) { 35 // If the lookup using RTLD_NEXT failed, the sanitizer runtime library is 36 // later in the library search order than the DSO that we are trying to 37 // intercept, which means that we cannot intercept this function. We still 38 // want the address of the real definition, though, so look it up using 39 // RTLD_DEFAULT. 40 addr = dlsym(RTLD_DEFAULT, name); 41 42 // In case `name' is not loaded, dlsym ends up finding the actual wrapper. 43 // We don't want to intercept the wrapper and have it point to itself. 44 if (reinterpret_cast<uintptr_t>(addr) == wrapper_addr) 45 addr = nullptr; 46 } 47 return addr; 48} 49 50static int FuzzerInited = 0; 51static bool FuzzerInitIsRunning; 52 53static void fuzzerInit(); 54 55static void ensureFuzzerInited() { 56 assert(!FuzzerInitIsRunning); 57 if (!FuzzerInited) { 58 fuzzerInit(); 59 } 60} 61 62static int internal_strcmp_strncmp(const char *s1, const char *s2, bool strncmp, 63 size_t n) { 64 size_t i = 0; 65 while (true) { 66 if (strncmp) { 67 if (i == n) 68 break; 69 i++; 70 } 71 unsigned c1 = *s1; 72 unsigned c2 = *s2; 73 if (c1 != c2) 74 return (c1 < c2) ? -1 : 1; 75 if (c1 == 0) 76 break; 77 s1++; 78 s2++; 79 } 80 return 0; 81} 82 83static int internal_strncmp(const char *s1, const char *s2, size_t n) { 84 return internal_strcmp_strncmp(s1, s2, true, n); 85} 86 87static int internal_strcmp(const char *s1, const char *s2) { 88 return internal_strcmp_strncmp(s1, s2, false, 0); 89} 90 91static int internal_memcmp(const void *s1, const void *s2, size_t n) { 92 const uint8_t *t1 = static_cast<const uint8_t *>(s1); 93 const uint8_t *t2 = static_cast<const uint8_t *>(s2); 94 for (size_t i = 0; i < n; ++i, ++t1, ++t2) 95 if (*t1 != *t2) 96 return *t1 < *t2 ? -1 : 1; 97 return 0; 98} 99 100static size_t internal_strlen(const char *s) { 101 size_t i = 0; 102 while (s[i]) 103 i++; 104 return i; 105} 106 107static char *internal_strstr(const char *haystack, const char *needle) { 108 // This is O(N^2), but we are not using it in hot places. 109 size_t len1 = internal_strlen(haystack); 110 size_t len2 = internal_strlen(needle); 111 if (len1 < len2) 112 return nullptr; 113 for (size_t pos = 0; pos <= len1 - len2; pos++) { 114 if (internal_memcmp(haystack + pos, needle, len2) == 0) 115 return const_cast<char *>(haystack) + pos; 116 } 117 return nullptr; 118} 119 120extern "C" { 121 122// Weak hooks forward-declared to avoid dependency on 123// <sanitizer/common_interface_defs.h>. 124void __sanitizer_weak_hook_memcmp(void *called_pc, const void *s1, 125 const void *s2, size_t n, int result); 126void __sanitizer_weak_hook_strncmp(void *called_pc, const char *s1, 127 const char *s2, size_t n, int result); 128void __sanitizer_weak_hook_strncasecmp(void *called_pc, const char *s1, 129 const char *s2, size_t n, int result); 130void __sanitizer_weak_hook_strcmp(void *called_pc, const char *s1, 131 const char *s2, int result); 132void __sanitizer_weak_hook_strcasecmp(void *called_pc, const char *s1, 133 const char *s2, int result); 134void __sanitizer_weak_hook_strstr(void *called_pc, const char *s1, 135 const char *s2, char *result); 136void __sanitizer_weak_hook_strcasestr(void *called_pc, const char *s1, 137 const char *s2, char *result); 138void __sanitizer_weak_hook_memmem(void *called_pc, const void *s1, size_t len1, 139 const void *s2, size_t len2, void *result); 140 141DEFINE_REAL(int, bcmp, const void *, const void *, size_t) 142DEFINE_REAL(int, memcmp, const void *, const void *, size_t) 143DEFINE_REAL(int, strncmp, const char *, const char *, size_t) 144DEFINE_REAL(int, strcmp, const char *, const char *) 145DEFINE_REAL(int, strncasecmp, const char *, const char *, size_t) 146DEFINE_REAL(int, strcasecmp, const char *, const char *) 147DEFINE_REAL(char *, strstr, const char *, const char *) 148DEFINE_REAL(char *, strcasestr, const char *, const char *) 149DEFINE_REAL(void *, memmem, const void *, size_t, const void *, size_t) 150 151ATTRIBUTE_INTERFACE int bcmp(const char *s1, const char *s2, size_t n) { 152 if (!FuzzerInited) 153 return internal_memcmp(s1, s2, n); 154 int result = REAL(bcmp)(s1, s2, n); 155 __sanitizer_weak_hook_memcmp(GET_CALLER_PC(), s1, s2, n, result); 156 return result; 157} 158 159ATTRIBUTE_INTERFACE int memcmp(const void *s1, const void *s2, size_t n) { 160 if (!FuzzerInited) 161 return internal_memcmp(s1, s2, n); 162 int result = REAL(memcmp)(s1, s2, n); 163 __sanitizer_weak_hook_memcmp(GET_CALLER_PC(), s1, s2, n, result); 164 return result; 165} 166 167ATTRIBUTE_INTERFACE int strncmp(const char *s1, const char *s2, size_t n) { 168 if (!FuzzerInited) 169 return internal_strncmp(s1, s2, n); 170 int result = REAL(strncmp)(s1, s2, n); 171 __sanitizer_weak_hook_strncmp(GET_CALLER_PC(), s1, s2, n, result); 172 return result; 173} 174 175ATTRIBUTE_INTERFACE int strcmp(const char *s1, const char *s2) { 176 if (!FuzzerInited) 177 return internal_strcmp(s1, s2); 178 int result = REAL(strcmp)(s1, s2); 179 __sanitizer_weak_hook_strcmp(GET_CALLER_PC(), s1, s2, result); 180 return result; 181} 182 183ATTRIBUTE_INTERFACE int strncasecmp(const char *s1, const char *s2, size_t n) { 184 ensureFuzzerInited(); 185 int result = REAL(strncasecmp)(s1, s2, n); 186 __sanitizer_weak_hook_strncasecmp(GET_CALLER_PC(), s1, s2, n, result); 187 return result; 188} 189 190ATTRIBUTE_INTERFACE int strcasecmp(const char *s1, const char *s2) { 191 ensureFuzzerInited(); 192 int result = REAL(strcasecmp)(s1, s2); 193 __sanitizer_weak_hook_strcasecmp(GET_CALLER_PC(), s1, s2, result); 194 return result; 195} 196 197ATTRIBUTE_INTERFACE char *strstr(const char *s1, const char *s2) { 198 if (!FuzzerInited) 199 return internal_strstr(s1, s2); 200 char *result = REAL(strstr)(s1, s2); 201 __sanitizer_weak_hook_strstr(GET_CALLER_PC(), s1, s2, result); 202 return result; 203} 204 205ATTRIBUTE_INTERFACE char *strcasestr(const char *s1, const char *s2) { 206 ensureFuzzerInited(); 207 char *result = REAL(strcasestr)(s1, s2); 208 __sanitizer_weak_hook_strcasestr(GET_CALLER_PC(), s1, s2, result); 209 return result; 210} 211 212ATTRIBUTE_INTERFACE 213void *memmem(const void *s1, size_t len1, const void *s2, size_t len2) { 214 ensureFuzzerInited(); 215 void *result = REAL(memmem)(s1, len1, s2, len2); 216 __sanitizer_weak_hook_memmem(GET_CALLER_PC(), s1, len1, s2, len2, result); 217 return result; 218} 219 220__attribute__((section(".preinit_array"), 221 used)) static void (*__local_fuzzer_preinit)(void) = fuzzerInit; 222 223} // extern "C" 224 225static void fuzzerInit() { 226 assert(!FuzzerInitIsRunning); 227 if (FuzzerInited) 228 return; 229 FuzzerInitIsRunning = true; 230 231 REAL(bcmp) = reinterpret_cast<memcmp_type>( 232 getFuncAddr("bcmp", reinterpret_cast<uintptr_t>(&bcmp))); 233 REAL(memcmp) = reinterpret_cast<memcmp_type>( 234 getFuncAddr("memcmp", reinterpret_cast<uintptr_t>(&memcmp))); 235 REAL(strncmp) = reinterpret_cast<strncmp_type>( 236 getFuncAddr("strncmp", reinterpret_cast<uintptr_t>(&strncmp))); 237 REAL(strcmp) = reinterpret_cast<strcmp_type>( 238 getFuncAddr("strcmp", reinterpret_cast<uintptr_t>(&strcmp))); 239 REAL(strncasecmp) = reinterpret_cast<strncasecmp_type>( 240 getFuncAddr("strncasecmp", reinterpret_cast<uintptr_t>(&strncasecmp))); 241 REAL(strcasecmp) = reinterpret_cast<strcasecmp_type>( 242 getFuncAddr("strcasecmp", reinterpret_cast<uintptr_t>(&strcasecmp))); 243 REAL(strstr) = reinterpret_cast<strstr_type>( 244 getFuncAddr("strstr", reinterpret_cast<uintptr_t>(&strstr))); 245 REAL(strcasestr) = reinterpret_cast<strcasestr_type>( 246 getFuncAddr("strcasestr", reinterpret_cast<uintptr_t>(&strcasestr))); 247 REAL(memmem) = reinterpret_cast<memmem_type>( 248 getFuncAddr("memmem", reinterpret_cast<uintptr_t>(&memmem))); 249 250 FuzzerInitIsRunning = false; 251 FuzzerInited = 1; 252} 253 254#endif 255