Skip to content

Commit 4369de7

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"). 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 26d7b7b commit 4369de7

16 files changed

+93
-14
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/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/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
@@ -172,6 +172,7 @@ set(SANITIZER_IMPL_HEADERS
172172
sanitizer_procmaps.h
173173
sanitizer_ptrauth.h
174174
sanitizer_quarantine.h
175+
sanitizer_redefine_builtins.h
175176
sanitizer_report_decorator.h
176177
sanitizer_ring_buffer.h
177178
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: 19 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,14 @@ void *internal_memset(void* s, int c, uptr n) {
95103
}
96104
return s;
97105
}
106+
} // extern "C"
107+
108+
void *internal_memcpy(void *dest, const void *src, uptr n)
109+
ALIAS(__sanitizer_internal_memcpy);
110+
void *internal_memmove(void *dest, const void *src, uptr n)
111+
ALIAS(__sanitizer_internal_memmove);
112+
void *internal_memset(void *s, int c, uptr n)
113+
ALIAS(__sanitizer_internal_memset);
98114

99115
uptr internal_strcspn(const char *s, const char *reject) {
100116
uptr i;
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
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+
#endif // !_MSC_VER || __clang__
25+
26+
#endif // SANITIZER_REDEFINE_BUILTINS_H
27+
#endif // SANITIZER_COMMON_NO_REDEFINE_BUILTINS

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/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})
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; }

0 commit comments

Comments
 (0)