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
26using namespace __nsan;
27using namespace __sanitizer;
28
29template <typename T> T min(T a, T b) { return a < b ? a : b; }
30
31INTERCEPTOR(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
42INTERCEPTOR(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
48INTERCEPTOR(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
60INTERCEPTOR(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
66INTERCEPTOR(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
81INTERCEPTOR(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
87INTERCEPTOR(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
95INTERCEPTOR(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
104INTERCEPTOR(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
112static 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
118static 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
123INTERCEPTOR(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
131INTERCEPTOR(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
139INTERCEPTOR(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
147INTERCEPTOR(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
153INTERCEPTOR(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
159INTERCEPTOR(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
165INTERCEPTOR(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
172INTERCEPTOR(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
179INTERCEPTOR(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
186INTERCEPTOR(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
192INTERCEPTOR(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
198INTERCEPTOR(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
204extern "C" int pthread_attr_init(void *attr);
205extern "C" int pthread_attr_destroy(void *attr);
206
207static 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
215INTERCEPTOR(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
234void __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