[nsan] Add nsan_preinit.cpp and make it static library only

#94322 defines .preinit_array to initialize nsan early.
DT_PREINIT_ARRAY can only be used with the main executable. GNU ld would
complain when a DSO has .preinit_array. Therefore,
nsan_preinit.cpp cannot be linked into `libclang_rt.nsan.so` (#98415).

Working with @alexander-shaposhnikov, we noticed that `Nsan-x86_64-Test
--gtest_output=json` without `.preinit_array` will sigsegv. This is
because googletest with the JSON output calls `localtime_r` , which
calls `free(0)` and fails when `REAL(free)` remains uninitialized
(nullptr). This is benign with the default output because malloc/free
are all paired and `REAL(free)(ptr)` is not called.

To fix the unittest failure, `__nsan_init` needs to be called early
(.preinit_array).
`asan/tests/CMakeLists.txt:ASAN_UNITTEST_INSTRUMENTED_LINK_FLAGS` ues
`-fsanitize=address` to ensure `asan_preinit.cpp.o` is linked into the
unittest executable. Port the approach and remove
`NSAN_TEST_RUNTIME_OBJECTS`.

Fix #98523

Pull Request: https://github.com/llvm/llvm-project/pull/98564
This commit is contained in:
Fangrui Song 2024-07-11 18:22:52 -07:00 committed by GitHub
parent 1bafe77d77
commit a853fe25df
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 36 additions and 24 deletions

View File

@ -11,6 +11,9 @@ set(NSAN_SOURCES
nsan_suppressions.cpp
)
set(NSAN_PREINIT_SOURCES
nsan_preinit.cpp)
set(NSAN_HEADERS
nsan.h
nsan_flags.h
@ -61,6 +64,12 @@ if(NOT APPLE)
ADDITIONAL_HEADERS ${NSAN_HEADERS}
CFLAGS ${NSAN_CFLAGS})
add_compiler_rt_object_libraries(RTNsan_preinit
ARCHS ${NSAN_SUPPORTED_ARCH}
SOURCES ${NSAN_PREINIT_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}
@ -72,7 +81,7 @@ add_compiler_rt_runtime(
clang_rt.nsan
STATIC
ARCHS ${NSAN_SUPPORTED_ARCH}
OBJECT_LIBS RTNsan
OBJECT_LIBS RTNsan_preinit RTNsan
${NSAN_COMMON_RUNTIME_OBJECT_LIBS}
CFLAGS ${NSAN_CFLAGS}
PARENT_TARGET nsan)

View File

@ -801,8 +801,3 @@ extern "C" SANITIZER_INTERFACE_ATTRIBUTE void __nsan_init() {
nsan_init_is_running = false;
nsan_initialized = true;
}
#if SANITIZER_CAN_USE_PREINIT_ARRAY
__attribute__((section(".preinit_array"),
used)) static void (*nsan_init_ptr)() = __nsan_init;
#endif

View File

@ -32,6 +32,8 @@ using __sanitizer::uptr;
// Private nsan interface. Used e.g. by interceptors.
extern "C" {
void __nsan_init();
// This marks the shadow type of the given block of application memory as
// unknown.
// printf-free (see comment in nsan_interceptors.cc).

View File

@ -0,0 +1,21 @@
//===- nsan_preinit.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
//
//===----------------------------------------------------------------------===//
//
// Call __nsan_init early using ELF DT_PREINIT_ARRAY.
//
//===----------------------------------------------------------------------===//
#include "nsan/nsan.h"
#include "sanitizer_common/sanitizer_internal_defs.h"
#if SANITIZER_CAN_USE_PREINIT_ARRAY
__attribute__((section(".preinit_array"), used)) static auto nsan_preinit =
__nsan_init;
#endif

View File

@ -15,6 +15,8 @@ set(NSAN_UNITTEST_LINK_FLAGS
${COMPILER_RT_UNITTEST_LINK_FLAGS}
${COMPILER_RT_UNWINDER_LINK_LIBS}
${SANITIZER_TEST_CXX_LIBRARIES})
set(NSAN_UNITTEST_INSTRUMENTED_LINK_FLAGS ${NSAN_UNITTEST_LINK_FLAGS})
list(APPEND NSAN_UNITTEST_INSTRUMENTED_LINK_FLAGS -fsanitize=numerical)
file(GLOB NSAN_HEADERS ../*.h)
set(NSAN_UNITTESTS
@ -27,30 +29,13 @@ if(COMPILER_RT_DEFAULT_TARGET_ARCH IN_LIST NSAN_SUPPORTED_ARCH)
# NSan unit tests are only run on the host machine.
set(arch ${COMPILER_RT_DEFAULT_TARGET_ARCH})
set(NSAN_TEST_RUNTIME RTNsanTest.${arch})
set(NSAN_TEST_RUNTIME_OBJECTS
$<TARGET_OBJECTS:RTNsan.${arch}>
$<TARGET_OBJECTS:RTInterception.${arch}>
$<TARGET_OBJECTS:RTSanitizerCommon.${arch}>
$<TARGET_OBJECTS:RTSanitizerCommonLibc.${arch}>
$<TARGET_OBJECTS:RTSanitizerCommonSymbolizer.${arch}>)
add_library(${NSAN_TEST_RUNTIME} STATIC
${NSAN_TEST_RUNTIME_OBJECTS})
set_target_properties(${NSAN_TEST_RUNTIME} PROPERTIES
ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
FOLDER "Compiler-RT Runtime tests")
set(NsanTestObjects)
generate_compiler_rt_tests(NsanTestObjects
NsanUnitTests "Nsan-${arch}-Test" ${arch}
SOURCES ${NSAN_UNITTESTS} ${COMPILER_RT_GTEST_SOURCE}
RUNTIME ${NSAN_TEST_RUNTIME}
DEPS ${NSAN_UNIT_TEST_HEADERS}
CFLAGS ${NSAN_UNITTEST_CFLAGS}
LINK_FLAGS ${NSAN_UNITTEST_LINK_FLAGS})
LINK_FLAGS ${NSAN_UNITTEST_INSTRUMENTED_LINK_FLAGS})
set_target_properties(NsanUnitTests PROPERTIES
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
endif()