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