summaryrefslogtreecommitdiff
path: root/lib/esan
diff options
context:
space:
mode:
authorDerek Bruening <bruening@google.com>2016-06-08 00:00:27 +0000
committerDerek Bruening <bruening@google.com>2016-06-08 00:00:27 +0000
commit61be7625b64a12917039725cfd8969e218706c8a (patch)
tree4d499095ae05cbcae78f573c196fae7eda224260 /lib/esan
parent29078f9b2cb5a3985b84b5fd117b242b259934d6 (diff)
[esan] Intercept calloc to avoid deadlocks with tcmalloc
Summary: When tcmalloc initializes before esan, esan's initialization ends up calling back into tcmalloc due to the calloc done by dlsym. This results in a deadlock. We avoid this by special-casing this single allocation. Intercepting calloc also gives us the opportunity to act on its zeroing as stores by the application. Reviewers: aizatsky Subscribers: vitalybuka, zhaoqin, kcc, eugenis, llvm-commits, kubabrecka Differential Revision: http://reviews.llvm.org/D21086 git-svn-id: https://llvm.org/svn/llvm-project/compiler-rt/trunk@272076 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'lib/esan')
-rw-r--r--lib/esan/esan_interceptors.cpp54
1 files changed, 52 insertions, 2 deletions
diff --git a/lib/esan/esan_interceptors.cpp b/lib/esan/esan_interceptors.cpp
index 15f09c48e..5d4edb51d 100644
--- a/lib/esan/esan_interceptors.cpp
+++ b/lib/esan/esan_interceptors.cpp
@@ -408,6 +408,56 @@ int real_sigaction(int signum, const void *act, void *oldact) {
#define ESAN_MAYBE_INTERCEPT_SIGACTION
#endif
+//===----------------------------------------------------------------------===//
+// Malloc interceptors
+//===----------------------------------------------------------------------===//
+
+static char early_alloc_buf[128];
+static bool used_early_alloc_buf;
+
+static void *handleEarlyAlloc(uptr size) {
+ // If esan is initialized during an interceptor (which happens with some
+ // tcmalloc implementations that call pthread_mutex_lock), the call from
+ // dlsym to calloc will deadlock. There is only one such calloc (dlsym
+ // allocates a single pthread key), so we work around it by using a
+ // static buffer for the calloc request. The loader currently needs
+ // 32 bytes but we size at 128 to allow for future changes.
+ // This solution will also allow us to deliberately intercept malloc & family
+ // in the future (to perform tool actions on each allocation, without
+ // replacing the allocator), as it also solves the problem of intercepting
+ // calloc when it will itself be called before its REAL pointer is
+ // initialized.
+ CHECK(!used_early_alloc_buf && size < sizeof(early_alloc_buf));
+ // We do not handle multiple threads here. This only happens at process init
+ // time, and while it's possible for a shared library to create early threads
+ // that race here, we consider that to be a corner case extreme enough that
+ // it's not worth the effort to handle.
+ used_early_alloc_buf = true;
+ return (void *)early_alloc_buf;
+}
+
+INTERCEPTOR(void*, calloc, uptr size, uptr n) {
+ if (EsanDuringInit && REAL(calloc) == nullptr)
+ return handleEarlyAlloc(size * n);
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, calloc, size, n);
+ void *res = REAL(calloc)(size, n);
+ // The memory is zeroed and thus is all written.
+ COMMON_INTERCEPTOR_WRITE_RANGE(nullptr, (uptr)res, size * n);
+ return res;
+}
+
+INTERCEPTOR(void, free, void *p) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, free, p);
+ if (p == (void *)early_alloc_buf) {
+ // We expect just a singleton use but we clear this for cleanliness.
+ used_early_alloc_buf = false;
+ return;
+ }
+ REAL(free)(p);
+}
+
namespace __esan {
void initializeInterceptors() {
@@ -432,8 +482,8 @@ void initializeInterceptors() {
ESAN_MAYBE_INTERCEPT_SIGNAL;
ESAN_MAYBE_INTERCEPT_SIGACTION;
- // TODO(bruening): we should intercept calloc() and other memory allocation
- // routines that zero memory and update our shadow memory appropriately.
+ INTERCEPT_FUNCTION(calloc);
+ INTERCEPT_FUNCTION(free);
// TODO(bruening): intercept routines that other sanitizers intercept that
// are not in the common pool or here yet, ideally by adding to the common