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
24using namespace __sanitizer;
25using __nsan::nsan_init_is_running;
26using __nsan::nsan_initialized;
27
28template <typename T> T min(T a, T b) { return a < b ? a : b; }
29
30INTERCEPTOR(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
41INTERCEPTOR(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
47INTERCEPTOR(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
59INTERCEPTOR(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
65INTERCEPTOR(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
80INTERCEPTOR(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
86INTERCEPTOR(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
94INTERCEPTOR(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
103INTERCEPTOR(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
111static 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
117static 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
122INTERCEPTOR(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
130INTERCEPTOR(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
138INTERCEPTOR(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
146INTERCEPTOR(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
152INTERCEPTOR(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
158INTERCEPTOR(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
164INTERCEPTOR(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
171INTERCEPTOR(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
178INTERCEPTOR(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
185INTERCEPTOR(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
191INTERCEPTOR(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
197INTERCEPTOR(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
204void __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