Skip to content

Commit 80ff3ac

Browse files
authored
[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: #98415
1 parent ae9bab5 commit 80ff3ac

File tree

5 files changed

+218
-136
lines changed

5 files changed

+218
-136
lines changed

compiler-rt/lib/nsan/CMakeLists.txt

Lines changed: 91 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ set(NSAN_SOURCES
66
nsan.cpp
77
nsan_flags.cpp
88
nsan_interceptors.cpp
9+
nsan_malloc_linux.cpp
910
nsan_stats.cpp
1011
nsan_suppressions.cpp
1112
)
@@ -24,30 +25,98 @@ append_list_if(COMPILER_RT_HAS_FPIC_FLAG -fPIC NSAN_CFLAGS)
2425
set(NSAN_DYNAMIC_LINK_FLAGS ${SANITIZER_COMMON_LINK_FLAGS})
2526

2627
set(NSAN_CFLAGS ${SANITIZER_COMMON_CFLAGS})
28+
set(NSAN_DYNAMIC_CFLAGS ${NSAN_CFLAGS})
2729

28-
foreach(arch ${NSAN_SUPPORTED_ARCH})
29-
add_compiler_rt_runtime(
30-
clang_rt.nsan
31-
STATIC
32-
ARCHS ${arch}
33-
SOURCES ${NSAN_SOURCES}
34-
$<TARGET_OBJECTS:RTInterception.${arch}>
35-
$<TARGET_OBJECTS:RTSanitizerCommon.${arch}>
36-
$<TARGET_OBJECTS:RTSanitizerCommonLibc.${arch}>
37-
$<TARGET_OBJECTS:RTSanitizerCommonCoverage.${arch}>
38-
$<TARGET_OBJECTS:RTSanitizerCommonSymbolizer.${arch}>
39-
$<TARGET_OBJECTS:RTUbsan.${arch}>
40-
ADDITIONAL_HEADERS ${NSAN_HEADERS}
41-
CFLAGS ${NSAN_CFLAGS}
42-
PARENT_TARGET nsan
43-
)
44-
endforeach()
45-
46-
add_compiler_rt_object_libraries(RTNsan
30+
set(NSAN_COMMON_RUNTIME_OBJECT_LIBS
31+
RTInterception
32+
RTSanitizerCommon
33+
RTSanitizerCommonLibc
34+
RTSanitizerCommonCoverage
35+
RTSanitizerCommonSymbolizer
36+
RTSanitizerCommonSymbolizerInternal
37+
RTUbsan)
38+
39+
set(NSAN_DYNAMIC_LIBS
40+
${COMPILER_RT_UNWINDER_LINK_LIBS}
41+
${SANITIZER_CXX_ABI_LIBRARIES}
42+
${SANITIZER_COMMON_LINK_LIBS})
43+
44+
append_list_if(COMPILER_RT_HAS_LIBDL dl NSAN_DYNAMIC_LIBS)
45+
append_list_if(COMPILER_RT_HAS_LIBRT rt NSAN_DYNAMIC_LIBS)
46+
append_list_if(COMPILER_RT_HAS_LIBM m NSAN_DYNAMIC_LIBS)
47+
append_list_if(COMPILER_RT_HAS_LIBPTHREAD pthread NSAN_DYNAMIC_LIBS)
48+
49+
# Compile sources into an object library.
50+
51+
add_compiler_rt_object_libraries(RTNsan_dynamic
52+
ARCHS ${NSAN_SUPPORTED_ARCH}
53+
SOURCES ${NSAN_SOURCES}
54+
ADDITIONAL_HEADERS ${NSAN_HEADERS}
55+
CFLAGS ${NSAN_CFLAGS})
56+
57+
if(NOT APPLE)
58+
add_compiler_rt_object_libraries(RTNsan
59+
ARCHS ${NSAN_SUPPORTED_ARCH}
60+
SOURCES ${NSAN_SOURCES}
61+
ADDITIONAL_HEADERS ${NSAN_HEADERS}
62+
CFLAGS ${NSAN_CFLAGS})
63+
64+
file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/dummy.cpp "")
65+
add_compiler_rt_object_libraries(RTNsan_dynamic_version_script_dummy
4766
ARCHS ${NSAN_SUPPORTED_ARCH}
48-
SOURCES ${NSAN_SOURCES}
49-
ADDITIONAL_HEADERS ${NSAN_HEADERS}
50-
CFLAGS ${NSAN_CFLAGS})
67+
SOURCES ${CMAKE_CURRENT_BINARY_DIR}/dummy.cpp
68+
CFLAGS ${NSAN_DYNAMIC_CFLAGS})
69+
endif()
70+
71+
add_compiler_rt_runtime(
72+
clang_rt.nsan
73+
STATIC
74+
ARCHS ${NSAN_SUPPORTED_ARCH}
75+
OBJECT_LIBS RTNsan
76+
${NSAN_COMMON_RUNTIME_OBJECT_LIBS}
77+
CFLAGS ${NSAN_CFLAGS}
78+
PARENT_TARGET nsan)
79+
80+
if(NOT APPLE)
81+
foreach(arch ${NSAN_SUPPORTED_ARCH})
82+
if (COMPILER_RT_HAS_VERSION_SCRIPT)
83+
add_sanitizer_rt_version_list(clang_rt.nsan-dynamic-${arch}
84+
LIBS clang_rt.nsan-${arch}
85+
EXTRA nsan.syms.extra)
86+
set(VERSION_SCRIPT_FLAG
87+
-Wl,--version-script,${CMAKE_CURRENT_BINARY_DIR}/clang_rt.nsan-dynamic-${arch}.vers)
88+
# The Solaris 11.4 linker supports a subset of GNU ld version scripts,
89+
# but requires a special option to enable it.
90+
if (COMPILER_RT_HAS_GNU_VERSION_SCRIPT_COMPAT)
91+
list(APPEND VERSION_SCRIPT_FLAG -Wl,-z,gnu-version-script-compat)
92+
endif()
93+
set_property(SOURCE
94+
${CMAKE_CURRENT_BINARY_DIR}/dummy.cpp
95+
APPEND PROPERTY
96+
OBJECT_DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/clang_rt.nsan-dynamic-${arch}.vers)
97+
else()
98+
set(VERSION_SCRIPT_FLAG)
99+
endif()
100+
101+
add_compiler_rt_runtime(
102+
clang_rt.nsan
103+
SHARED
104+
ARCHS ${arch}
105+
OBJECT_LIBS ${NSAN_COMMON_RUNTIME_OBJECT_LIBS}
106+
RTNsan_dynamic
107+
# The only purpose of RTNsan_dynamic_version_script_dummy is to
108+
# carry a dependency of the shared runtime on the version script.
109+
# Replacing it with a straightforward
110+
# add_dependencies(clang_rt.nsan-dynamic-${arch} clang_rt.nsan-dynamic-${arch}-version-list)
111+
# generates an order-only dependency in ninja.
112+
RTNsan_dynamic_version_script_dummy
113+
CFLAGS ${NSAN_DYNAMIC_CFLAGS}
114+
LINK_FLAGS ${NSAN_DYNAMIC_LINK_FLAGS}
115+
${VERSION_SCRIPT_FLAG}
116+
LINK_LIBS ${NSAN_DYNAMIC_LIBS}
117+
PARENT_TARGET nsan)
118+
endforeach()
119+
endif()
51120

