[compiler-rt] r200384 - [asan] experimental intercetor for __tls_get_addr. So far it does nothing interesting, actual usage will come later. See https://groups.google.com/forum/#!topic/address-sanitizer/BfwYD8HMxTM for background

Kostya Serebryany kcc at google.com
Wed Jan 29 01:29:17 PST 2014


Author: kcc
Date: Wed Jan 29 03:29:16 2014
New Revision: 200384

URL: http://llvm.org/viewvc/llvm-project?rev=200384&view=rev
Log:
[asan] experimental intercetor for __tls_get_addr. So far it does nothing interesting, actual usage will come later. See https://groups.google.com/forum/#!topic/address-sanitizer/BfwYD8HMxTM for background

Added:
    compiler-rt/trunk/lib/asan/lit_tests/TestCases/Linux/stress_dtls.c
    compiler-rt/trunk/lib/sanitizer_common/sanitizer_tls_get_addr.cc
    compiler-rt/trunk/lib/sanitizer_common/sanitizer_tls_get_addr.h
Modified:
    compiler-rt/trunk/lib/asan/asan_malloc_linux.cc
    compiler-rt/trunk/lib/asan/asan_thread.cc
    compiler-rt/trunk/lib/sanitizer_common/CMakeLists.txt
    compiler-rt/trunk/lib/sanitizer_common/sanitizer_common_interceptors.inc
    compiler-rt/trunk/lib/sanitizer_common/sanitizer_platform_interceptors.h

Modified: compiler-rt/trunk/lib/asan/asan_malloc_linux.cc
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/asan/asan_malloc_linux.cc?rev=200384&r1=200383&r2=200384&view=diff
==============================================================================
--- compiler-rt/trunk/lib/asan/asan_malloc_linux.cc (original)
+++ compiler-rt/trunk/lib/asan/asan_malloc_linux.cc Wed Jan 29 03:29:16 2014
@@ -17,6 +17,7 @@
 #include "sanitizer_common/sanitizer_platform.h"
 #if SANITIZER_LINUX
 
+#include "sanitizer_common/sanitizer_tls_get_addr.h"
 #include "asan_allocator.h"
 #include "asan_interceptors.h"
 #include "asan_internal.h"
@@ -101,8 +102,12 @@ INTERCEPTOR(void*, memalign, uptr bounda
   return asan_memalign(boundary, size, &stack, FROM_MALLOC);
 }
 
