Skip to content

Commit e614d56

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"). Reviewed By: vitalybuka, dvyukov Differential Revision: https://reviews.llvm.org/D151152
1 parent 33ee5c4 commit e614d56

15 files changed

+88
-13
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: 17 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,23 @@ 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+
void *internal_memcpy(void *dest, const void *src, uptr n)
53+
ALIAS(__sanitizer_internal_memcpy);
54+
SANITIZER_INTERFACE_ATTRIBUTE
55+
extern "C" void *__sanitizer_internal_memcpy(void *dest, const void *src,
56+
uptr n) {
5057
char *d = (char*)dest;
5158
const char *s = (const char *)src;
5259
for (uptr i = 0; i < n; ++i)
5360
d[i] = s[i];
5461
return dest;
5562
}
5663

57-
void *internal_memmove(void *dest, const void *src, uptr n) {
64+
void *internal_memmove(void *dest, const void *src, uptr n)
65+
ALIAS(__sanitizer_internal_memmove);
66+
SANITIZER_INTERFACE_ATTRIBUTE
67+
extern "C" void *__sanitizer_internal_memmove(void *dest, const void *src,
68+
uptr n) {
5869
char *d = (char*)dest;
5970
const char *s = (const char *)src;
6071
sptr i, signed_n = (sptr)n;
@@ -72,7 +83,10 @@ void *internal_memmove(void *dest, const void *src, uptr n) {
7283
return dest;
7384
}
7485

75-
void *internal_memset(void* s, int c, uptr n) {
86+
void *internal_memset(void *s, int c, uptr n)
87+
ALIAS(__sanitizer_internal_memset);
88+
SANITIZER_INTERFACE_ATTRIBUTE
89+
extern "C" void *__sanitizer_internal_memset(void *s, int c, uptr n) {
7690
// Optimize for the most performance-critical case:
7791
if ((reinterpret_cast<uptr>(s) % 16) == 0 && (n % 16) == 0) {
7892
u64 *p = reinterpret_cast<u64*>(s);
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

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)