[nsan] Add shared runtime
so that `clang -fsanitize=numerical -shared-libsan` will use `libclang_rt.nsan.so` on Linux. Shared runtime is preferred for some platforms (Android, Apple, Fuchsia; though they are not supported yet) and helps plugin use cases (#98302). * Update `ninja nsan` to build `libclang_rt.nsan.so` * Fix `nsan.syms.extra`: `nsan_*` is unneeded. Add `__ubsan_*` so that `-fsanitize=numerical,undefined -shared-libsan` works. * Move allocation functions to `nsan_malloc_linux.cpp`. While Apple platforms aren't supported yet, this separation makes it easier to add Apple support. * Delete interceptors for very obsoleted pvalloc/valloc but retain memalign. * Replace `HandleEarlyAlloc` with `DlsymAlloc`. Pull Request: https://github.com/llvm/llvm-project/pull/98415
This commit is contained in:
@@ -6,6 +6,7 @@ set(NSAN_SOURCES
|
||||
nsan.cpp
|
||||
nsan_flags.cpp
|
||||
nsan_interceptors.cpp
|
||||
nsan_malloc_linux.cpp
|
||||
nsan_stats.cpp
|
||||
nsan_suppressions.cpp
|
||||
)
|
||||
@@ -24,30 +25,98 @@ append_list_if(COMPILER_RT_HAS_FPIC_FLAG -fPIC NSAN_CFLAGS)
|
||||
set(NSAN_DYNAMIC_LINK_FLAGS ${SANITIZER_COMMON_LINK_FLAGS})
|
||||
|
||||
set(NSAN_CFLAGS ${SANITIZER_COMMON_CFLAGS})
|
||||
set(NSAN_DYNAMIC_CFLAGS ${NSAN_CFLAGS})
|
||||
|
||||
foreach(arch ${NSAN_SUPPORTED_ARCH})
|
||||
add_compiler_rt_runtime(
|
||||
clang_rt.nsan
|
||||
STATIC
|
||||
ARCHS ${arch}
|
||||
SOURCES ${NSAN_SOURCES}
|
||||
$<TARGET_OBJECTS:RTInterception.${arch}>
|
||||
$<TARGET_OBJECTS:RTSanitizerCommon.${arch}>
|
||||
$<TARGET_OBJECTS:RTSanitizerCommonLibc.${arch}>
|
||||
$<TARGET_OBJECTS:RTSanitizerCommonCoverage.${arch}>
|
||||
$<TARGET_OBJECTS:RTSanitizerCommonSymbolizer.${arch}>
|
||||
$<TARGET_OBJECTS:RTUbsan.${arch}>
|
||||
ADDITIONAL_HEADERS ${NSAN_HEADERS}
|
||||
CFLAGS ${NSAN_CFLAGS}
|
||||
PARENT_TARGET nsan
|
||||
)
|
||||
endforeach()
|
||||
set(NSAN_COMMON_RUNTIME_OBJECT_LIBS
|
||||
RTInterception
|
||||
RTSanitizerCommon
|
||||
RTSanitizerCommonLibc
|
||||
RTSanitizerCommonCoverage
|
||||
RTSanitizerCommonSymbolizer
|
||||
RTSanitizerCommonSymbolizerInternal
|
||||
RTUbsan)
|
||||
|
||||
add_compiler_rt_object_libraries(RTNsan
|
||||
set(NSAN_DYNAMIC_LIBS
|
||||
${COMPILER_RT_UNWINDER_LINK_LIBS}
|
||||
${SANITIZER_CXX_ABI_LIBRARIES}
|
||||
${SANITIZER_COMMON_LINK_LIBS})
|
||||
|
||||
append_list_if(COMPILER_RT_HAS_LIBDL dl NSAN_DYNAMIC_LIBS)
|
||||
append_list_if(COMPILER_RT_HAS_LIBRT rt NSAN_DYNAMIC_LIBS)
|
||||
append_list_if(COMPILER_RT_HAS_LIBM m NSAN_DYNAMIC_LIBS)
|
||||
append_list_if(COMPILER_RT_HAS_LIBPTHREAD pthread NSAN_DYNAMIC_LIBS)
|
||||
|
||||
# Compile sources into an object library.
|
||||
|
||||
add_compiler_rt_object_libraries(RTNsan_dynamic
|
||||
ARCHS ${NSAN_SUPPORTED_ARCH}
|
||||
SOURCES ${NSAN_SOURCES}
|
||||
ADDITIONAL_HEADERS ${NSAN_HEADERS}
|
||||
CFLAGS ${NSAN_CFLAGS})
|
||||
|
||||
if(NOT APPLE)
|
||||
add_compiler_rt_object_libraries(RTNsan
|
||||
ARCHS ${NSAN_SUPPORTED_ARCH}
|
||||
SOURCES ${NSAN_SOURCES}
|
||||
ADDITIONAL_HEADERS ${NSAN_HEADERS}
|
||||
CFLAGS ${NSAN_CFLAGS})
|
||||
|
||||
file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/dummy.cpp "")
|
||||
add_compiler_rt_object_libraries(RTNsan_dynamic_version_script_dummy
|
||||
ARCHS ${NSAN_SUPPORTED_ARCH}
|
||||
SOURCES ${NSAN_SOURCES}
|
||||
ADDITIONAL_HEADERS ${NSAN_HEADERS}
|
||||
CFLAGS ${NSAN_CFLAGS})
|
||||
SOURCES ${CMAKE_CURRENT_BINARY_DIR}/dummy.cpp
|
||||
CFLAGS ${NSAN_DYNAMIC_CFLAGS})
|
||||
endif()
|
||||
|
||||
add_compiler_rt_runtime(
|
||||
clang_rt.nsan
|
||||
STATIC
|
||||
ARCHS ${NSAN_SUPPORTED_ARCH}
|
||||
OBJECT_LIBS RTNsan
|
||||
${NSAN_COMMON_RUNTIME_OBJECT_LIBS}
|
||||
CFLAGS ${NSAN_CFLAGS}
|
||||
PARENT_TARGET nsan)
|
||||
|
||||
if(NOT APPLE)
|
||||
foreach(arch ${NSAN_SUPPORTED_ARCH})
|
||||
if (COMPILER_RT_HAS_VERSION_SCRIPT)
|
||||
add_sanitizer_rt_version_list(clang_rt.nsan-dynamic-${arch}
|
||||
LIBS clang_rt.nsan-${arch}
|
||||
EXTRA nsan.syms.extra)
|
||||
set(VERSION_SCRIPT_FLAG
|
||||
-Wl,--version-script,${CMAKE_CURRENT_BINARY_DIR}/clang_rt.nsan-dynamic-${arch}.vers)
|
||||
# The Solaris 11.4 linker supports a subset of GNU ld version scripts,
|
||||
# but requires a special option to enable it.
|
||||
if (COMPILER_RT_HAS_GNU_VERSION_SCRIPT_COMPAT)
|
||||
list(APPEND VERSION_SCRIPT_FLAG -Wl,-z,gnu-version-script-compat)
|
||||
endif()
|
||||
set_property(SOURCE
|
||||
${CMAKE_CURRENT_BINARY_DIR}/dummy.cpp
|
||||
APPEND PROPERTY
|
||||
OBJECT_DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/clang_rt.nsan-dynamic-${arch}.vers)
|
||||
else()
|
||||
set(VERSION_SCRIPT_FLAG)
|
||||
endif()
|
||||
|
||||
add_compiler_rt_runtime(
|
||||
clang_rt.nsan
|
||||
SHARED
|
||||
ARCHS ${arch}
|
||||
OBJECT_LIBS ${NSAN_COMMON_RUNTIME_OBJECT_LIBS}
|
||||
RTNsan_dynamic
|
||||
# The only purpose of RTNsan_dynamic_version_script_dummy is to
|
||||
# carry a dependency of the shared runtime on the version script.
|
||||
# Replacing it with a straightforward
|
||||
# add_dependencies(clang_rt.nsan-dynamic-${arch} clang_rt.nsan-dynamic-${arch}-version-list)
|
||||
# generates an order-only dependency in ninja.
|
||||
RTNsan_dynamic_version_script_dummy
|
||||
CFLAGS ${NSAN_DYNAMIC_CFLAGS}
|
||||
LINK_FLAGS ${NSAN_DYNAMIC_LINK_FLAGS}
|
||||
${VERSION_SCRIPT_FLAG}
|
||||
LINK_LIBS ${NSAN_DYNAMIC_LIBS}
|
||||
PARENT_TARGET nsan)
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
if(COMPILER_RT_INCLUDE_TESTS)
|
||||
add_subdirectory(tests)
|
||||
|
||||
@@ -55,6 +55,7 @@ extern bool nsan_initialized;
|
||||
extern bool nsan_init_is_running;
|
||||
|
||||
void InitializeInterceptors();
|
||||
void InitializeMallocInterceptors();
|
||||
|
||||
// See notes in nsan_platform.
|
||||
// printf-free (see comment in nsan_interceptors.cc).
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
nsan_*
|
||||
__nsan_*
|
||||
__ubsan_*
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//===-- nsan_interceptors.cc ----------------------------------------------===//
|
||||
//===- 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.
|
||||
@@ -29,26 +29,8 @@ using namespace __sanitizer;
|
||||
using __nsan::nsan_init_is_running;
|
||||
using __nsan::nsan_initialized;
|
||||
|
||||
constexpr uptr kEarlyAllocBufSize = 16384;
|
||||
static uptr allocated_bytes;
|
||||
static char early_alloc_buf[kEarlyAllocBufSize];
|
||||
|
||||
static bool isInEarlyAllocBuf(const void *ptr) {
|
||||
return ((uptr)ptr >= (uptr)early_alloc_buf &&
|
||||
((uptr)ptr - (uptr)early_alloc_buf) < sizeof(early_alloc_buf));
|
||||
}
|
||||
|
||||
template <typename T> T min(T a, T b) { return a < b ? a : b; }
|
||||
|
||||
// Handle allocation requests early (before all interceptors are setup). dlsym,
|
||||
// for example, calls calloc.
|
||||
static void *HandleEarlyAlloc(uptr size) {
|
||||
void *Mem = (void *)&early_alloc_buf[allocated_bytes];
|
||||
allocated_bytes += size;
|
||||
CHECK_LT(allocated_bytes, kEarlyAllocBufSize);
|
||||
return Mem;
|
||||
}
|
||||
|
||||
INTERCEPTOR(void *, memset, void *dst, int v, uptr size) {
|
||||
// NOTE: This guard is needed because nsan's initialization code might call
|
||||
// memset.
|
||||
@@ -105,90 +87,6 @@ INTERCEPTOR(wchar_t *, wmemcpy, wchar_t *dst, const wchar_t *src, uptr size) {
|
||||
return res;
|
||||
}
|
||||
|
||||
INTERCEPTOR(void *, malloc, uptr size) {
|
||||
// NOTE: This guard is needed because nsan's initialization code might call
|
||||
// malloc.
|
||||
if (nsan_init_is_running && REAL(malloc) == nullptr)
|
||||
return HandleEarlyAlloc(size);
|
||||
|
||||
void *res = REAL(malloc)(size);
|
||||
if (res)
|
||||
__nsan_set_value_unknown(static_cast<u8 *>(res), size);
|
||||
return res;
|
||||
}
|
||||
|
||||
INTERCEPTOR(void *, realloc, void *ptr, uptr size) {
|
||||
void *res = REAL(realloc)(ptr, size);
|
||||
// FIXME: We might want to copy the types from the original allocation
|
||||
// (although that would require that we know its size).
|
||||
if (res)
|
||||
__nsan_set_value_unknown(static_cast<u8 *>(res), size);
|
||||
return res;
|
||||
}
|
||||
|
||||
INTERCEPTOR(void *, calloc, uptr Nmemb, uptr size) {
|
||||
// NOTE: This guard is needed because nsan's initialization code might call
|
||||
// calloc.
|
||||
if (nsan_init_is_running && REAL(calloc) == nullptr) {
|
||||
// Note: EarlyAllocBuf is initialized with zeros.
|
||||
return HandleEarlyAlloc(Nmemb * size);
|
||||
}
|
||||
|
||||
void *res = REAL(calloc)(Nmemb, size);
|
||||
if (res)
|
||||
__nsan_set_value_unknown(static_cast<u8 *>(res), Nmemb * size);
|
||||
return res;
|
||||
}
|
||||
|
||||
INTERCEPTOR(void, free, void *P) {
|
||||
// There are only a few early allocation requests, so we simply skip the free.
|
||||
if (isInEarlyAllocBuf(P))
|
||||
return;
|
||||
REAL(free)(P);
|
||||
}
|
||||
|
||||
INTERCEPTOR(void *, valloc, uptr size) {
|
||||
void *const res = REAL(valloc)(size);
|
||||
if (res)
|
||||
__nsan_set_value_unknown(static_cast<u8 *>(res), size);
|
||||
return res;
|
||||
}
|
||||
|
||||
INTERCEPTOR(void *, memalign, uptr align, uptr size) {
|
||||
void *const res = REAL(memalign)(align, size);
|
||||
if (res)
|
||||
__nsan_set_value_unknown(static_cast<u8 *>(res), size);
|
||||
return res;
|
||||
}
|
||||
|
||||
INTERCEPTOR(void *, __libc_memalign, uptr align, uptr size) {
|
||||
void *const res = REAL(__libc_memalign)(align, size);
|
||||
if (res)
|
||||
__nsan_set_value_unknown(static_cast<u8 *>(res), size);
|
||||
return res;
|
||||
}
|
||||
|
||||
INTERCEPTOR(void *, pvalloc, uptr size) {
|
||||
void *const res = REAL(pvalloc)(size);
|
||||
if (res)
|
||||
__nsan_set_value_unknown(static_cast<u8 *>(res), size);
|
||||
return res;
|
||||
}
|
||||
|
||||
INTERCEPTOR(void *, aligned_alloc, uptr align, uptr size) {
|
||||
void *const res = REAL(aligned_alloc)(align, size);
|
||||
if (res)
|
||||
__nsan_set_value_unknown(static_cast<u8 *>(res), size);
|
||||
return res;
|
||||
}
|
||||
|
||||
INTERCEPTOR(int, posix_memalign, void **memptr, uptr align, uptr size) {
|
||||
int res = REAL(posix_memalign)(memptr, align, size);
|
||||
if (res == 0 && *memptr)
|
||||
__nsan_set_value_unknown(static_cast<u8 *>(*memptr), size);
|
||||
return res;
|
||||
}
|
||||
|
||||
INTERCEPTOR(char *, strfry, char *s) {
|
||||
const auto Len = internal_strlen(s);
|
||||
char *res = REAL(strfry)(s);
|
||||
@@ -317,16 +215,7 @@ void __nsan::InitializeInterceptors() {
|
||||
mallopt(-3, 32 * 1024); // M_MMAP_THRESHOLD
|
||||
#endif
|
||||
|
||||
INTERCEPT_FUNCTION(malloc);
|
||||
INTERCEPT_FUNCTION(calloc);
|
||||
INTERCEPT_FUNCTION(free);
|
||||
INTERCEPT_FUNCTION(realloc);
|
||||
INTERCEPT_FUNCTION(valloc);
|
||||
INTERCEPT_FUNCTION(memalign);
|
||||
INTERCEPT_FUNCTION(__libc_memalign);
|
||||
INTERCEPT_FUNCTION(pvalloc);
|
||||
INTERCEPT_FUNCTION(aligned_alloc);
|
||||
INTERCEPT_FUNCTION(posix_memalign);
|
||||
InitializeMallocInterceptors();
|
||||
|
||||
INTERCEPT_FUNCTION(memset);
|
||||
INTERCEPT_FUNCTION(wmemset);
|
||||
|
||||
123
compiler-rt/lib/nsan/nsan_malloc_linux.cpp
Normal file
123
compiler-rt/lib/nsan/nsan_malloc_linux.cpp
Normal file
@@ -0,0 +1,123 @@
|
||||
//===- nsan_malloc_linux.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 memory allocation functions on ELF OSes.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "interception/interception.h"
|
||||
#include "nsan/nsan.h"
|
||||
#include "sanitizer_common/sanitizer_allocator_dlsym.h"
|
||||
#include "sanitizer_common/sanitizer_common.h"
|
||||
#include "sanitizer_common/sanitizer_platform.h"
|
||||
#include "sanitizer_common/sanitizer_platform_interceptors.h"
|
||||
|
||||
#if !SANITIZER_APPLE && !SANITIZER_WINDOWS
|
||||
using namespace __sanitizer;
|
||||
using __nsan::nsan_initialized;
|
||||
|
||||
namespace {
|
||||
struct DlsymAlloc : public DlSymAllocator<DlsymAlloc> {
|
||||
static bool UseImpl() { return !nsan_initialized; }
|
||||
};
|
||||
} // namespace
|
||||
|
||||
INTERCEPTOR(void *, aligned_alloc, uptr align, uptr size) {
|
||||
void *res = REAL(aligned_alloc)(align, size);
|
||||
if (res)
|
||||
__nsan_set_value_unknown(static_cast<u8 *>(res), size);
|
||||
return res;
|
||||
}
|
||||
|
||||
INTERCEPTOR(void *, calloc, uptr nmemb, uptr size) {
|
||||
if (DlsymAlloc::Use())
|
||||
return DlsymAlloc::Callocate(nmemb, size);
|
||||
|
||||
void *res = REAL(calloc)(nmemb, size);
|
||||
if (res)
|
||||
__nsan_set_value_unknown(static_cast<u8 *>(res), nmemb * size);
|
||||
return res;
|
||||
}
|
||||
|
||||
INTERCEPTOR(void, free, void *ptr) {
|
||||
if (DlsymAlloc::PointerIsMine(ptr))
|
||||
return DlsymAlloc::Free(ptr);
|
||||
REAL(free)(ptr);
|
||||
}
|
||||
|
||||
INTERCEPTOR(void *, malloc, uptr size) {
|
||||
if (DlsymAlloc::Use())
|
||||
return DlsymAlloc::Allocate(size);
|
||||
void *res = REAL(malloc)(size);
|
||||
if (res)
|
||||
__nsan_set_value_unknown(static_cast<u8 *>(res), size);
|
||||
return res;
|
||||
}
|
||||
|
||||
INTERCEPTOR(void *, realloc, void *ptr, uptr size) {
|
||||
if (DlsymAlloc::Use() || DlsymAlloc::PointerIsMine(ptr))
|
||||
return DlsymAlloc::Realloc(ptr, size);
|
||||
void *res = REAL(realloc)(ptr, size);
|
||||
// TODO: We might want to copy the types from the original allocation
|
||||
// (although that would require that we know its size).
|
||||
if (res)
|
||||
__nsan_set_value_unknown(static_cast<u8 *>(res), size);
|
||||
return res;
|
||||
}
|
||||
|
||||
#if SANITIZER_INTERCEPT_REALLOCARRAY
|
||||
INTERCEPTOR(void *, reallocarray, void *ptr, uptr nmemb, uptr size) {
|
||||
void *res = REAL(reallocarray)(ptr, nmemb, size);
|
||||
if (res)
|
||||
__nsan_set_value_unknown(static_cast<u8 *>(res), nmemb * size);
|
||||
return res;
|
||||
}
|
||||
#endif // SANITIZER_INTERCEPT_REALLOCARRAY
|
||||
|
||||
INTERCEPTOR(int, posix_memalign, void **memptr, uptr align, uptr size) {
|
||||
int res = REAL(posix_memalign)(memptr, align, size);
|
||||
if (res == 0 && *memptr)
|
||||
__nsan_set_value_unknown(static_cast<u8 *>(*memptr), size);
|
||||
return res;
|
||||
}
|
||||
|
||||
// Deprecated allocation functions (memalign, etc).
|
||||
#if SANITIZER_INTERCEPT_MEMALIGN
|
||||
INTERCEPTOR(void *, memalign, uptr align, uptr size) {
|
||||
void *const res = REAL(memalign)(align, size);
|
||||
if (res)
|
||||
__nsan_set_value_unknown(static_cast<u8 *>(res), size);
|
||||
return res;
|
||||
}
|
||||
|
||||
INTERCEPTOR(void *, __libc_memalign, uptr align, uptr size) {
|
||||
void *const res = REAL(__libc_memalign)(align, size);
|
||||
if (res)
|
||||
__nsan_set_value_unknown(static_cast<u8 *>(res), size);
|
||||
return res;
|
||||
}
|
||||
#endif
|
||||
|
||||
void __nsan::InitializeMallocInterceptors() {
|
||||
INTERCEPT_FUNCTION(aligned_alloc);
|
||||
INTERCEPT_FUNCTION(calloc);
|
||||
INTERCEPT_FUNCTION(free);
|
||||
INTERCEPT_FUNCTION(malloc);
|
||||
INTERCEPT_FUNCTION(posix_memalign);
|
||||
INTERCEPT_FUNCTION(realloc);
|
||||
#if SANITIZER_INTERCEPT_REALLOCARRAY
|
||||
INTERCEPT_FUNCTION(reallocarray);
|
||||
#endif
|
||||
|
||||
#if SANITIZER_INTERCEPT_MEMALIGN
|
||||
INTERCEPT_FUNCTION(memalign);
|
||||
INTERCEPT_FUNCTION(__libc_memalign);
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user