For some targets uptr is mapped to unsigned int and size_t to unsigned long and sizeof(int)==sizeof(long) holds. Still, these are distinct types and type checking may fail. Therefore, replace uptr by usize/SIZE_T wherever a size_t is expected. Part of #116957
268 lines
8.3 KiB
C++
268 lines
8.3 KiB
C++
//===- nsan_interceptors.cpp ----------------------------------------------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// Interceptors for standard library functions.
|
|
//
|
|
// A note about `printf`: Make sure none of the interceptor code calls any
|
|
// part of the nsan framework that can call `printf`, since this could create
|
|
// a loop (`printf` itself uses the libc). printf-free functions are documented
|
|
// as such in nsan.h.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "interception/interception.h"
|
|
#include "nsan.h"
|
|
#include "nsan_thread.h"
|
|
#include "sanitizer_common/sanitizer_common.h"
|
|
#include "sanitizer_common/sanitizer_linux.h"
|
|
|
|
#include <wchar.h>
|
|
|
|
using namespace __nsan;
|
|
using namespace __sanitizer;
|
|
|
|
template <typename T> T min(T a, T b) { return a < b ? a : b; }
|
|
|
|
INTERCEPTOR(void *, memset, void *dst, int v, usize size) {
|
|
// NOTE: This guard is needed because nsan's initialization code might call
|
|
// memset.
|
|
if (!nsan_initialized && REAL(memset) == nullptr)
|
|
return internal_memset(dst, v, size);
|
|
|
|
void *res = REAL(memset)(dst, v, size);
|
|
__nsan_set_value_unknown(static_cast<u8 *>(dst), size);
|
|
return res;
|
|
}
|
|
|
|
INTERCEPTOR(wchar_t *, wmemset, wchar_t *dst, wchar_t v, usize size) {
|
|
wchar_t *res = REAL(wmemset)(dst, v, size);
|
|
__nsan_set_value_unknown((u8 *)dst, sizeof(wchar_t) * size);
|
|
return res;
|
|
}
|
|
|
|
INTERCEPTOR(void *, memmove, void *dst, const void *src, usize size) {
|
|
// NOTE: This guard is needed because nsan's initialization code might call
|
|
// memmove.
|
|
if (!nsan_initialized && REAL(memmove) == nullptr)
|
|
return internal_memmove(dst, src, size);
|
|
|
|
void *res = REAL(memmove)(dst, src, size);
|
|
__nsan_copy_values(static_cast<u8 *>(dst), static_cast<const u8 *>(src),
|
|
size);
|
|
return res;
|
|
}
|
|
|
|
INTERCEPTOR(wchar_t *, wmemmove, wchar_t *dst, const wchar_t *src, usize size) {
|
|
wchar_t *res = REAL(wmemmove)(dst, src, size);
|
|
__nsan_copy_values((u8 *)dst, (const u8 *)src, sizeof(wchar_t) * size);
|
|
return res;
|
|
}
|
|
|
|
INTERCEPTOR(void *, memcpy, void *dst, const void *src, usize size) {
|
|
// NOTE: This guard is needed because nsan's initialization code might call
|
|
// memcpy.
|
|
if (!nsan_initialized && REAL(memcpy) == nullptr) {
|
|
// memmove is used here because on some platforms this will also
|
|
// intercept the memmove implementation.
|
|
return internal_memmove(dst, src, size);
|
|
}
|
|
|
|
void *res = REAL(memcpy)(dst, src, size);
|
|
__nsan_copy_values(static_cast<u8 *>(dst), static_cast<const u8 *>(src),
|
|
size);
|
|
return res;
|
|
}
|
|
|
|
INTERCEPTOR(wchar_t *, wmemcpy, wchar_t *dst, const wchar_t *src, usize size) {
|
|
wchar_t *res = REAL(wmemcpy)(dst, src, size);
|
|
__nsan_copy_values((u8 *)dst, (const u8 *)src, sizeof(wchar_t) * size);
|
|
return res;
|
|
}
|
|
|
|
INTERCEPTOR(char *, strfry, char *s) {
|
|
const auto Len = internal_strlen(s);
|
|
char *res = REAL(strfry)(s);
|
|
if (res)
|
|
__nsan_set_value_unknown(reinterpret_cast<u8 *>(s), Len);
|
|
return res;
|
|
}
|
|
|
|
INTERCEPTOR(char *, strsep, char **Stringp, const char *delim) {
|
|
char *OrigStringp = REAL(strsep)(Stringp, delim);
|
|
if (*Stringp != nullptr) {
|
|
// The previous character has been overwritten with a '\0' char.
|
|
__nsan_set_value_unknown(reinterpret_cast<u8 *>(*Stringp) - 1, 1);
|
|
}
|
|
return OrigStringp;
|
|
}
|
|
|
|
INTERCEPTOR(char *, strtok, char *str, const char *delim) {
|
|
// This is overly conservative, but the probability that modern code is using
|
|
// strtok on double data is essentially zero anyway.
|
|
if (str)
|
|
__nsan_set_value_unknown(reinterpret_cast<u8 *>(str), internal_strlen(str));
|
|
return REAL(strtok)(str, delim);
|
|
}
|
|
|
|
static void nsanCopyZeroTerminated(char *dst, const char *src, uptr n) {
|
|
__nsan_copy_values(reinterpret_cast<u8 *>(dst),
|
|
reinterpret_cast<const u8 *>(src), n); // Data.
|
|
__nsan_set_value_unknown(reinterpret_cast<u8 *>(dst) + n, 1); // Terminator.
|
|
}
|
|
|
|
static void nsanWCopyZeroTerminated(wchar_t *dst, const wchar_t *src, uptr n) {
|
|
__nsan_copy_values((u8 *)dst, (const u8 *)(src), sizeof(wchar_t) * n);
|
|
__nsan_set_value_unknown((u8 *)(dst + n), sizeof(wchar_t));
|
|
}
|
|
|
|
INTERCEPTOR(char *, strdup, const char *S) {
|
|
char *res = REAL(strdup)(S);
|
|
if (res) {
|
|
nsanCopyZeroTerminated(res, S, internal_strlen(S));
|
|
}
|
|
return res;
|
|
}
|
|
|
|
INTERCEPTOR(wchar_t *, wcsdup, const wchar_t *S) {
|
|
wchar_t *res = REAL(wcsdup)(S);
|
|
if (res) {
|
|
nsanWCopyZeroTerminated(res, S, wcslen(S));
|
|
}
|
|
return res;
|
|
}
|
|
|
|
INTERCEPTOR(char *, strndup, const char *S, usize size) {
|
|
char *res = REAL(strndup)(S, size);
|
|
if (res) {
|
|
nsanCopyZeroTerminated(res, S, min(internal_strlen(S), size));
|
|
}
|
|
return res;
|
|
}
|
|
|
|
INTERCEPTOR(char *, strcpy, char *dst, const char *src) {
|
|
char *res = REAL(strcpy)(dst, src);
|
|
nsanCopyZeroTerminated(dst, src, internal_strlen(src));
|
|
return res;
|
|
}
|
|
|
|
INTERCEPTOR(wchar_t *, wcscpy, wchar_t *dst, const wchar_t *src) {
|
|
wchar_t *res = REAL(wcscpy)(dst, src);
|
|
nsanWCopyZeroTerminated(dst, src, wcslen(src));
|
|
return res;
|
|
}
|
|
|
|
INTERCEPTOR(char *, strncpy, char *dst, const char *src, usize size) {
|
|
char *res = REAL(strncpy)(dst, src, size);
|
|
nsanCopyZeroTerminated(dst, src, min(size, internal_strlen(src)));
|
|
return res;
|
|
}
|
|
|
|
INTERCEPTOR(char *, strcat, char *dst, const char *src) {
|
|
const auto DstLenBeforeCat = internal_strlen(dst);
|
|
char *res = REAL(strcat)(dst, src);
|
|
nsanCopyZeroTerminated(dst + DstLenBeforeCat, src, internal_strlen(src));
|
|
return res;
|
|
}
|
|
|
|
INTERCEPTOR(wchar_t *, wcscat, wchar_t *dst, const wchar_t *src) {
|
|
const auto DstLenBeforeCat = wcslen(dst);
|
|
wchar_t *res = REAL(wcscat)(dst, src);
|
|
nsanWCopyZeroTerminated(dst + DstLenBeforeCat, src, wcslen(src));
|
|
return res;
|
|
}
|
|
|
|
INTERCEPTOR(char *, strncat, char *dst, const char *src, usize size) {
|
|
const auto DstLen = internal_strlen(dst);
|
|
char *res = REAL(strncat)(dst, src, size);
|
|
nsanCopyZeroTerminated(dst + DstLen, src, min(size, internal_strlen(src)));
|
|
return res;
|
|
}
|
|
|
|
INTERCEPTOR(char *, stpcpy, char *dst, const char *src) {
|
|
char *res = REAL(stpcpy)(dst, src);
|
|
nsanCopyZeroTerminated(dst, src, internal_strlen(src));
|
|
return res;
|
|
}
|
|
|
|
INTERCEPTOR(wchar_t *, wcpcpy, wchar_t *dst, const wchar_t *src) {
|
|
wchar_t *res = REAL(wcpcpy)(dst, src);
|
|
nsanWCopyZeroTerminated(dst, src, wcslen(src));
|
|
return res;
|
|
}
|
|
|
|
INTERCEPTOR(usize, strxfrm, char *dst, const char *src, usize size) {
|
|
// This is overly conservative, but this function should very rarely be used.
|
|
__nsan_set_value_unknown(reinterpret_cast<u8 *>(dst), internal_strlen(dst));
|
|
return REAL(strxfrm)(dst, src, size);
|
|
}
|
|
|
|
extern "C" int pthread_attr_init(void *attr);
|
|
extern "C" int pthread_attr_destroy(void *attr);
|
|
|
|
static void *NsanThreadStartFunc(void *arg) {
|
|
auto *t = reinterpret_cast<NsanThread *>(arg);
|
|
SetCurrentThread(t);
|
|
t->Init();
|
|
SetSigProcMask(&t->starting_sigset_, nullptr);
|
|
return t->ThreadStart();
|
|
}
|
|
|
|
INTERCEPTOR(int, pthread_create, void *th, void *attr,
|
|
void *(*callback)(void *), void *param) {
|
|
__sanitizer_pthread_attr_t myattr;
|
|
if (!attr) {
|
|
pthread_attr_init(&myattr);
|
|
attr = &myattr;
|
|
}
|
|
|
|
AdjustStackSize(attr);
|
|
|
|
NsanThread *t = NsanThread::Create(callback, param);
|
|
ScopedBlockSignals block(&t->starting_sigset_);
|
|
int res = REAL(pthread_create)(th, attr, NsanThreadStartFunc, t);
|
|
|
|
if (attr == &myattr)
|
|
pthread_attr_destroy(&myattr);
|
|
return res;
|
|
}
|
|
|
|
void __nsan::InitializeInterceptors() {
|
|
static bool initialized = false;
|
|
CHECK(!initialized);
|
|
|
|
InitializeMallocInterceptors();
|
|
|
|
INTERCEPT_FUNCTION(memset);
|
|
INTERCEPT_FUNCTION(wmemset);
|
|
INTERCEPT_FUNCTION(memmove);
|
|
INTERCEPT_FUNCTION(wmemmove);
|
|
INTERCEPT_FUNCTION(memcpy);
|
|
INTERCEPT_FUNCTION(wmemcpy);
|
|
|
|
INTERCEPT_FUNCTION(strdup);
|
|
INTERCEPT_FUNCTION(wcsdup);
|
|
INTERCEPT_FUNCTION(strndup);
|
|
INTERCEPT_FUNCTION(stpcpy);
|
|
INTERCEPT_FUNCTION(wcpcpy);
|
|
INTERCEPT_FUNCTION(strcpy);
|
|
INTERCEPT_FUNCTION(wcscpy);
|
|
INTERCEPT_FUNCTION(strncpy);
|
|
INTERCEPT_FUNCTION(strcat);
|
|
INTERCEPT_FUNCTION(wcscat);
|
|
INTERCEPT_FUNCTION(strncat);
|
|
INTERCEPT_FUNCTION(strxfrm);
|
|
|
|
INTERCEPT_FUNCTION(strfry);
|
|
INTERCEPT_FUNCTION(strsep);
|
|
INTERCEPT_FUNCTION(strtok);
|
|
|
|
INTERCEPT_FUNCTION(pthread_create);
|
|
|
|
initialized = 1;
|
|
}
|