268 lines · cpp
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-exception6//7//===----------------------------------------------------------------------===//8//9// Interceptors for standard library functions.10//11// A note about `printf`: Make sure none of the interceptor code calls any12// part of the nsan framework that can call `printf`, since this could create13// a loop (`printf` itself uses the libc). printf-free functions are documented14// 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 call33 // 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 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((u8 *)dst, 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 call50 // 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 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((u8 *)dst, (const u8 *)src, 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 call68 // memcpy.69 if (!nsan_initialized && REAL(memcpy) == nullptr) {70 // memmove is used here because on some platforms this will also71 // 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 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((u8 *)dst, (const u8 *)src, 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(reinterpret_cast<u8 *>(s), 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(reinterpret_cast<u8 *>(*Stringp) - 1, 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 using106 // 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 112static 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 118static 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 123INTERCEPTOR(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 131INTERCEPTOR(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 139INTERCEPTOR(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 147INTERCEPTOR(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 153INTERCEPTOR(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 159INTERCEPTOR(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 165INTERCEPTOR(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 172INTERCEPTOR(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 179INTERCEPTOR(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 186INTERCEPTOR(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 192INTERCEPTOR(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 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(reinterpret_cast<u8 *>(dst), internal_strlen(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(&t->starting_sigset_, 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(&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 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