1 | //===- nsan_interceptors.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 | // |
9 | // Interceptors for standard library functions. |
10 | // |
11 | // A note about `printf`: Make sure none of the interceptor code calls any |
12 | // part of the nsan framework that can call `printf`, since this could create |
13 | // a loop (`printf` itself uses the libc). printf-free functions are documented |
14 | // as such in nsan.h. |
15 | // |
16 | //===----------------------------------------------------------------------===// |
17 | |
18 | #include "interception/interception.h" |
19 | #include "nsan.h" |
20 | #include "nsan_thread.h" |
21 | #include "sanitizer_common/sanitizer_common.h" |
22 | #include "sanitizer_common/sanitizer_linux.h" |
23 | |
24 | #include <wchar.h> |
25 | |
26 | using namespace __nsan; |
27 | using namespace __sanitizer; |
28 | |
29 | template <typename T> T min(T a, T b) { return a < b ? a : b; } |
30 | |
31 | INTERCEPTOR(void *, memset, void *dst, int v, usize size) { |
32 | // NOTE: This guard is needed because nsan's initialization code might call |
33 | // memset. |
34 | if (!nsan_initialized && REAL(memset) == nullptr) |
35 | return internal_memset(s: dst, c: v, n: size); |
36 | |
37 | void *res = REAL(memset)(dst, v, size); |
38 | __nsan_set_value_unknown(addr: static_cast<u8 *>(dst), size); |
39 | return res; |
40 | } |
41 | |
42 | INTERCEPTOR(wchar_t *, wmemset, wchar_t *dst, wchar_t v, usize size) { |
43 | wchar_t *res = REAL(wmemset)(dst, v, size); |
44 | __nsan_set_value_unknown(addr: (u8 *)dst, size: sizeof(wchar_t) * size); |
45 | return res; |
46 | } |
47 | |
48 | INTERCEPTOR(void *, memmove, void *dst, const void *src, usize size) { |
49 | // NOTE: This guard is needed because nsan's initialization code might call |
50 | // memmove. |
51 | if (!nsan_initialized && REAL(memmove) == nullptr) |
52 | return internal_memmove(dest: dst, src, n: size); |
53 | |
54 | void *res = REAL(memmove)(dst, src, size); |
55 | __nsan_copy_values(daddr: static_cast<u8 *>(dst), saddr: static_cast<const u8 *>(src), |
56 | size); |
57 | return res; |
58 | } |
59 | |
60 | INTERCEPTOR(wchar_t *, wmemmove, wchar_t *dst, const wchar_t *src, usize size) { |
61 | wchar_t *res = REAL(wmemmove)(dst, src, size); |
62 | __nsan_copy_values(daddr: (u8 *)dst, saddr: (const u8 *)src, size: sizeof(wchar_t) * size); |
63 | return res; |
64 | } |
65 | |
66 | INTERCEPTOR(void *, memcpy, void *dst, const void *src, usize size) { |
67 | // NOTE: This guard is needed because nsan's initialization code might call |
68 | // memcpy. |
69 | if (!nsan_initialized && REAL(memcpy) == nullptr) { |
70 | // memmove is used here because on some platforms this will also |
71 | // intercept the memmove implementation. |
72 | return internal_memmove(dest: dst, src, n: size); |
73 | } |
74 | |
75 | void *res = REAL(memcpy)(dst, src, size); |
76 | __nsan_copy_values(daddr: static_cast<u8 *>(dst), saddr: static_cast<const u8 *>(src), |
77 | size); |
78 | return res; |
79 | } |
80 | |
81 | INTERCEPTOR(wchar_t *, wmemcpy, wchar_t *dst, const wchar_t *src, usize size) { |
82 | wchar_t *res = REAL(wmemcpy)(dst, src, size); |
83 | __nsan_copy_values(daddr: (u8 *)dst, saddr: (const u8 *)src, size: sizeof(wchar_t) * size); |
84 | return res; |
85 | } |
86 | |
87 | INTERCEPTOR(char *, strfry, char *s) { |
88 | const auto Len = internal_strlen(s); |
89 | char *res = REAL(strfry)(s); |
90 | if (res) |
91 | __nsan_set_value_unknown(addr: reinterpret_cast<u8 *>(s), size: Len); |
92 | return res; |
93 | } |
94 | |
95 | INTERCEPTOR(char *, strsep, char **Stringp, const char *delim) { |
96 | char *OrigStringp = REAL(strsep)(Stringp, delim); |
97 | if (*Stringp != nullptr) { |
98 | // The previous character has been overwritten with a '\0' char. |
99 | __nsan_set_value_unknown(addr: reinterpret_cast<u8 *>(*Stringp) - 1, size: 1); |
100 | } |
101 | return OrigStringp; |
102 | } |
103 | |
104 | INTERCEPTOR(char *, strtok, char *str, const char *delim) { |
105 | // This is overly conservative, but the probability that modern code is using |
106 | // strtok on double data is essentially zero anyway. |
107 | if (str) |
108 | __nsan_set_value_unknown(addr: reinterpret_cast<u8 *>(str), size: internal_strlen(s: str)); |
109 | return REAL(strtok)(str, delim); |
110 | } |
111 | |
112 | static void nsanCopyZeroTerminated(char *dst, const char *src, uptr n) { |
113 | __nsan_copy_values(daddr: reinterpret_cast<u8 *>(dst), |
114 | saddr: reinterpret_cast<const u8 *>(src), size: n); // Data. |
115 | __nsan_set_value_unknown(addr: reinterpret_cast<u8 *>(dst) + n, size: 1); // Terminator. |
116 | } |
117 | |
118 | static void nsanWCopyZeroTerminated(wchar_t *dst, const wchar_t *src, uptr n) { |
119 | __nsan_copy_values(daddr: (u8 *)dst, saddr: (const u8 *)(src), size: sizeof(wchar_t) * n); |
120 | __nsan_set_value_unknown(addr: (u8 *)(dst + n), size: sizeof(wchar_t)); |
121 | } |
122 | |
123 | INTERCEPTOR(char *, strdup, const char *S) { |
124 | char *res = REAL(strdup)(S); |
125 | if (res) { |
126 | nsanCopyZeroTerminated(dst: res, src: S, n: internal_strlen(s: S)); |
127 | } |
128 | return res; |
129 | } |
130 | |
131 | INTERCEPTOR(wchar_t *, wcsdup, const wchar_t *S) { |
132 | wchar_t *res = REAL(wcsdup)(S); |
133 | if (res) { |
134 | nsanWCopyZeroTerminated(dst: res, src: S, n: wcslen(s: S)); |
135 | } |
136 | return res; |
137 | } |
138 | |
139 | INTERCEPTOR(char *, strndup, const char *S, usize size) { |
140 | char *res = REAL(strndup)(S, size); |
141 | if (res) { |
142 | nsanCopyZeroTerminated(dst: res, src: S, n: min(a: internal_strlen(s: S), b: size)); |
143 | } |
144 | return res; |
145 | } |
146 | |
147 | INTERCEPTOR(char *, strcpy, char *dst, const char *src) { |
148 | char *res = REAL(strcpy)(dst, src); |
149 | nsanCopyZeroTerminated(dst, src, n: internal_strlen(s: src)); |
150 | return res; |
151 | } |
152 | |
153 | INTERCEPTOR(wchar_t *, wcscpy, wchar_t *dst, const wchar_t *src) { |
154 | wchar_t *res = REAL(wcscpy)(dst, src); |
155 | nsanWCopyZeroTerminated(dst, src, n: wcslen(s: src)); |
156 | return res; |
157 | } |
158 | |
159 | INTERCEPTOR(char *, strncpy, char *dst, const char *src, usize size) { |
160 | char *res = REAL(strncpy)(dst, src, size); |
161 | nsanCopyZeroTerminated(dst, src, n: min(a: size, b: internal_strlen(s: src))); |
162 | return res; |
163 | } |
164 | |
165 | INTERCEPTOR(char *, strcat, char *dst, const char *src) { |
166 | const auto DstLenBeforeCat = internal_strlen(s: dst); |
167 | char *res = REAL(strcat)(dst, src); |
168 | nsanCopyZeroTerminated(dst: dst + DstLenBeforeCat, src, n: internal_strlen(s: src)); |
169 | return res; |
170 | } |
171 | |
172 | INTERCEPTOR(wchar_t *, wcscat, wchar_t *dst, const wchar_t *src) { |
173 | const auto DstLenBeforeCat = wcslen(s: dst); |
174 | wchar_t *res = REAL(wcscat)(dst, src); |
175 | nsanWCopyZeroTerminated(dst: dst + DstLenBeforeCat, src, n: wcslen(s: src)); |
176 | return res; |
177 | } |
178 | |
179 | INTERCEPTOR(char *, strncat, char *dst, const char *src, usize size) { |
180 | const auto DstLen = internal_strlen(s: dst); |
181 | char *res = REAL(strncat)(dst, src, size); |
182 | nsanCopyZeroTerminated(dst: dst + DstLen, src, n: min(a: size, b: internal_strlen(s: src))); |
183 | return res; |
184 | } |
185 | |
186 | INTERCEPTOR(char *, stpcpy, char *dst, const char *src) { |
187 | char *res = REAL(stpcpy)(dst, src); |
188 | nsanCopyZeroTerminated(dst, src, n: internal_strlen(s: src)); |
189 | return res; |
190 | } |
191 | |
192 | INTERCEPTOR(wchar_t *, wcpcpy, wchar_t *dst, const wchar_t *src) { |
193 | wchar_t *res = REAL(wcpcpy)(dst, src); |
194 | nsanWCopyZeroTerminated(dst, src, n: wcslen(s: src)); |
195 | return res; |
196 | } |
197 | |
198 | INTERCEPTOR(usize, strxfrm, char *dst, const char *src, usize size) { |
199 | // This is overly conservative, but this function should very rarely be used. |
200 | __nsan_set_value_unknown(addr: reinterpret_cast<u8 *>(dst), size: internal_strlen(s: dst)); |
201 | return REAL(strxfrm)(dst, src, size); |
202 | } |
203 | |
204 | extern "C" int pthread_attr_init(void *attr); |
205 | extern "C" int pthread_attr_destroy(void *attr); |
206 | |
207 | static void *NsanThreadStartFunc(void *arg) { |
208 | auto *t = reinterpret_cast<NsanThread *>(arg); |
209 | SetCurrentThread(t); |
210 | t->Init(); |
211 | SetSigProcMask(set: &t->starting_sigset_, oldset: nullptr); |
212 | return t->ThreadStart(); |
213 | } |
214 | |
215 | INTERCEPTOR(int, pthread_create, void *th, void *attr, |
216 | void *(*callback)(void *), void *param) { |
217 | __sanitizer_pthread_attr_t myattr; |
218 | if (!attr) { |
219 | pthread_attr_init(attr: &myattr); |
220 | attr = &myattr; |
221 | } |
222 | |
223 | AdjustStackSize(attr); |
224 | |
225 | NsanThread *t = NsanThread::Create(start_routine: callback, arg: param); |
226 | ScopedBlockSignals block(&t->starting_sigset_); |
227 | int res = REAL(pthread_create)(th, attr, NsanThreadStartFunc, t); |
228 | |
229 | if (attr == &myattr) |
230 | pthread_attr_destroy(attr: &myattr); |
231 | return res; |
232 | } |
233 | |
234 | void __nsan::InitializeInterceptors() { |
235 | static bool initialized = false; |
236 | CHECK(!initialized); |
237 | |
238 | InitializeMallocInterceptors(); |
239 | |
240 | INTERCEPT_FUNCTION(memset); |
241 | INTERCEPT_FUNCTION(wmemset); |
242 | INTERCEPT_FUNCTION(memmove); |
243 | INTERCEPT_FUNCTION(wmemmove); |
244 | INTERCEPT_FUNCTION(memcpy); |
245 | INTERCEPT_FUNCTION(wmemcpy); |
246 | |
247 | INTERCEPT_FUNCTION(strdup); |
248 | INTERCEPT_FUNCTION(wcsdup); |
249 | INTERCEPT_FUNCTION(strndup); |
250 | INTERCEPT_FUNCTION(stpcpy); |
251 | INTERCEPT_FUNCTION(wcpcpy); |
252 | INTERCEPT_FUNCTION(strcpy); |
253 | INTERCEPT_FUNCTION(wcscpy); |
254 | INTERCEPT_FUNCTION(strncpy); |
255 | INTERCEPT_FUNCTION(strcat); |
256 | INTERCEPT_FUNCTION(wcscat); |
257 | INTERCEPT_FUNCTION(strncat); |
258 | INTERCEPT_FUNCTION(strxfrm); |
259 | |
260 | INTERCEPT_FUNCTION(strfry); |
261 | INTERCEPT_FUNCTION(strsep); |
262 | INTERCEPT_FUNCTION(strtok); |
263 | |
264 | INTERCEPT_FUNCTION(pthread_create); |
265 | |
266 | initialized = 1; |
267 | } |
268 | |