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 | |
32 | static void *getFuncAddr(const char *name, uintptr_t wrapper_addr) { |
33 | void *addr = dlsym(RTLD_NEXT, name: 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: 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 | |
50 | static int FuzzerInited = 0; |
51 | static bool FuzzerInitIsRunning; |
52 | |
53 | static void fuzzerInit(); |
54 | |
55 | static void ensureFuzzerInited() { |
56 | assert(!FuzzerInitIsRunning); |
57 | if (!FuzzerInited) { |
58 | fuzzerInit(); |
59 | } |
60 | } |
61 | |
62 | static 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 | |
83 | static int internal_strncmp(const char *s1, const char *s2, size_t n) { |
84 | return internal_strcmp_strncmp(s1, s2, strncmp: true, n); |
85 | } |
86 | |
87 | static int internal_strcmp(const char *s1, const char *s2) { |
88 | return internal_strcmp_strncmp(s1, s2, strncmp: false, n: 0); |
89 | } |
90 | |
91 | static 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 | |
100 | static size_t internal_strlen(const char *s) { |
101 | size_t i = 0; |
102 | while (s[i]) |
103 | i++; |
104 | return i; |
105 | } |
106 | |
107 | static 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(s: haystack); |
110 | size_t len2 = internal_strlen(s: needle); |
111 | if (len1 < len2) |
112 | return nullptr; |
113 | for (size_t pos = 0; pos <= len1 - len2; pos++) { |
114 | if (internal_memcmp(s1: haystack + pos, s2: needle, n: len2) == 0) |
115 | return const_cast<char *>(haystack) + pos; |
116 | } |
117 | return nullptr; |
118 | } |
119 | |
120 | extern "C" { |
121 | |
122 | // Weak hooks forward-declared to avoid dependency on |
123 | // <sanitizer/common_interface_defs.h>. |
124 | void __sanitizer_weak_hook_memcmp(void *called_pc, const void *s1, |
125 | const void *s2, size_t n, int result); |
126 | void __sanitizer_weak_hook_strncmp(void *called_pc, const char *s1, |
127 | const char *s2, size_t n, int result); |
128 | void __sanitizer_weak_hook_strncasecmp(void *called_pc, const char *s1, |
129 | const char *s2, size_t n, int result); |
130 | void __sanitizer_weak_hook_strcmp(void *called_pc, const char *s1, |
131 | const char *s2, int result); |
132 | void __sanitizer_weak_hook_strcasecmp(void *called_pc, const char *s1, |
133 | const char *s2, int result); |
134 | void __sanitizer_weak_hook_strstr(void *called_pc, const char *s1, |
135 | const char *s2, char *result); |
136 | void __sanitizer_weak_hook_strcasestr(void *called_pc, const char *s1, |
137 | const char *s2, char *result); |
138 | void __sanitizer_weak_hook_memmem(void *called_pc, const void *s1, size_t len1, |
139 | const void *s2, size_t len2, void *result); |
140 | |
141 | DEFINE_REAL(int, bcmp, const void *, const void *, size_t) |
142 | DEFINE_REAL(int, memcmp, const void *, const void *, size_t) |
143 | DEFINE_REAL(int, strncmp, const char *, const char *, size_t) |
144 | DEFINE_REAL(int, strcmp, const char *, const char *) |
145 | DEFINE_REAL(int, strncasecmp, const char *, const char *, size_t) |
146 | DEFINE_REAL(int, strcasecmp, const char *, const char *) |
147 | DEFINE_REAL(char *, strstr, const char *, const char *) |
148 | DEFINE_REAL(char *, strcasestr, const char *, const char *) |
149 | DEFINE_REAL(void *, memmem, const void *, size_t, const void *, size_t) |
150 | |
151 | ATTRIBUTE_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 | |
159 | ATTRIBUTE_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 | |
167 | ATTRIBUTE_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 | |
175 | ATTRIBUTE_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 | |
183 | ATTRIBUTE_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 | |
190 | ATTRIBUTE_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 | |
197 | ATTRIBUTE_INTERFACE char *strstr(const char *s1, const char *s2) { |
198 | if (!FuzzerInited) |
199 | return internal_strstr(haystack: s1, needle: s2); |
200 | char *result = REAL(strstr)(s1, s2); |
201 | __sanitizer_weak_hook_strstr(GET_CALLER_PC(), s1, s2, result); |
202 | return result; |
203 | } |
204 | |
205 | ATTRIBUTE_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 | |
212 | ATTRIBUTE_INTERFACE |
213 | void *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 | |
225 | static void fuzzerInit() { |
226 | assert(!FuzzerInitIsRunning); |
227 | if (FuzzerInited) |
228 | return; |
229 | FuzzerInitIsRunning = true; |
230 | |
231 | REAL(bcmp) = reinterpret_cast<memcmp_type>( |
232 | getFuncAddr(name: "bcmp" , wrapper_addr: reinterpret_cast<uintptr_t>(&bcmp))); |
233 | REAL(memcmp) = reinterpret_cast<memcmp_type>( |
234 | getFuncAddr(name: "memcmp" , wrapper_addr: reinterpret_cast<uintptr_t>(&memcmp))); |
235 | REAL(strncmp) = reinterpret_cast<strncmp_type>( |
236 | getFuncAddr(name: "strncmp" , wrapper_addr: reinterpret_cast<uintptr_t>(&strncmp))); |
237 | REAL(strcmp) = reinterpret_cast<strcmp_type>( |
238 | getFuncAddr(name: "strcmp" , wrapper_addr: reinterpret_cast<uintptr_t>(&strcmp))); |
239 | REAL(strncasecmp) = reinterpret_cast<strncasecmp_type>( |
240 | getFuncAddr(name: "strncasecmp" , wrapper_addr: reinterpret_cast<uintptr_t>(&strncasecmp))); |
241 | REAL(strcasecmp) = reinterpret_cast<strcasecmp_type>( |
242 | getFuncAddr(name: "strcasecmp" , wrapper_addr: reinterpret_cast<uintptr_t>(&strcasecmp))); |
243 | REAL(strstr) = reinterpret_cast<strstr_type>( |
244 | getFuncAddr(name: "strstr" , wrapper_addr: reinterpret_cast<uintptr_t>(&strstr))); |
245 | REAL(strcasestr) = reinterpret_cast<strcasestr_type>( |
246 | getFuncAddr(name: "strcasestr" , wrapper_addr: reinterpret_cast<uintptr_t>(&strcasestr))); |
247 | REAL(memmem) = reinterpret_cast<memmem_type>( |
248 | getFuncAddr(name: "memmem" , wrapper_addr: reinterpret_cast<uintptr_t>(&memmem))); |
249 | |
250 | FuzzerInitIsRunning = false; |
251 | FuzzerInited = 1; |
252 | } |
253 | |
254 | #endif |
255 | |