52121
if(COMPILER_RT_INCLUDE_TESTS)
53122
add_subdirectory(tests)

compiler-rt/lib/nsan/nsan.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ extern bool nsan_initialized;
5555
extern bool nsan_init_is_running;
5656

5757
void InitializeInterceptors();
58+
void InitializeMallocInterceptors();
5859

5960
// See notes in nsan_platform.
6061
// printf-free (see comment in nsan_interceptors.cc).

compiler-rt/lib/nsan/nsan.syms.extra

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
nsan_*
21
__nsan_*
2+
__ubsan_*

compiler-rt/lib/nsan/nsan_interceptors.cpp

Lines changed: 2 additions & 113 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
//===-- nsan_interceptors.cc ----------------------------------------------===//
1+
//===- nsan_interceptors.cpp ----------------------------------------------===//
22
//
33
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
44
// See https://llvm.org/LICENSE.txt for license information.
@@ -29,26 +29,8 @@ using namespace __sanitizer;
2929
using __nsan::nsan_init_is_running;
3030
using __nsan::nsan_initialized;
3131

32-
constexpr uptr kEarlyAllocBufSize = 16384;
33-
static uptr allocated_bytes;
34-
static char early_alloc_buf[kEarlyAllocBufSize];
35-
36-
static bool isInEarlyAllocBuf(const void *ptr) {
37-
return ((uptr)ptr >= (uptr)early_alloc_buf &&
38-
((uptr)ptr - (uptr)early_alloc_buf) < sizeof(early_alloc_buf));
39-
}
40-
4132
template <typename T> T min(T a, T b) { return a < b ? a : b; }
4233

