llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-clang-driver Author: Fangrui Song (MaskRay) <details> <summary>Changes</summary> 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. Note: The driver change will be pushed as a separate commit. --- Full diff: https://github.com/llvm/llvm-project/pull/98415.diff 8 Files Affected: - (modified) clang/lib/Driver/SanitizerArgs.cpp (+2-2) - (modified) clang/lib/Driver/ToolChains/CommonArgs.cpp (+3-1) - (modified) clang/test/Driver/sanitizer-ld.c (+18) - (modified) compiler-rt/lib/nsan/CMakeLists.txt (+91-22) - (modified) compiler-rt/lib/nsan/nsan.h (+1) - (modified) compiler-rt/lib/nsan/nsan.syms.extra (+1-1) - (modified) compiler-rt/lib/nsan/nsan_interceptors.cpp (+2-113) - (added) compiler-rt/lib/nsan/nsan_malloc_linux.cpp (+137) ``````````diff diff --git a/clang/lib/Driver/SanitizerArgs.cpp b/clang/lib/Driver/SanitizerArgs.cpp index 86825a6ccf7a1..1fd870b72286e 100644 --- a/clang/lib/Driver/SanitizerArgs.cpp +++ b/clang/lib/Driver/SanitizerArgs.cpp @@ -285,8 +285,8 @@ bool SanitizerArgs::needsFuzzerInterceptors() const { bool SanitizerArgs::needsUbsanRt() const { // All of these include ubsan. - if (needsAsanRt() || needsMsanRt() || needsHwasanRt() || needsTsanRt() || - needsDfsanRt() || needsLsanRt() || needsCfiDiagRt() || + if (needsAsanRt() || needsMsanRt() || needsNsanRt() || needsHwasanRt() || + needsTsanRt() || needsDfsanRt() || needsLsanRt() || needsCfiDiagRt() || (needsScudoRt() && !requiresMinimalRuntime())) return false; diff --git a/clang/lib/Driver/ToolChains/CommonArgs.cpp b/clang/lib/Driver/ToolChains/CommonArgs.cpp index ab1590104b790..80a2b2bf31183 100644 --- a/clang/lib/Driver/ToolChains/CommonArgs.cpp +++ b/clang/lib/Driver/ToolChains/CommonArgs.cpp @@ -1409,6 +1409,8 @@ collectSanitizerRuntimes(const ToolChain &TC, const ArgList &Args, if (!Args.hasArg(options::OPT_shared) && !TC.getTriple().isAndroid()) HelperStaticRuntimes.push_back("memprof-preinit"); } + if (SanArgs.needsNsanRt()) + SharedRuntimes.push_back("nsan"); if (SanArgs.needsUbsanRt()) { if (SanArgs.requiresMinimalRuntime()) SharedRuntimes.push_back("ubsan_minimal"); @@ -1479,7 +1481,7 @@ collectSanitizerRuntimes(const ToolChain &TC, const ArgList &Args, if (SanArgs.linkCXXRuntimes()) StaticRuntimes.push_back("msan_cxx"); } - if (SanArgs.needsNsanRt()) + if (!SanArgs.needsSharedRt() && SanArgs.needsNsanRt()) StaticRuntimes.push_back("nsan"); if (!SanArgs.needsSharedRt() && SanArgs.needsTsanRt()) { StaticRuntimes.push_back("tsan"); diff --git a/clang/test/Driver/sanitizer-ld.c b/clang/test/Driver/sanitizer-ld.c index e215c034070e0..48e4ea1f7dcb5 100644 --- a/clang/test/Driver/sanitizer-ld.c +++ b/clang/test/Driver/sanitizer-ld.c @@ -646,6 +646,24 @@ // CHECK-NSAN-LINUX: libclang_rt.nsan.a" // CHECK-NSAN-LINUX: "-lpthread" "-lrt" "-lm" "-ldl" "-lresolv" +// RUN: %clang -### %s 2>&1 --target=x86_64-unknown-linux -fuse-ld=ld -fsanitize=numerical -shared-libsan \ +// RUN: -resource-dir=%S/Inputs/resource_dir \ +// RUN: --sysroot=%S/Inputs/basic_linux_tree \ +// RUN: | FileCheck --check-prefix=CHECK-NSAN-SHARED-LINUX %s + +// CHECK-NSAN-SHARED-LINUX: libclang_rt.nsan.so" +// CHECK-NSAN-SHARED-LINUX-NOT: "-lpthread" +// CHECK-NSAN-SHARED-LINUX-NOT: "-ldl" +// CHECK-NSAN-SHARED-LINUX-NOT: "--dynamic-list + +// RUN: %clang -### %s 2>&1 --target=x86_64-unknown-linux -fsanitize=numerical,undefined \ +// RUN: -resource-dir=%S/Inputs/resource_dir \ +// RUN: --sysroot=%S/Inputs/basic_linux_tree \ +// RUN: | FileCheck --check-prefix=CHECK-NSAN-UBSAN %s + +// CHECK-NSAN-UBSAN: "--whole-archive" "{{[^"]*}}libclang_rt.nsan.a" "--no-whole-archive" +// CHECK-NSAN-UBSAN-NOT: libclang_rt.ubsan + // CFI by itself does not link runtime libraries. // RUN: not %clang -fsanitize=cfi -### %s 2>&1 \ // RUN: --target=x86_64-unknown-linux -fuse-ld=ld -rtlib=platform \ diff --git a/compiler-rt/lib/nsan/CMakeLists.txt b/compiler-rt/lib/nsan/CMakeLists.txt index aef9b651ab2ec..d67c3ac434d3a 100644 --- a/compiler-rt/lib/nsan/CMakeLists.txt +++ b/compiler-rt/lib/nsan/CMakeLists.txt @@ -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() - -add_compiler_rt_object_libraries(RTNsan +set(NSAN_COMMON_RUNTIME_OBJECT_LIBS + RTInterception + RTSanitizerCommon + RTSanitizerCommonLibc + RTSanitizerCommonCoverage + RTSanitizerCommonSymbolizer + RTSanitizerCommonSymbolizerInternal + RTUbsan) + +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) diff --git a/compiler-rt/lib/nsan/nsan.h b/compiler-rt/lib/nsan/nsan.h index 896e5379dfc37..0fb998b049854 100644 --- a/compiler-rt/lib/nsan/nsan.h +++ b/compiler-rt/lib/nsan/nsan.h @@ -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). diff --git a/compiler-rt/lib/nsan/nsan.syms.extra b/compiler-rt/lib/nsan/nsan.syms.extra index f3be6d39736b7..53ce5833520b8 100644 --- a/compiler-rt/lib/nsan/nsan.syms.extra +++ b/compiler-rt/lib/nsan/nsan.syms.extra @@ -1,2 +1,2 @@ -nsan_* __nsan_* +__ubsan_* diff --git a/compiler-rt/lib/nsan/nsan_interceptors.cpp b/compiler-rt/lib/nsan/nsan_interceptors.cpp index 68127f169ee46..544b44f53cc42 100644 --- a/compiler-rt/lib/nsan/nsan_interceptors.cpp +++ b/compiler-rt/lib/nsan/nsan_interceptors.cpp @@ -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); diff --git a/compiler-rt/lib/nsan/nsan_malloc_linux.cpp b/compiler-rt/lib/nsan/nsan_malloc_linux.cpp new file mode 100644 index 0000000000000..3bfc16f69b9be --- /dev/null +++ b/compiler-rt/lib/nsan/nsan_malloc_linux.cpp @@ -0,0 +1,137 @@ +//===- 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; + +struct DlsymAlloc : public DlSymAllocator<DlsymAlloc> { + static bool UseImpl() { return !nsan_initialized; } +}; + +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 *, 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; +} + +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(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; +} + +#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), size); +} +#endif // SANITIZER_INTERCEPT_REALLOCARRAY + +// Deprecated allocation functions (memalign, pvalloc, valloc, 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 + +#if SANITIZER_INTERCEPT_PVALLOC +INTERCEPTOR(void *, pvalloc, uptr size) { + void *const res = REAL(pvalloc)(size); + if (res) + __nsan_set_value_unknown(static_cast<u8 *>(res), size); + return res; +} +#endif + +INTERCEPTOR(void *, valloc, uptr size) { + void *const res = REAL(valloc)(size); + if (res) + __nsan_set_value_unknown(static_cast<u8 *>(res), size); + return res; +} + +void __nsan::InitializeMallocInterceptors() { + INTERCEPT_FUNCTION(aligned_alloc); + INTERCEPT_FUNCTION(calloc); + INTERCEPT_FUNCTION(free); + INTERCEPT_FUNCTION(malloc); + INTERCEPT_FUNCTION(posix_memalign); + INTERCEPT_FUNCTION(realloc); + + INTERCEPT_FUNCTION(memalign); + INTERCEPT_FUNCTION(__libc_memalign); + INTERCEPT_FUNCTION(pvalloc); + INTERCEPT_FUNCTION(valloc); +#if SANITIZER_INTERCEPT_REALLOCARRAY + INTERCEPT_FUNCTION(reallocarray); +#endif +} + +#endif `````````` </details> https://github.com/llvm/llvm-project/pull/98415 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits