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