43-
// Handle allocation requests early (before all interceptors are setup). dlsym,
44-
// for example, calls calloc.
45-
static void *HandleEarlyAlloc(uptr size) {
46-
void *Mem = (void *)&early_alloc_buf[allocated_bytes];
47-
allocated_bytes += size;
48-
CHECK_LT(allocated_bytes, kEarlyAllocBufSize);
49-
return Mem;
50-
}
51-
5234
INTERCEPTOR(void *, memset, void *dst, int v, uptr size) {
5335
// NOTE: This guard is needed because nsan's initialization code might call
5436
// memset.
@@ -105,90 +87,6 @@ INTERCEPTOR(wchar_t *, wmemcpy, wchar_t *dst, const wchar_t *src, uptr size) {
10587
return res;
10688
}
10789

108-
INTERCEPTOR(void *, malloc, uptr size) {
109-
// NOTE: This guard is needed because nsan's initialization code might call
110-
// malloc.
111-
if (nsan_init_is_running && REAL(malloc) == nullptr)
112-
return HandleEarlyAlloc(size);
113-
114-
void *res = REAL(malloc)(size);
115-
if (res)
116-
__nsan_set_value_unknown(static_cast<u8 *>(res), size);
117-
return res;
118-
}
119-
120-
INTERCEPTOR(void *, realloc, void *ptr, uptr size) {
121-
void *res = REAL(realloc)(ptr, size);
122-
// FIXME: We might want to copy the types from the original allocation
123-
// (although that would require that we know its size).
124-
if (res)
125-
__nsan_set_value_unknown(static_cast<u8 *>(res), size);
126-
return res;
127-
}
128-
129-
INTERCEPTOR(void *, calloc, uptr Nmemb, uptr size) {
130-
// NOTE: This guard is needed because nsan's initialization code might call
131-
// calloc.
132-
if (nsan_init_is_running && REAL(calloc) == nullptr) {
133-
// Note: EarlyAllocBuf is initialized with zeros.
134-
return HandleEarlyAlloc(Nmemb * size);
135-
}
136-
137-
void *res = REAL(calloc)(Nmemb, size);
138-
if (res)
139-
__nsan_set_value_unknown(static_cast<u8 *>(res), Nmemb * size);
140-
return res;
141-
}
142-
143-
INTERCEPTOR(void, free, void *P) {
144-
// There are only a few early allocation requests, so we simply skip the free.
145-
if (isInEarlyAllocBuf(P))
146-
return;
147-
REAL(free)(P);
148-
}
149-
150-
INTERCEPTOR(void *, valloc, uptr size) {
151-
void *const res = REAL(valloc)(size);
152-
if (res)
153-
__nsan_set_value_unknown(static_cast<u8 *>(res), size);
154-
return res;
155-
}
156-
157-
INTERCEPTOR(void *, memalign, uptr align, uptr size) {
158-
void *const res = REAL(memalign)(align, size);
159-
if (res)
160-
__nsan_set_value_unknown(static_cast<u8 *>(res), size);
161-
return res;
162-
}
163-
164-
INTERCEPTOR(void *, __libc_memalign, uptr align, uptr size) {
165-
void *const res = REAL(__libc_memalign)(align, size);
166-
if (res)
167-
__nsan_set_value_unknown(static_cast<u8 *>(res), size);
168-
return res;
169-
}
170-
171-
INTERCEPTOR(void *, pvalloc, uptr size) {
172-
void *const res = REAL(pvalloc)(size);
173-
if (res)
174-
__nsan_set_value_unknown(static_cast<u8 *>(res), size);
175-
return res;
176-
}
177-
178-
INTERCEPTOR(void *, aligned_alloc, uptr align, uptr size) {
179-
void *const res = REAL(aligned_alloc)(align, size);
180-
if (res)
181-
__nsan_set_value_unknown(static_cast<u8 *>(res), size);
182-
return res;
183-
}
184-
185-
INTERCEPTOR(int, posix_memalign, void **memptr, uptr align, uptr size) {
186-
int res = REAL(posix_memalign)(memptr, align, size);
187-
if (res == 0 && *memptr)
188-
__nsan_set_value_unknown(static_cast<u8 *>(*memptr), size);
189-
return res;
190-
}
191-
19290
INTERCEPTOR(char *, strfry, char *s) {
19391
const auto Len = internal_strlen(s);
19492
char *res = REAL(strfry)(s);
@@ -317,16 +215,7 @@ void __nsan::InitializeInterceptors() {
317215
mallopt(-3, 32 * 1024); // M_MMAP_THRESHOLD
318216
#endif
319217

320-
INTERCEPT_FUNCTION(malloc);
321-
INTERCEPT_FUNCTION(calloc);
322-
INTERCEPT_FUNCTION(free);
323-
INTERCEPT_FUNCTION(realloc);
324-
INTERCEPT_FUNCTION(valloc);
325-
INTERCEPT_FUNCTION(memalign);
326-
INTERCEPT_FUNCTION(__libc_memalign);
327-
INTERCEPT_FUNCTION(pvalloc);
328-
INTERCEPT_FUNCTION(aligned_alloc);
329-
INTERCEPT_FUNCTION(posix_memalign);
218+
InitializeMallocInterceptors();
330219

331220
INTERCEPT_FUNCTION(memset);
332221
INTERCEPT_FUNCTION(wmemset);

0 commit comments

Comments
 (0)