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