[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 02:32:29 PST 2014
Yea. Turns out I've put the interceptor under the wrong ifdef,
r200386 should fix this.
--kcc
On Wed, Jan 29, 2014 at 2:16 PM, Alexander Potapenko <glider at google.com> wrote:
> Note that __tls_get_addr is unavailable on OSX
>
> On Wed, Jan 29, 2014 at 1:29 PM, Kostya Serebryany <kcc at google.com> wrote:
>> 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
>>
>>
>> _______________________________________________
>> llvm-commits mailing list
>> llvm-commits at cs.uiuc.edu
>> http://lists.cs.uiuc.edu/mailman/listinfo/llvm-commits
>
>
>
> --
> Alexander Potapenko
> Software Engineer
> Google Moscow
More information about the llvm-commits
mailing list