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(s: dst, c: v, n: size); |
35 | |
36 | void *res = REAL(memset)(dst, v, size); |
37 | __nsan_set_value_unknown(addr: 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(addr: (u8 *)dst, size: 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(dest: dst, src, n: size); |
52 | |
53 | void *res = REAL(memmove)(dst, src, size); |
54 | __nsan_copy_values(daddr: static_cast<u8 *>(dst), saddr: 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(daddr: (u8 *)dst, saddr: (const u8 *)src, size: 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(dest: dst, src, n: size); |
72 | } |
73 | |
74 | void *res = REAL(memcpy)(dst, src, size); |
75 | __nsan_copy_values(daddr: static_cast<u8 *>(dst), saddr: 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(daddr: (u8 *)dst, saddr: (const u8 *)src, size: 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(addr: reinterpret_cast<u8 *>(s), size: 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(addr: reinterpret_cast<u8 *>(*Stringp) - 1, size: 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(addr: reinterpret_cast<u8 *>(str), size: internal_strlen(s: str)); |
108 | return REAL(strtok)(str, delim); |
109 | } |
110 | |
111 | static void nsanCopyZeroTerminated(char *dst, const char *src, uptr n) { |
112 | __nsan_copy_values(daddr: reinterpret_cast<u8 *>(dst), |
113 | saddr: reinterpret_cast<const u8 *>(src), size: n); // Data. |
114 | __nsan_set_value_unknown(addr: reinterpret_cast<u8 *>(dst) + n, size: 1); // Terminator. |
115 | } |
116 | |
117 | static void nsanWCopyZeroTerminated(wchar_t *dst, const wchar_t *src, uptr n) { |
118 | __nsan_copy_values(daddr: (u8 *)dst, saddr: (const u8 *)(src), size: sizeof(wchar_t) * n); |
119 | __nsan_set_value_unknown(addr: (u8 *)(dst + n), size: sizeof(wchar_t)); |
120 | } |
121 | |
122 | INTERCEPTOR(char *, strdup, const char *S) { |
123 | char *res = REAL(strdup)(S); |
124 | if (res) { |
125 | nsanCopyZeroTerminated(dst: res, src: S, n: internal_strlen(s: 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(dst: res, src: S, n: wcslen(s: 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(dst: res, src: S, n: min(a: internal_strlen(s: S), b: 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, n: internal_strlen(s: 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, n: wcslen(s: 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, n: min(a: size, b: internal_strlen(s: src))); |
161 | return res; |
162 | } |
163 | |
164 | INTERCEPTOR(char *, strcat, char *dst, const char *src) { |
165 | const auto DstLenBeforeCat = internal_strlen(s: dst); |
166 | char *res = REAL(strcat)(dst, src); |
167 | nsanCopyZeroTerminated(dst: dst + DstLenBeforeCat, src, n: internal_strlen(s: src)); |
168 | return res; |
169 | } |
170 | |
171 | INTERCEPTOR(wchar_t *, wcscat, wchar_t *dst, const wchar_t *src) { |
172 | const auto DstLenBeforeCat = wcslen(s: dst); |
173 | wchar_t *res = REAL(wcscat)(dst, src); |
174 | nsanWCopyZeroTerminated(dst: dst + DstLenBeforeCat, src, n: wcslen(s: src)); |
175 | return res; |
176 | } |
177 | |
178 | INTERCEPTOR(char *, strncat, char *dst, const char *src, uptr size) { |
179 | const auto DstLen = internal_strlen(s: dst); |
180 | char *res = REAL(strncat)(dst, src, size); |
181 | nsanCopyZeroTerminated(dst: dst + DstLen, src, n: min(a: size, b: internal_strlen(s: 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, n: internal_strlen(s: 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, n: wcslen(s: 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(addr: reinterpret_cast<u8 *>(dst), size: internal_strlen(s: 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 | |