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