-INTERCEPTOR(void*, __libc_memalign, uptr align, uptr s)
-  ALIAS("memalign");
+INTERCEPTOR(void*, __libc_memalign, uptr boundary, uptr size) {
+  GET_STACK_TRACE_MALLOC;
+  void *res = asan_memalign(boundary, size, &stack, FROM_MALLOC);
+  DTLS_on_libc_memalign(res, size * boundary);
+  return res;
+}
 
 INTERCEPTOR(uptr, malloc_usable_size, void *ptr) {
   GET_CURRENT_PC_BP_SP;

Modified: compiler-rt/trunk/lib/asan/asan_thread.cc
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/asan/asan_thread.cc?rev=200384&r1=200383&r2=200384&view=diff
==============================================================================
--- compiler-rt/trunk/lib/asan/asan_thread.cc (original)
+++ compiler-rt/trunk/lib/asan/asan_thread.cc Wed Jan 29 03:29:16 2014
@@ -20,6 +20,7 @@
 #include "sanitizer_common/sanitizer_common.h"
 #include "sanitizer_common/sanitizer_placement_new.h"
 #include "sanitizer_common/sanitizer_stackdepot.h"
+#include "sanitizer_common/sanitizer_tls_get_addr.h"
 #include "lsan/lsan_common.h"
 
 namespace __asan {
@@ -107,6 +108,7 @@ void AsanThread::Destroy() {
   DeleteFakeStack(tid);
   uptr size = RoundUpTo(sizeof(AsanThread), GetPageSizeCached());
   UnmapOrDie(this, size);
+  DTLS_Destroy();
 }
 
 // We want to create the FakeStack lazyly on the first use, but not eralier

Added: compiler-rt/trunk/lib/asan/lit_tests/TestCases/Linux/stress_dtls.c
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/asan/lit_tests/TestCases/Linux/stress_dtls.c?rev=200384&view=auto
==============================================================================
--- compiler-rt/trunk/lib/asan/lit_tests/TestCases/Linux/stress_dtls.c (added)
+++ compiler-rt/trunk/lib/asan/lit_tests/TestCases/Linux/stress_dtls.c Wed Jan 29 03:29:16 2014
@@ -0,0 +1,102 @@
+// REQUIRES: asan-64-bits
+// Stress test dynamic TLS + dlopen + threads.
+//
+// Note that glibc 2.15 seems utterly broken on this test,
+// it fails with ~17 DSOs dlopen-ed.
+// glibc 2.19 seems fine.
+//
+//
+// RUN: %clangxx_asan -x c -DSO_NAME=f0 %s -shared -o %t-f0.so -fPIC
+// RUN: %clangxx_asan -x c -DSO_NAME=f1 %s -shared -o %t-f1.so -fPIC
+// RUN: %clangxx_asan -x c -DSO_NAME=f2 %s -shared -o %t-f2.so -fPIC
+// RUN: %clangxx_asan %s -o %t
+// RUN: %t 0 3
+// RUN: %t 2 3
+// RUN: ASAN_OPTIONS=verbosity=2 %t 2 2 2>&1 | FileCheck %s
+// CHECK: __tls_get_addr
+// CHECK: __tls_get_addr
+// CHECK: __tls_get_addr
+// CHECK: __tls_get_addr
+// CHECK: __tls_get_addr
+/*
+cc=your-compiler
+
+$cc stress_dtls.c -lpthread -ldl
+for((i=0;i<100;i++)); do
+  $cc -fPIC -shared -DSO_NAME=f$i -o a.out-f$i.so stress_dtls.c;
+done
+./a.out 2 4  # <<<<<< 2 threads, 4 libs
+./a.out 3 50 # <<<<<< 3 threads, 50 libs
+*/
+#ifndef SO_NAME
+#define _GNU_SOURCE
+#include <assert.h>
+#include <dlfcn.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <pthread.h>
+#include <stdint.h>
+
+typedef void **(*f_t)();
+
+__thread int my_tls;
+
+#define MAX_N_FUNCTIONS 1000
+f_t Functions[MAX_N_FUNCTIONS];
+
+void *PrintStuff(void *unused) {
+  uintptr_t stack;
+  // fprintf(stderr, "STACK: %p TLS: %p SELF: %p\n", &stack, &my_tls,
+  //        (void *)pthread_self());
+  int i;
+  for (i = 0; i < MAX_N_FUNCTIONS; i++) {
+    if (!Functions[i]) break;
+    uintptr_t dtls = (uintptr_t)Functions[i]();
+    fprintf(stderr, "  dtls[%03d]: %lx\n", i, dtls);
+    *(long*)dtls = 42;  // check that this is writable.
+  }
+  return NULL;
+}
+
+int main(int argc, char *argv[]) {
+  int num_threads = 1;
+  int num_libs = 1;
+  if (argc >= 2)
+    num_threads = atoi(argv[1]);
+  if (argc >= 3)
+    num_libs = atoi(argv[2]);
+  assert(num_libs <= MAX_N_FUNCTIONS);
+
+  int lib;
+  for (lib = 0; lib < num_libs; lib++) {
+    char buf[4096];
+    snprintf(buf, sizeof(buf), "%s-f%d.so", argv[0], lib);
+    void *handle = dlopen(buf, RTLD_LAZY);
+    if (!handle) {
+      fprintf(stderr, "%s\n", dlerror());
+      exit(1);
+    }
+    snprintf(buf, sizeof(buf), "f%d", lib);
+    Functions[lib] = (f_t)dlsym(handle, buf);
+    if (!Functions[lib]) {
+      fprintf(stderr, "%s\n", dlerror());
+      exit(1);
+    }
+    fprintf(stderr, "LIB[%03d] %s: %p\n", lib, buf, Functions[lib]);
+    PrintStuff(0);
+
+    int i;
+    for (i = 0; i < num_threads; i++) {
+      pthread_t t;
+      pthread_create(&t, 0, PrintStuff, 0);
+      pthread_join(t, 0);
+    }
+  }
+  return 0;
+}
+#else  // SO_NAME
+__thread void *huge_thread_local_array[1 << 17];
+void **SO_NAME() {
+  return &huge_thread_local_array[0];
+}
+#endif

Modified: compiler-rt/trunk/lib/sanitizer_common/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/sanitizer_common/CMakeLists.txt?rev=200384&r1=200383&r2=200384&view=diff
==============================================================================
--- compiler-rt/trunk/lib/sanitizer_common/CMakeLists.txt (original)
+++ compiler-rt/trunk/lib/sanitizer_common/CMakeLists.txt Wed Jan 29 03:29:16 2014
@@ -22,6 +22,7 @@ set(SANITIZER_SOURCES
   sanitizer_symbolizer.cc
   sanitizer_symbolizer_libbacktrace.cc
   sanitizer_symbolizer_win.cc
+  sanitizer_tls_get_addr.cc
   sanitizer_thread_registry.cc
   sanitizer_win.cc)
 

Modified: compiler-rt/trunk/lib/sanitizer_common/sanitizer_common_interceptors.inc
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/sanitizer_common/sanitizer_common_interceptors.inc?rev=200384&r1=200383&r2=200384&view=diff
==============================================================================
--- compiler-rt/trunk/lib/sanitizer_common/sanitizer_common_interceptors.inc (original)
+++ compiler-rt/trunk/lib/sanitizer_common/sanitizer_common_interceptors.inc Wed Jan 29 03:29:16 2014
@@ -29,6 +29,7 @@
 //===----------------------------------------------------------------------===//
 #include "interception/interception.h"
 #include "sanitizer_platform_interceptors.h"
+#include "sanitizer_tls_get_addr.h"
 
 #include <stdarg.h>
 
@@ -3063,10 +3064,23 @@ INTERCEPTOR(__sanitizer_clock_t, times,
   return res;
 }
 #define INIT_TIMES COMMON_INTERCEPT_FUNCTION(times);
+INTERCEPTOR(void *, __tls_get_addr, void *arg) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, __tls_get_addr, arg);
+  void *res = REAL(__tls_get_addr)(arg);
+  DTLS_on_tls_get_addr(arg, res);
+  return res;
+}
 #else
 #define INIT_TIMES
 #endif
 
