Skip to content

Commit 0a71e25

Browse files
committed
[compiler-rt] Avoid memintrinsic calls inserted by the compiler
D135716 introduced -ftrivial-auto-var-init=pattern where supported. Unfortunately this introduces unwanted memset() for large stack arrays, as shown by the new tests added for asan and msan (tsan already had this test). In general, the problem of compiler-inserted memintrinsic calls (memset/memcpy/memmove) is not new to compiler-rt, and has been a problem before. To avoid introducing unwanted memintrinsic calls, we redefine memintrinsics as __sanitizer_internal_mem* at the assembly level for most source files automatically (where sanitizer_common_internal_defs.h is included). In few cases, redefining a symbol in this way causes issues for interceptors, namely the memintrinsic interceptor themselves. For such source files we have to selectively disable the redefinition. Other alternatives have been considered, but simply do not work well in the context of compiler-rt: 1. Linker --wrap: this does not work because --wrap only applies to the final link, and would not apply when building sanitizer static libraries. 2. Changing references to memset() via objcopy: this may work, but due to the complexities of the build system, introducing such a post-processing step for the right object files (in particular object files defining memset cannot be touched) seems infeasible. The chosen solution works well (as shown by the tests). Other libraries have chosen the same solution where nothing else works (see e.g. glibc's "symbol-hacks.h"). v4: - Add interface attribute to __sanitizer_internal_mem* declarations as well, as otherwise some compilers (MSVC) will complain. - Add SANITIZER_COMMON_NO_REDEFINE_BUILTINS to source files using C++STL, since this could lead to ODR violations (see added comment). v3: - Don't use ALIAS() to alias internal_mem*() functions to __sanitizer_internal_mem*() functions, but just define them as ALWAYS_INLINE functions instead. This will work on darwin and windows. v2: - Fix ubsan_minimal build where compiler decides to insert memset/memcpy: ubsan_minimal has work without RTSanitizerCommonLibc, therefore do not redefine the builtins. - Fix definition of internal_mem* functions with compilers that want the aliased function to already be defined before. - Fix definition of __sanitizer_internal_mem* functions with compilers more pedantic about attribute placement around extern "C". Reviewed By: vitalybuka, dvyukov Differential Revision: https://reviews.llvm.org/D151152
1 parent 06e253c commit 0a71e25

23 files changed

+138
-18
lines changed

compiler-rt/lib/asan/asan_interceptors_memintrinsics.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
// ASan versions of memcpy, memmove, and memset.
1212
//===---------------------------------------------------------------------===//
1313

14+
#define SANITIZER_COMMON_NO_REDEFINE_BUILTINS
15+
1416
#include "asan_interceptors_memintrinsics.h"
1517

1618
#include "asan_interceptors.h"

compiler-rt/lib/asan/tests/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ set(ASAN_UNITTEST_COMMON_CFLAGS
2828
-I${COMPILER_RT_SOURCE_DIR}/lib
2929
-I${COMPILER_RT_SOURCE_DIR}/lib/asan
3030
-I${COMPILER_RT_SOURCE_DIR}/lib/sanitizer_common/tests
31+
-DSANITIZER_COMMON_NO_REDEFINE_BUILTINS
3132
-fno-rtti
3233
-O2
3334
-Wno-format

compiler-rt/lib/hwasan/hwasan_interceptors.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
// sanitizer_common/sanitizer_common_interceptors.h
1515
//===----------------------------------------------------------------------===//
1616

17+
#define SANITIZER_COMMON_NO_REDEFINE_BUILTINS
18+
1719
#include "hwasan.h"
1820
#include "hwasan_allocator.h"
1921
#include "hwasan_checks.h"

compiler-rt/lib/interception/tests/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ set(INTERCEPTION_TEST_CFLAGS_COMMON
1717
-I${COMPILER_RT_SOURCE_DIR}/include
1818
-I${COMPILER_RT_SOURCE_DIR}/lib
1919
-I${COMPILER_RT_SOURCE_DIR}/lib/interception
20+
-DSANITIZER_COMMON_NO_REDEFINE_BUILTINS
2021
-fno-rtti
2122
-O2
2223
-Werror=sign-compare)

compiler-rt/lib/memprof/memprof_interceptors_memintrinsics.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
// MemProf versions of memcpy, memmove, and memset.
1212
//===---------------------------------------------------------------------===//
1313

14+
#define SANITIZER_COMMON_NO_REDEFINE_BUILTINS
15+
1416
#include "memprof_interceptors_memintrinsics.h"
1517

1618
#include "memprof_interceptors.h"

compiler-rt/lib/memprof/tests/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ set(MEMPROF_UNITTEST_CFLAGS
88
${COMPILER_RT_GMOCK_CFLAGS}
99
${SANITIZER_TEST_CXX_CFLAGS}
1010
-I${COMPILER_RT_SOURCE_DIR}/lib/
11+
-DSANITIZER_COMMON_NO_REDEFINE_BUILTINS
1112
-O2
1213
-g
1314
-fno-rtti

compiler-rt/lib/msan/msan_interceptors.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
// sanitizer_common/sanitizer_common_interceptors.h
1515
//===----------------------------------------------------------------------===//
1616

17+
#define SANITIZER_COMMON_NO_REDEFINE_BUILTINS
18+
1719
#include "interception/interception.h"
1820
#include "msan.h"
1921
#include "msan_chained_origin_depot.h"

compiler-rt/lib/sanitizer_common/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,7 @@ set(SANITIZER_IMPL_HEADERS
173173
sanitizer_procmaps.h
174174
sanitizer_ptrauth.h
175175
sanitizer_quarantine.h
176+
sanitizer_redefine_builtins.h
176177
sanitizer_report_decorator.h
177178
sanitizer_ring_buffer.h
178179
sanitizer_signal_interceptors.inc

compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors_memintrinsics.inc

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@
99
// Memintrinsic function interceptors for tools like AddressSanitizer,
1010
// ThreadSanitizer, MemorySanitizer, etc.
1111
//
12+
// These interceptors are part of the common interceptors, but separated out so
13+
// that implementations may add them, if necessary, to a separate source file
14+
// that should define SANITIZER_COMMON_NO_REDEFINE_BUILTINS at the top.
15+
//
1216
// This file should be included into the tool's memintrinsic interceptor file,
1317
// which has to define its own macros:
1418
// COMMON_INTERCEPTOR_ENTER
@@ -20,6 +24,10 @@
2024
// COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED
2125
//===----------------------------------------------------------------------===//
2226

27+
#ifdef SANITIZER_REDEFINE_BUILTINS_H
28+
#error "Define SANITIZER_COMMON_NO_REDEFINE_BUILTINS in .cpp file"
29+
#endif
30+
2331
#include "interception/interception.h"
2432
#include "sanitizer_platform_interceptors.h"
2533

compiler-rt/lib/sanitizer_common/sanitizer_common_interface.inc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,3 +46,7 @@ INTERFACE_FUNCTION(__sanitizer_purge_allocator)
4646
INTERFACE_FUNCTION(__sanitizer_print_memory_profile)
4747
INTERFACE_WEAK_FUNCTION(__sanitizer_free_hook)
4848
INTERFACE_WEAK_FUNCTION(__sanitizer_malloc_hook)
49+
// Memintrinsic functions.
50+
INTERFACE_FUNCTION(__sanitizer_internal_memcpy)
51+
INTERFACE_FUNCTION(__sanitizer_internal_memmove)
52+
INTERFACE_FUNCTION(__sanitizer_internal_memset)

compiler-rt/lib/sanitizer_common/sanitizer_internal_defs.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#define SANITIZER_DEFS_H
1414

1515
#include "sanitizer_platform.h"
16+
#include "sanitizer_redefine_builtins.h"
1617

1718
#ifndef SANITIZER_DEBUG
1819
# define SANITIZER_DEBUG 0

compiler-rt/lib/sanitizer_common/sanitizer_libc.cpp

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@
1010
// run-time libraries. See sanitizer_libc.h for details.
1111
//===----------------------------------------------------------------------===//
1212

13+
// Do not redefine builtins; this file is defining the builtin replacements.
14+
#define SANITIZER_COMMON_NO_REDEFINE_BUILTINS
15+
1316
#include "sanitizer_allocator_internal.h"
1417
#include "sanitizer_common.h"
1518
#include "sanitizer_libc.h"
@@ -46,15 +49,19 @@ int internal_memcmp(const void* s1, const void* s2, uptr n) {
4649
return 0;
4750
}
4851

49-
void *internal_memcpy(void *dest, const void *src, uptr n) {
52+
extern "C" {
53+
SANITIZER_INTERFACE_ATTRIBUTE void *__sanitizer_internal_memcpy(void *dest,
54+
const void *src,
55+
uptr n) {
5056
char *d = (char*)dest;
5157
const char *s = (const char *)src;
5258
for (uptr i = 0; i < n; ++i)
5359
d[i] = s[i];
5460
return dest;
5561
}
5662

57-
void *internal_memmove(void *dest, const void *src, uptr n) {
63+
SANITIZER_INTERFACE_ATTRIBUTE void *__sanitizer_internal_memmove(
64+
void *dest, const void *src, uptr n) {
5865
char *d = (char*)dest;
5966
const char *s = (const char *)src;
6067
sptr i, signed_n = (sptr)n;
@@ -72,7 +79,8 @@ void *internal_memmove(void *dest, const void *src, uptr n) {
7279
return dest;
7380
}
7481

75-
void *internal_memset(void* s, int c, uptr n) {
82+
SANITIZER_INTERFACE_ATTRIBUTE void *__sanitizer_internal_memset(void *s, int c,
83+
uptr n) {
7684
// Optimize for the most performance-critical case:
7785
if ((reinterpret_cast<uptr>(s) % 16) == 0 && (n % 16) == 0) {
7886
u64 *p = reinterpret_cast<u64*>(s);
@@ -95,6 +103,7 @@ void *internal_memset(void* s, int c, uptr n) {
95103
}
96104
return s;
97105
}
106+
} // extern "C"
98107

99108
uptr internal_strcspn(const char *s, const char *reject) {
100109
uptr i;

compiler-rt/lib/sanitizer_common/sanitizer_libc.h

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,15 +24,33 @@ namespace __sanitizer {
2424

2525
// internal_X() is a custom implementation of X() for use in RTL.
2626

27+
extern "C" {
28+
// These are used as builtin replacements; see sanitizer_redefine_builtins.h.
29+
// In normal runtime code, use the __sanitizer::internal_X() aliases instead.
30+
SANITIZER_INTERFACE_ATTRIBUTE void *__sanitizer_internal_memcpy(void *dest,
31+
const void *src,
32+
uptr n);
33+
SANITIZER_INTERFACE_ATTRIBUTE void *__sanitizer_internal_memmove(
34+
void *dest, const void *src, uptr n);
35+
SANITIZER_INTERFACE_ATTRIBUTE void *__sanitizer_internal_memset(void *s, int c,
36+
uptr n);
37+
} // extern "C"
38+
2739
// String functions
2840
s64 internal_atoll(const char *nptr);
2941
void *internal_memchr(const void *s, int c, uptr n);
3042
void *internal_memrchr(const void *s, int c, uptr n);
3143
int internal_memcmp(const void* s1, const void* s2, uptr n);
32-
void *internal_memcpy(void *dest, const void *src, uptr n);
33-
void *internal_memmove(void *dest, const void *src, uptr n);
44+
ALWAYS_INLINE void *internal_memcpy(void *dest, const void *src, uptr n) {
45+
return __sanitizer_internal_memcpy(dest, src, n);
46+
}
47+
ALWAYS_INLINE void *internal_memmove(void *dest, const void *src, uptr n) {
48+
return __sanitizer_internal_memmove(dest, src, n);
49+
}
3450
// Should not be used in performance-critical places.
35-
void *internal_memset(void *s, int c, uptr n);
51+
ALWAYS_INLINE void *internal_memset(void *s, int c, uptr n) {
52+
return __sanitizer_internal_memset(s, c, n);
53+
}
3654
char* internal_strchr(const char *s, int c);
3755
char *internal_strchrnul(const char *s, int c);
3856
int internal_strcmp(const char *s1, const char *s2);
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
//===-- sanitizer_redefine_builtins.h ---------------------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
//
9+
// Redefine builtin functions to use internal versions. This is needed where
10+
// compiler optimizations end up producing unwanted libcalls!
11+
//
12+
//===----------------------------------------------------------------------===//
13+
#ifndef SANITIZER_COMMON_NO_REDEFINE_BUILTINS
14+
#ifndef SANITIZER_REDEFINE_BUILTINS_H
15+
#define SANITIZER_REDEFINE_BUILTINS_H
16+
17+
// The asm hack only works with GCC and Clang.
18+
#if !defined(_MSC_VER) || defined(__clang__)
19+
20+
asm("memcpy = __sanitizer_internal_memcpy");
21+
asm("memmove = __sanitizer_internal_memmove");
22+
asm("memset = __sanitizer_internal_memset");
23+
24+
// The builtins should not be redefined in source files that make use of C++
25+
// standard libraries, in particular where C++STL headers with inline functions
26+
// are used. The redefinition in such cases would lead to ODR violations.
27+
//
28+
// Try to break the build in common cases where builtins shouldn't be redefined.
29+
namespace std {
30+
class Define_SANITIZER_COMMON_NO_REDEFINE_BUILTINS_in_cpp_file {
31+
Define_SANITIZER_COMMON_NO_REDEFINE_BUILTINS_in_cpp_file(
32+
const Define_SANITIZER_COMMON_NO_REDEFINE_BUILTINS_in_cpp_file&) = delete;
33+
Define_SANITIZER_COMMON_NO_REDEFINE_BUILTINS_in_cpp_file& operator=(
34+
const Define_SANITIZER_COMMON_NO_REDEFINE_BUILTINS_in_cpp_file&) = delete;
35+
};
36+
using array = Define_SANITIZER_COMMON_NO_REDEFINE_BUILTINS_in_cpp_file;
37+
using atomic = Define_SANITIZER_COMMON_NO_REDEFINE_BUILTINS_in_cpp_file;
38+
using function = Define_SANITIZER_COMMON_NO_REDEFINE_BUILTINS_in_cpp_file;
39+
using map = Define_SANITIZER_COMMON_NO_REDEFINE_BUILTINS_in_cpp_file;
40+
using set = Define_SANITIZER_COMMON_NO_REDEFINE_BUILTINS_in_cpp_file;
41+
using shared_ptr = Define_SANITIZER_COMMON_NO_REDEFINE_BUILTINS_in_cpp_file;
42+
using string = Define_SANITIZER_COMMON_NO_REDEFINE_BUILTINS_in_cpp_file;
43+
using unique_ptr = Define_SANITIZER_COMMON_NO_REDEFINE_BUILTINS_in_cpp_file;
44+
using unordered_map = Define_SANITIZER_COMMON_NO_REDEFINE_BUILTINS_in_cpp_file;
45+
using unordered_set = Define_SANITIZER_COMMON_NO_REDEFINE_BUILTINS_in_cpp_file;
46+
using vector = Define_SANITIZER_COMMON_NO_REDEFINE_BUILTINS_in_cpp_file;
47+
} // namespace std
48+
49+
#endif // !_MSC_VER || __clang__
50+
51+
#endif // SANITIZER_REDEFINE_BUILTINS_H
52+
#endif // SANITIZER_COMMON_NO_REDEFINE_BUILTINS

compiler-rt/lib/sanitizer_common/tests/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ set(SANITIZER_TEST_CFLAGS_COMMON
7070
-I${COMPILER_RT_SOURCE_DIR}/include
7171
-I${COMPILER_RT_SOURCE_DIR}/lib
7272
-I${COMPILER_RT_SOURCE_DIR}/lib/sanitizer_common
73+
-DSANITIZER_COMMON_NO_REDEFINE_BUILTINS
7374
-fno-rtti
7475
-O2
7576
-Werror=sign-compare

compiler-rt/lib/tsan/rtl/tsan_interceptors_memintrinsics.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
//
1111
//===----------------------------------------------------------------------===//
1212

13+
#define SANITIZER_COMMON_NO_REDEFINE_BUILTINS
14+
1315
#include "tsan_interceptors.h"
1416
#include "tsan_interface.h"
1517

compiler-rt/lib/tsan/tests/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ set(TSAN_UNITTEST_CFLAGS
1111
-I${COMPILER_RT_SOURCE_DIR}/include
1212
-I${COMPILER_RT_SOURCE_DIR}/lib
1313
-I${COMPILER_RT_SOURCE_DIR}/lib/tsan/rtl
14+
-DSANITIZER_COMMON_NO_REDEFINE_BUILTINS
1415
-DGTEST_HAS_RTTI=0
1516
-fno-rtti
1617
)

compiler-rt/lib/ubsan_minimal/CMakeLists.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ set(UBSAN_MINIMAL_SOURCES
66

77
include_directories(..)
88

9-
set(UBSAN_CFLAGS ${SANITIZER_COMMON_CFLAGS})
9+
set(UBSAN_CFLAGS
10+
${SANITIZER_COMMON_CFLAGS}
11+
-DSANITIZER_COMMON_NO_REDEFINE_BUILTINS)
1012
append_rtti_flag(OFF UBSAN_CFLAGS)
1113

1214
set(UBSAN_LINK_FLAGS ${SANITIZER_COMMON_LINK_FLAGS})

compiler-rt/lib/xray/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ include_directories(../../include)
141141
set(XRAY_CFLAGS
142142
${COMPILER_RT_COMMON_CFLAGS}
143143
${COMPILER_RT_CXX_CFLAGS})
144-
set(XRAY_COMMON_DEFINITIONS XRAY_HAS_EXCEPTIONS=1)
144+
set(XRAY_COMMON_DEFINITIONS SANITIZER_COMMON_NO_REDEFINE_BUILTINS XRAY_HAS_EXCEPTIONS=1)
145145

146146
# Too many existing bugs, needs cleanup.
147147
append_list_if(COMPILER_RT_HAS_WNO_FORMAT -Wno-format XRAY_CFLAGS)
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// Verify runtime doesn't contain compiler-emitted memcpy/memmove calls.
2+
//
3+
// REQUIRES: shared_unwind, x86_64-target-arch
4+
5+
// RUN: %clang_asan -O1 %s -o %t
6+
// RUN: llvm-objdump -d -l %t | FileCheck --implicit-check-not="{{(callq|jmpq) .*<(__interceptor_.*)?mem(cpy|set|move)>}}" %s
7+
8+
int main() { return 0; }
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// Verify runtime doesn't contain compiler-emitted memcpy/memmove calls.
2+
//
3+
// REQUIRES: shared_unwind, x86_64-target-arch
4+
5+
// RUN: %clang_msan -O1 %s -o %t
6+
// RUN: llvm-objdump -d -l %t | FileCheck --implicit-check-not="{{(callq|jmpq) .*<(__interceptor_.*)?mem(cpy|set|move)>}}" %s
7+
8+
int main() { return 0; }

compiler-rt/test/tsan/Linux/check_memcpy.c

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,9 @@
55
// This could fail if using a static libunwind because that static libunwind
66
// could be uninstrumented and contain memcpy/memmove calls not intercepted by
77
// tsan.
8-
// REQUIRES: shared_unwind
8+
// REQUIRES: shared_unwind, x86_64-target-arch
99

1010
// RUN: %clang_tsan -O1 %s -o %t
11-
// RUN: llvm-objdump -d -l %t | FileCheck %s
12-
13-
int main() {
14-
return 0;
15-
}
16-
17-
// CHECK-NOT: callq {{.*<(__interceptor_)?mem(cpy|set)>}}
18-
// tail calls:
19-
// CHECK-NOT: jmpq {{.*<(__interceptor_)?mem(cpy|set)>}}
11+
// RUN: llvm-objdump -d -l %t | FileCheck --implicit-check-not="{{(callq|jmpq) .*<(__interceptor_.*)?mem(cpy|set|move)>}}" %s
2012

13+
int main() { return 0; }

llvm/utils/gn/secondary/compiler-rt/lib/sanitizer_common/BUILD.gn

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ source_set("sources") {
114114
"sanitizer_procmaps_solaris.cpp",
115115
"sanitizer_ptrauth.h",
116116
"sanitizer_quarantine.h",
117+
"sanitizer_redefine_builtins.h",
117118
"sanitizer_report_decorator.h",
118119
"sanitizer_ring_buffer.h",
119120
"sanitizer_solaris.cpp",

0 commit comments

Comments
 (0)