| 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(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 | |
| 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(addr: (u8 *)dst, size: 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(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 | |
| 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(daddr: (u8 *)dst, saddr: (const u8 *)src, size: 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(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 | |
| 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(daddr: (u8 *)dst, saddr: (const u8 *)src, size: 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(addr: reinterpret_cast<u8 *>(s), size: 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(addr: reinterpret_cast<u8 *>(*Stringp) - 1, size: 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(addr: reinterpret_cast<u8 *>(str), size: internal_strlen(s: str)); |
| 109 | return REAL(strtok)(str, delim); |
| 110 | } |
| 111 | |
| 112 | static 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 | |
| 118 | static 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 | |
| 123 | INTERCEPTOR(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 | |
| 131 | INTERCEPTOR(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 | |
| 139 | INTERCEPTOR(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 | |
| 147 | INTERCEPTOR(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 | |
| 153 | INTERCEPTOR(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 | |
| 159 | INTERCEPTOR(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 | |
| 165 | INTERCEPTOR(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 | |
| 172 | INTERCEPTOR(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 | |
| 179 | INTERCEPTOR(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 | |
| 186 | INTERCEPTOR(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 | |
| 192 | INTERCEPTOR(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 | |
| 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(addr: reinterpret_cast<u8 *>(dst), size: internal_strlen(s: 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(set: &t->starting_sigset_, oldset: 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(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 | |
| 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 | |