+#if SANITIZER_INTERCEPT_TLS_GET_ADDR
+#define INIT_TLS_GET_ADDR COMMON_INTERCEPT_FUNCTION(__tls_get_addr)
+#else
+#define INIT_TLS_GET_ADDR
+#endif
+
 #define SANITIZER_COMMON_INTERCEPTORS_INIT \
   INIT_TEXTDOMAIN;                         \
   INIT_STRCMP;                             \
@@ -3182,4 +3196,5 @@ INTERCEPTOR(__sanitizer_clock_t, times,
   INIT_GETLINE;                            \
   INIT_ICONV;                              \
   INIT_TIMES;                              \
+  INIT_TLS_GET_ADDR;                       \
 /**/

Modified: compiler-rt/trunk/lib/sanitizer_common/sanitizer_platform_interceptors.h
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/sanitizer_common/sanitizer_platform_interceptors.h?rev=200384&r1=200383&r2=200384&view=diff
==============================================================================
--- compiler-rt/trunk/lib/sanitizer_common/sanitizer_platform_interceptors.h (original)
+++ compiler-rt/trunk/lib/sanitizer_common/sanitizer_platform_interceptors.h Wed Jan 29 03:29:16 2014
@@ -177,4 +177,6 @@
 #define SANITIZER_INTERCEPT_PTHREAD_COND SI_NOT_WINDOWS
 #define SANITIZER_INTERCEPT_PTHREAD_SETNAME_NP SI_LINUX_NOT_ANDROID
 
+#define SANITIZER_INTERCEPT_TLS_GET_ADDR SI_LINUX_NOT_ANDROID
+
 #endif  // #ifndef SANITIZER_PLATFORM_INTERCEPTORS_H

Added: compiler-rt/trunk/lib/sanitizer_common/sanitizer_tls_get_addr.cc
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/sanitizer_common/sanitizer_tls_get_addr.cc?rev=200384&view=auto
==============================================================================
--- compiler-rt/trunk/lib/sanitizer_common/sanitizer_tls_get_addr.cc (added)
+++ compiler-rt/trunk/lib/sanitizer_common/sanitizer_tls_get_addr.cc Wed Jan 29 03:29:16 2014
@@ -0,0 +1,114 @@
+//===-- sanitizer_tls_get_addr.cc -----------------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Handle the __tls_get_addr call.
+//
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_tls_get_addr.h"
+
+#include "sanitizer_flags.h"
+#include "sanitizer_platform_interceptors.h"
+
+namespace __sanitizer {
+#if SANITIZER_INTERCEPT_TLS_GET_ADDR
+
+// The actual parameter that comes to __tls_get_addr
+// is a pointer to a struct with two words in it:
+struct TlsGetAddrParam {
+  uptr dso_id;
+  uptr offset;
+};
+
+// Glibc starting from 2.19 allocates tls using __signal_safe_memalign,
+// which has such header.
+struct Glibc_2_19_tls_header {
+  uptr size;
+  uptr start;
+};
+
+// This must be static TLS, i.e. the run-time should be build with
+// -ftls-model=initial-exec or equivalent.
+static __thread DTLS dtls;
+
+// Make sure we properly destroy the DTLS objects:
+// this counter should never get too large.
+static atomic_uintptr_t number_of_live_dtls;
+
+static const uptr kDestroyedThread = -1;
+
+static inline void DTLS_Resize(uptr new_size) {
+  if (dtls.dtv_size >= new_size) return;
+  new_size = RoundUpToPowerOfTwo(new_size);
+  new_size = Max(new_size, 4096UL / sizeof(DTLS::DTV));
+  DTLS::DTV *new_dtv =
+      (DTLS::DTV *)MmapOrDie(new_size * sizeof(DTLS::DTV), "DTLS_Resize");
+  CHECK_LT(atomic_fetch_add(&number_of_live_dtls, 1, memory_order_relaxed),
+           1 << 20);
+  if (dtls.dtv_size)
+    internal_memcpy(new_dtv, dtls.dtv, dtls.dtv_size * sizeof(DTLS::DTV));
+  DTLS_Destroy();
+  dtls.dtv = new_dtv;
+  dtls.dtv_size = new_size;
+}
+
+void DTLS_Destroy() {
+  if (!dtls.dtv_size) return;
+  UnmapOrDie(dtls.dtv, dtls.dtv_size * sizeof(DTLS::DTV));
+  atomic_fetch_sub(&number_of_live_dtls, 1, memory_order_relaxed);
+  dtls.dtv_size = kDestroyedThread;
+}
+
+void DTLS_on_tls_get_addr(void *arg_void, void *res) {
+  TlsGetAddrParam *arg = reinterpret_cast<TlsGetAddrParam *>(arg_void);
+  uptr dso_id = arg->dso_id;
+  if (dtls.dtv_size == kDestroyedThread) return;
+  DTLS_Resize(dso_id + 1);
+  if (dtls.dtv[dso_id].beg)
+    return;
+  uptr tls_size = 0;
+  uptr tls_beg = reinterpret_cast<uptr>(res) - arg->offset;
+  VPrintf(2, "__tls_get_addr: %p {%p,%p} => %p; tls_beg: %p\n", arg,
+          arg->dso_id, arg->offset, res, tls_beg);
+  if (dtls.last_memalign_ptr == tls_beg) {
+    tls_size = dtls.last_memalign_size;
+    VPrintf(2, "__tls_get_addr: glibc <=2.18 suspected; tls={%p,%p}\n", tls_beg,
+            tls_size);
+  } else if ((tls_beg % 4096) == sizeof(Glibc_2_19_tls_header)) {
+    // We may want to check gnu_get_libc_version().
+    Glibc_2_19_tls_header *header = (Glibc_2_19_tls_header *)tls_beg - 1;
+    tls_size = header->size;
+    tls_beg = header->start;
+    VPrintf(2, "__tls_get_addr: glibc >=2.19 suspected; tls={%p %p}\n", tls_beg,
+            tls_size);
+  } else {
+    VPrintf(2, "__tls_get_addr: Can't guess glibc version\n");
+    // This may happen inside the DTOR of main thread, so just ignore it.
+    tls_size = 0;
+  }
+  dtls.dtv[dso_id].beg = tls_beg;
+  dtls.dtv[dso_id].size = tls_size;
+}
+
+void DTLS_on_libc_memalign(void *ptr, uptr size) {
+  VPrintf(2, "DTLS_on_libc_memalign: %p %p\n", ptr, size);
+  dtls.last_memalign_ptr = reinterpret_cast<uptr>(ptr);
+  dtls.last_memalign_size = size;
+}
+
+DTLS *DTLS_Get() { return &dtls; }
+
+#else
+void DTLS_on_libc_memalign(void *ptr, uptr size) {}
+void DTLS_on_tls_get_addr(void *arg, void *res) {}
+DTLS *DTLS_Get() { return 0; }
+void DTLS_Destroy() {}
+#endif  // SANITIZER_INTERCEPT_TLS_GET_ADDR
+
+}  // namespace __sanitizer

Added: compiler-rt/trunk/lib/sanitizer_common/sanitizer_tls_get_addr.h
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/sanitizer_common/sanitizer_tls_get_addr.h?rev=200384&view=auto
==============================================================================
--- compiler-rt/trunk/lib/sanitizer_common/sanitizer_tls_get_addr.h (added)
+++ compiler-rt/trunk/lib/sanitizer_common/sanitizer_tls_get_addr.h Wed Jan 29 03:29:16 2014
@@ -0,0 +1,58 @@
+//===-- sanitizer_tls_get_addr.h --------------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Handle the __tls_get_addr call.
+//
+// All this magic is specific to glibc and is required to workaround
+// the lack of interface that would tell us about the Dynamic TLS (DTLS).
+// https://sourceware.org/bugzilla/show_bug.cgi?id=16291
+//
+// The matters get worse because the glibc implementation changed between
+// 2.18 and 2.19:
+// https://groups.google.com/forum/#!topic/address-sanitizer/BfwYD8HMxTM
+//
+// Before 2.19, every DTLS chunk is allocated with __libc_memalign,
+// which we intercept and thus know where is the DTLS.
+// Since 2.19, DTLS chunks are allocated with __signal_safe_memalign,
+// which is an internal function that wraps a mmap call, neither of which
+// we can intercept. Luckily, __signal_safe_memalign has a simple parseable
+// header which we can use.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef SANITIZER_TLS_GET_ADDR_H
+#define SANITIZER_TLS_GET_ADDR_H
+
+#include "sanitizer_common.h"
+
+namespace __sanitizer {
+
+struct DTLS {
+  // Array of DTLS chunks for the current Thread.
+  // If beg == 0, the chunk is unused.
+  struct DTV {
+    uptr beg, size;
+  };
+
+  uptr dtv_size;
+  DTV *dtv;  // dtv_size elements, allocated by MmapOrDie.
+
+  // Auxilary fields, don't access them outside sanitizer_tls_get_addr.cc
+  uptr last_memalign_size;
+  uptr last_memalign_ptr;
+};
+
+void DTLS_on_tls_get_addr(void *arg, void *res);
+void DTLS_on_libc_memalign(void *ptr, uptr size);
+DTLS *DTLS_Get();
+void DTLS_Destroy();  // Make sure to call this before the thread is destroyed.
+
+}  // namespace __sanitizer
+
+#endif  // SANITIZER_TLS_GET_ADDR_H





More information about the llvm-commits mailing list