xref: /freebsd/contrib/llvm-project/compiler-rt/lib/nsan/nsan_interceptors.cpp (revision b64c5a0ace59af62eff52bfe110a521dc73c937b)
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/nsan.h"
20 #include "sanitizer_common/sanitizer_common.h"
21 
22 #include <wchar.h>
23 
24 using namespace __sanitizer;
25 using __nsan::nsan_init_is_running;
26 using __nsan::nsan_initialized;
27 
28 template <typename T> T min(T a, T b) { return a < b ? a : b; }
29 
30 INTERCEPTOR(void *, memset, void *dst, int v, uptr size) {
31   // NOTE: This guard is needed because nsan's initialization code might call
32   // memset.
33   if (!nsan_initialized && REAL(memset) == nullptr)
34     return internal_memset(dst, v, size);
35 
36   void *res = REAL(memset)(dst, v, size);
37   __nsan_set_value_unknown(static_cast<u8 *>(dst), size);
38   return res;
39 }
40 
41 INTERCEPTOR(wchar_t *, wmemset, wchar_t *dst, wchar_t v, uptr size) {
42   wchar_t *res = REAL(wmemset)(dst, v, size);
43   __nsan_set_value_unknown((u8 *)dst, sizeof(wchar_t) * size);
44   return res;
45 }
46 
47 INTERCEPTOR(void *, memmove, void *dst, const void *src, uptr size) {
48   // NOTE: This guard is needed because nsan's initialization code might call
49   // memmove.
50   if (!nsan_initialized && REAL(memmove) == nullptr)
51     return internal_memmove(dst, src, size);
52 
53   void *res = REAL(memmove)(dst, src, size);
54   __nsan_copy_values(static_cast<u8 *>(dst), static_cast<const u8 *>(src),
55                      size);
56   return res;
57 }
58 
59 INTERCEPTOR(wchar_t *, wmemmove, wchar_t *dst, const wchar_t *src, uptr size) {
60   wchar_t *res = REAL(wmemmove)(dst, src, size);
61   __nsan_copy_values((u8 *)dst, (const u8 *)src, sizeof(wchar_t) * size);
62   return res;
63 }
64 
65 INTERCEPTOR(void *, memcpy, void *dst, const void *src, uptr size) {
66   // NOTE: This guard is needed because nsan's initialization code might call
67   // memcpy.
68   if (!nsan_initialized && REAL(memcpy) == nullptr) {
69     // memmove is used here because on some platforms this will also
70     // intercept the memmove implementation.
71     return internal_memmove(dst, src, size);
72   }
73 
74   void *res = REAL(memcpy)(dst, src, size);
75   __nsan_copy_values(static_cast<u8 *>(dst), static_cast<const u8 *>(src),
76                      size);
77   return res;
78 }
79 
80 INTERCEPTOR(wchar_t *, wmemcpy, wchar_t *dst, const wchar_t *src, uptr size) {
81   wchar_t *res = REAL(wmemcpy)(dst, src, size);
82   __nsan_copy_values((u8 *)dst, (const u8 *)src, sizeof(wchar_t) * size);
83   return res;
84 }
85 
86 INTERCEPTOR(char *, strfry, char *s) {
87   const auto Len = internal_strlen(s);
88   char *res = REAL(strfry)(s);
89   if (res)
90     __nsan_set_value_unknown(reinterpret_cast<u8 *>(s), Len);
91   return res;
92 }
93 
94 INTERCEPTOR(char *, strsep, char **Stringp, const char *delim) {
95   char *OrigStringp = REAL(strsep)(Stringp, delim);
96   if (Stringp != nullptr) {
97     // The previous character has been overwritten with a '\0' char.
98     __nsan_set_value_unknown(reinterpret_cast<u8 *>(*Stringp) - 1, 1);
99   }
100   return OrigStringp;
101 }
102 
103 INTERCEPTOR(char *, strtok, char *str, const char *delim) {
104   // This is overly conservative, but the probability that modern code is using
105   // strtok on double data is essentially zero anyway.
106   if (str)
107     __nsan_set_value_unknown(reinterpret_cast<u8 *>(str), internal_strlen(str));
108   return REAL(strtok)(str, delim);
109 }
110 
111 static void nsanCopyZeroTerminated(char *dst, const char *src, uptr n) {
112   __nsan_copy_values(reinterpret_cast<u8 *>(dst),
113                      reinterpret_cast<const u8 *>(src), n);     // Data.
114   __nsan_set_value_unknown(reinterpret_cast<u8 *>(dst) + n, 1); // Terminator.
115 }
116 
117 static void nsanWCopyZeroTerminated(wchar_t *dst, const wchar_t *src, uptr n) {
118   __nsan_copy_values((u8 *)dst, (const u8 *)(src), sizeof(wchar_t) * n);
119   __nsan_set_value_unknown((u8 *)(dst + n), sizeof(wchar_t));
120 }
121 
122 INTERCEPTOR(char *, strdup, const char *S) {
123   char *res = REAL(strdup)(S);
124   if (res) {
125     nsanCopyZeroTerminated(res, S, internal_strlen(S));
126   }
127   return res;
128 }
129 
130 INTERCEPTOR(wchar_t *, wcsdup, const wchar_t *S) {
131   wchar_t *res = REAL(wcsdup)(S);
132   if (res) {
133     nsanWCopyZeroTerminated(res, S, wcslen(S));
134   }
135   return res;
136 }
137 
138 INTERCEPTOR(char *, strndup, const char *S, uptr size) {
139   char *res = REAL(strndup)(S, size);
140   if (res) {
141     nsanCopyZeroTerminated(res, S, min(internal_strlen(S), size));
142   }
143   return res;
144 }
145 
146 INTERCEPTOR(char *, strcpy, char *dst, const char *src) {
147   char *res = REAL(strcpy)(dst, src);
148   nsanCopyZeroTerminated(dst, src, internal_strlen(src));
149   return res;
150 }
151 
152 INTERCEPTOR(wchar_t *, wcscpy, wchar_t *dst, const wchar_t *src) {
153   wchar_t *res = REAL(wcscpy)(dst, src);
154   nsanWCopyZeroTerminated(dst, src, wcslen(src));
155   return res;
156 }
157 
158 INTERCEPTOR(char *, strncpy, char *dst, const char *src, uptr size) {
159   char *res = REAL(strncpy)(dst, src, size);
160   nsanCopyZeroTerminated(dst, src, min(size, internal_strlen(src)));
161   return res;
162 }
163 
164 INTERCEPTOR(char *, strcat, char *dst, const char *src) {
165   const auto DstLenBeforeCat = internal_strlen(dst);
166   char *res = REAL(strcat)(dst, src);
167   nsanCopyZeroTerminated(dst + DstLenBeforeCat, src, internal_strlen(src));
168   return res;
169 }
170 
171 INTERCEPTOR(wchar_t *, wcscat, wchar_t *dst, const wchar_t *src) {
172   const auto DstLenBeforeCat = wcslen(dst);
173   wchar_t *res = REAL(wcscat)(dst, src);
174   nsanWCopyZeroTerminated(dst + DstLenBeforeCat, src, wcslen(src));
175   return res;
176 }
177 
178 INTERCEPTOR(char *, strncat, char *dst, const char *src, uptr size) {
179   const auto DstLen = internal_strlen(dst);
180   char *res = REAL(strncat)(dst, src, size);
181   nsanCopyZeroTerminated(dst + DstLen, src, min(size, internal_strlen(src)));
182   return res;
183 }
184 
185 INTERCEPTOR(char *, stpcpy, char *dst, const char *src) {
186   char *res = REAL(stpcpy)(dst, src);
187   nsanCopyZeroTerminated(dst, src, internal_strlen(src));
188   return res;
189 }
190 
191 INTERCEPTOR(wchar_t *, wcpcpy, wchar_t *dst, const wchar_t *src) {
192   wchar_t *res = REAL(wcpcpy)(dst, src);
193   nsanWCopyZeroTerminated(dst, src, wcslen(src));
194   return res;
195 }
196 
197 INTERCEPTOR(uptr, strxfrm, char *dst, const char *src, uptr size) {
198   // This is overly conservative, but this function should very rarely be used.
199   __nsan_set_value_unknown(reinterpret_cast<u8 *>(dst), internal_strlen(dst));
200   const uptr res = REAL(strxfrm)(dst, src, size);
201   return res;
202 }
203 
204 void __nsan::InitializeInterceptors() {
205   static bool initialized = false;
206   CHECK(!initialized);
207 
208   InitializeMallocInterceptors();
209 
210   INTERCEPT_FUNCTION(memset);
211   INTERCEPT_FUNCTION(wmemset);
212   INTERCEPT_FUNCTION(memmove);
213   INTERCEPT_FUNCTION(wmemmove);
214   INTERCEPT_FUNCTION(memcpy);
215   INTERCEPT_FUNCTION(wmemcpy);
216 
217   INTERCEPT_FUNCTION(strdup);
218   INTERCEPT_FUNCTION(wcsdup);
219   INTERCEPT_FUNCTION(strndup);
220   INTERCEPT_FUNCTION(stpcpy);
221   INTERCEPT_FUNCTION(wcpcpy);
222   INTERCEPT_FUNCTION(strcpy);
223   INTERCEPT_FUNCTION(wcscpy);
224   INTERCEPT_FUNCTION(strncpy);
225   INTERCEPT_FUNCTION(strcat);
226   INTERCEPT_FUNCTION(wcscat);
227   INTERCEPT_FUNCTION(strncat);
228   INTERCEPT_FUNCTION(strxfrm);
229 
230   INTERCEPT_FUNCTION(strfry);
231   INTERCEPT_FUNCTION(strsep);
232   INTERCEPT_FUNCTION(strtok);
233 
234   initialized = 1;
235 }
236