xref: /llvm-project/compiler-rt/lib/nsan/nsan_interceptors.cpp (revision 9a156f6b2b0c892d8713ba907f07f027b24953d8)
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(dst, v, size);
36 
37   void *res = REAL(memset)(dst, v, size);
38   __nsan_set_value_unknown(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((u8 *)dst, 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(dst, src, size);
53 
54   void *res = REAL(memmove)(dst, src, size);
55   __nsan_copy_values(static_cast<u8 *>(dst), 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((u8 *)dst, (const u8 *)src, 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(dst, src, size);
73   }
74 
75   void *res = REAL(memcpy)(dst, src, size);
76   __nsan_copy_values(static_cast<u8 *>(dst), 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((u8 *)dst, (const u8 *)src, 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(reinterpret_cast<u8 *>(s), 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(reinterpret_cast<u8 *>(*Stringp) - 1, 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(reinterpret_cast<u8 *>(str), internal_strlen(str));
109   return REAL(strtok)(str, delim);
110 }
111 
112 static void nsanCopyZeroTerminated(char *dst, const char *src, uptr n) {
113   __nsan_copy_values(reinterpret_cast<u8 *>(dst),
114                      reinterpret_cast<const u8 *>(src), n);     // Data.
115   __nsan_set_value_unknown(reinterpret_cast<u8 *>(dst) + n, 1); // Terminator.
116 }
117 
118 static void nsanWCopyZeroTerminated(wchar_t *dst, const wchar_t *src, uptr n) {
119   __nsan_copy_values((u8 *)dst, (const u8 *)(src), sizeof(wchar_t) * n);
120   __nsan_set_value_unknown((u8 *)(dst + n), sizeof(wchar_t));
121 }
122 
123 INTERCEPTOR(char *, strdup, const char *S) {
124   char *res = REAL(strdup)(S);
125   if (res) {
126     nsanCopyZeroTerminated(res, S, internal_strlen(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(res, S, wcslen(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(res, S, min(internal_strlen(S), 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, internal_strlen(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, wcslen(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, min(size, internal_strlen(src)));
162   return res;
163 }
164 
165 INTERCEPTOR(char *, strcat, char *dst, const char *src) {
166   const auto DstLenBeforeCat = internal_strlen(dst);
167   char *res = REAL(strcat)(dst, src);
168   nsanCopyZeroTerminated(dst + DstLenBeforeCat, src, internal_strlen(src));
169   return res;
170 }
171 
172 INTERCEPTOR(wchar_t *, wcscat, wchar_t *dst, const wchar_t *src) {
173   const auto DstLenBeforeCat = wcslen(dst);
174   wchar_t *res = REAL(wcscat)(dst, src);
175   nsanWCopyZeroTerminated(dst + DstLenBeforeCat, src, wcslen(src));
176   return res;
177 }
178 
179 INTERCEPTOR(char *, strncat, char *dst, const char *src, usize size) {
180   const auto DstLen = internal_strlen(dst);
181   char *res = REAL(strncat)(dst, src, size);
182   nsanCopyZeroTerminated(dst + DstLen, src, min(size, internal_strlen(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, internal_strlen(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, wcslen(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(reinterpret_cast<u8 *>(dst), internal_strlen(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(&t->starting_sigset_, 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(&myattr);
220     attr = &myattr;
221   }
222 
223   AdjustStackSize(attr);
224 
225   NsanThread *t = NsanThread::Create(callback, 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(&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