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