Skip to content

Commit

Permalink
Revert "[sanitizer] Simplify GetTls with dl_iterate_phdr"
Browse files Browse the repository at this point in the history
This reverts commit 9be8f8b.
This breaks tsan on Ubuntu 16.04:

    $ cat tiny_race.c
    #include <pthread.h>
    int Global;
    void *Thread1(void *x) {
      Global = 42;
      return x;
    }
    int main() {
      pthread_t t;
      pthread_create(&t, NULL, Thread1, NULL);
      Global = 43;
      pthread_join(t, NULL);
      return Global;
    }
    $ out/gn/bin/clang -fsanitize=thread -g -O1 tiny_race.c --sysroot ~/src/chrome/src/build/linux/debian_sid_amd64-sysroot/
    $ docker run -v $PWD:/foo ubuntu:xenial /foo/a.out
    FATAL: ThreadSanitizer CHECK failed: ../../compiler-rt/lib/tsan/rtl/tsan_platform_linux.cpp:447 "((thr_beg)) >= ((tls_addr))" (0x7fddd76beb80, 0xfffffffffffff980)
        #0 <null> <null> (a.out+0x4960b6)
        #1 <null> <null> (a.out+0x4b677f)
        rust-lang#2 <null> <null> (a.out+0x49cf94)
        rust-lang#3 <null> <null> (a.out+0x499bd2)
        rust-lang#4 <null> <null> (a.out+0x42aaf1)
        rust-lang#5 <null> <null> (libpthread.so.0+0x76b9)
        rust-lang#6 <null> <null> (libc.so.6+0x1074dc)

(Get the sysroot from here: https://commondatastorage.googleapis.com/chrome-linux-sysroot/toolchain/500976182686961e34974ea7bdc0a21fca32be06/debian_sid_amd64_sysroot.tar.xz)

Also reverts follow-on commits:
This reverts commit 58c62fd.
This reverts commit 31e541e.
  • Loading branch information
nico committed Apr 2, 2021
1 parent 1d54aa2 commit f176803
Show file tree
Hide file tree
Showing 16 changed files with 184 additions and 111 deletions.
5 changes: 4 additions & 1 deletion compiler-rt/lib/asan/asan_rtl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -490,6 +490,9 @@ static void AsanInitInternal() {
if (flags()->start_deactivated)
AsanDeactivate();

// interceptors
InitTlsSize();

// Create main thread.
AsanThread *main_thread = CreateMainThread();
CHECK_EQ(0, main_thread->tid());
Expand Down Expand Up @@ -565,7 +568,7 @@ void UnpoisonStack(uptr bottom, uptr top, const char *type) {
type, top, bottom, top - bottom, top - bottom);
return;
}
PoisonShadow(bottom, RoundUpTo(top - bottom, SHADOW_GRANULARITY), 0);
PoisonShadow(bottom, top - bottom, 0);
}

static void UnpoisonDefaultStack() {
Expand Down
2 changes: 1 addition & 1 deletion compiler-rt/lib/asan/asan_thread.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -307,7 +307,7 @@ void AsanThread::SetThreadStackAndTls(const InitOptions *options) {
uptr stack_size = 0;
GetThreadStackAndTls(tid() == 0, &stack_bottom_, &stack_size, &tls_begin_,
&tls_size);
stack_top_ = RoundDownTo(stack_bottom_ + stack_size, SHADOW_GRANULARITY);
stack_top_ = stack_bottom_ + stack_size;
tls_end_ = tls_begin_ + tls_size;
dtls_ = DTLS_Get();

Expand Down
2 changes: 2 additions & 0 deletions compiler-rt/lib/hwasan/hwasan.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,8 @@ void __hwasan_init() {
hwasan_init_is_running = 1;
SanitizerToolName = "HWAddressSanitizer";

InitTlsSize();

CacheBinaryName();
InitializeFlags();

Expand Down
1 change: 1 addition & 0 deletions compiler-rt/lib/lsan/lsan.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ extern "C" void __lsan_init() {
InitCommonLsan();
InitializeAllocator();
ReplaceSystemMalloc();
InitTlsSize();
InitializeInterceptors();
InitializeThreadRegistry();
InstallDeadlySignalHandlers(LsanOnDeadlySignal);
Expand Down
3 changes: 3 additions & 0 deletions compiler-rt/lib/memprof/memprof_rtl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,9 @@ static void MemprofInitInternal() {

InitializeCoverage(common_flags()->coverage, common_flags()->coverage_dir);

// interceptors
InitTlsSize();

// Create main thread.
MemprofThread *main_thread = CreateMainThread();
CHECK_EQ(0, main_thread->tid());
Expand Down
1 change: 1 addition & 0 deletions compiler-rt/lib/msan/msan.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -436,6 +436,7 @@ void __msan_init() {

InitializeInterceptors();
CheckASLR();
InitTlsSize();
InstallDeadlySignalHandlers(MsanOnDeadlySignal);
InstallAtExitHandler(); // Needs __cxa_atexit interceptor.

Expand Down
1 change: 1 addition & 0 deletions compiler-rt/lib/sanitizer_common/sanitizer_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,7 @@ void SetSandboxingCallback(void (*f)());

void InitializeCoverage(bool enabled, const char *coverage_dir);

void InitTlsSize();
uptr GetTlsSize();

// Other
Expand Down
1 change: 1 addition & 0 deletions compiler-rt/lib/sanitizer_common/sanitizer_fuchsia.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ void DisableCoreDumperIfNecessary() {}
void InstallDeadlySignalHandlers(SignalHandlerType handler) {}
void SetAlternateSignalStack() {}
void UnsetAlternateSignalStack() {}
void InitTlsSize() {}

bool SignalContext::IsStackOverflow() const { return false; }
void SignalContext::DumpAllRegisters(void *context) { UNIMPLEMENTED(); }
Expand Down
1 change: 1 addition & 0 deletions compiler-rt/lib/sanitizer_common/sanitizer_linux.h
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ class ThreadLister {
// Exposed for testing.
uptr ThreadDescriptorSize();
uptr ThreadSelf();
uptr ThreadSelfOffset();

// Matches a library's file name against a base name (stripping path and version
// information).
Expand Down
251 changes: 143 additions & 108 deletions compiler-rt/lib/sanitizer_common/sanitizer_linux_libcdep.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -188,8 +188,80 @@ __attribute__((unused)) static bool GetLibcVersion(int *major, int *minor,
#endif
}

// ThreadDescriptorSize() is only used by lsan to get the pointer to
// thread-specific data keys in the thread control block.
#if SANITIZER_GLIBC && !SANITIZER_GO
static uptr g_tls_size;

#ifdef __i386__
#define CHECK_GET_TLS_STATIC_INFO_VERSION (!__GLIBC_PREREQ(2, 27))
#else
#define CHECK_GET_TLS_STATIC_INFO_VERSION 0
#endif

#if CHECK_GET_TLS_STATIC_INFO_VERSION
#define DL_INTERNAL_FUNCTION __attribute__((regparm(3), stdcall))
#else
#define DL_INTERNAL_FUNCTION
#endif

namespace {
struct GetTlsStaticInfoCall {
typedef void (*get_tls_func)(size_t*, size_t*);
};
struct GetTlsStaticInfoRegparmCall {
typedef void (*get_tls_func)(size_t*, size_t*) DL_INTERNAL_FUNCTION;
};

template <typename T>
void CallGetTls(void* ptr, size_t* size, size_t* align) {
typename T::get_tls_func get_tls;
CHECK_EQ(sizeof(get_tls), sizeof(ptr));
internal_memcpy(&get_tls, &ptr, sizeof(ptr));
CHECK_NE(get_tls, 0);
get_tls(size, align);
}

bool CmpLibcVersion(int major, int minor, int patch) {
int ma;
int mi;
int pa;
if (!GetLibcVersion(&ma, &mi, &pa))
return false;
if (ma > major)
return true;
if (ma < major)
return false;
if (mi > minor)
return true;
if (mi < minor)
return false;
return pa >= patch;
}

} // namespace

void InitTlsSize() {
// all current supported platforms have 16 bytes stack alignment
const size_t kStackAlign = 16;
void *get_tls_static_info_ptr = dlsym(RTLD_NEXT, "_dl_get_tls_static_info");
size_t tls_size = 0;
size_t tls_align = 0;
// On i?86, _dl_get_tls_static_info used to be internal_function, i.e.
// __attribute__((regparm(3), stdcall)) before glibc 2.27 and is normal
// function in 2.27 and later.
if (CHECK_GET_TLS_STATIC_INFO_VERSION && !CmpLibcVersion(2, 27, 0))
CallGetTls<GetTlsStaticInfoRegparmCall>(get_tls_static_info_ptr,
&tls_size, &tls_align);
else
CallGetTls<GetTlsStaticInfoCall>(get_tls_static_info_ptr,
&tls_size, &tls_align);
if (tls_align < kStackAlign)
tls_align = kStackAlign;
g_tls_size = RoundUpTo(tls_size, tls_align);
}
#else
void InitTlsSize() { }
#endif // SANITIZER_GLIBC && !SANITIZER_GO

#if (defined(__x86_64__) || defined(__i386__) || defined(__mips__) || \
defined(__aarch64__) || defined(__powerpc64__) || defined(__s390__) || \
defined(__arm__) || SANITIZER_RISCV64) && \
Expand Down Expand Up @@ -262,6 +334,13 @@ uptr ThreadDescriptorSize() {
return val;
}

// The offset at which pointer to self is located in the thread descriptor.
const uptr kThreadSelfOffset = FIRST_32_SECOND_64(8, 16);

uptr ThreadSelfOffset() {
return kThreadSelfOffset;
}

#if defined(__mips__) || defined(__powerpc64__) || SANITIZER_RISCV64
// TlsPreTcbSize includes size of struct pthread_descr and size of tcb
// head structure. It lies before the static tls blocks.
Expand All @@ -280,63 +359,48 @@ static uptr TlsPreTcbSize() {
}
#endif

#if !SANITIZER_GO
namespace {
struct TlsRange {
uptr begin, end, align;
size_t tls_modid;
bool operator<(const TlsRange &rhs) const { return begin < rhs.begin; }
};
} // namespace

static int CollectStaticTlsRanges(struct dl_phdr_info *info, size_t size,
void *data) {
if (!info->dlpi_tls_data)
return 0;
const uptr begin = (uptr)info->dlpi_tls_data;
for (unsigned i = 0; i != info->dlpi_phnum; ++i)
if (info->dlpi_phdr[i].p_type == PT_TLS) {
static_cast<InternalMmapVector<TlsRange> *>(data)->push_back(
TlsRange{begin, begin + info->dlpi_phdr[i].p_memsz,
info->dlpi_phdr[i].p_align, info->dlpi_tls_modid});
break;
}
return 0;
uptr ThreadSelf() {
uptr descr_addr;
#if defined(__i386__)
asm("mov %%gs:%c1,%0" : "=r"(descr_addr) : "i"(kThreadSelfOffset));
#elif defined(__x86_64__)
asm("mov %%fs:%c1,%0" : "=r"(descr_addr) : "i"(kThreadSelfOffset));
#elif defined(__mips__)
// MIPS uses TLS variant I. The thread pointer (in hardware register $29)
// points to the end of the TCB + 0x7000. The pthread_descr structure is
// immediately in front of the TCB. TlsPreTcbSize() includes the size of the
// TCB and the size of pthread_descr.
const uptr kTlsTcbOffset = 0x7000;
uptr thread_pointer;
asm volatile(".set push;\
.set mips64r2;\
rdhwr %0,$29;\
.set pop" : "=r" (thread_pointer));
descr_addr = thread_pointer - kTlsTcbOffset - TlsPreTcbSize();
#elif defined(__aarch64__) || defined(__arm__)
descr_addr = reinterpret_cast<uptr>(__builtin_thread_pointer()) -
ThreadDescriptorSize();
#elif SANITIZER_RISCV64
// https://github.com/riscv/riscv-elf-psabi-doc/issues/53
uptr thread_pointer = reinterpret_cast<uptr>(__builtin_thread_pointer());
descr_addr = thread_pointer - TlsPreTcbSize();
#elif defined(__s390__)
descr_addr = reinterpret_cast<uptr>(__builtin_thread_pointer());
#elif defined(__powerpc64__)
// PPC64LE uses TLS variant I. The thread pointer (in GPR 13)
// points to the end of the TCB + 0x7000. The pthread_descr structure is
// immediately in front of the TCB. TlsPreTcbSize() includes the size of the
// TCB and the size of pthread_descr.
const uptr kTlsTcbOffset = 0x7000;
uptr thread_pointer;
asm("addi %0,13,%1" : "=r"(thread_pointer) : "I"(-kTlsTcbOffset));
descr_addr = thread_pointer - TlsPreTcbSize();
#else
#error "unsupported CPU arch"
#endif
return descr_addr;
}

static void GetStaticTlsRange(uptr *addr, uptr *size, uptr *align) {
InternalMmapVector<TlsRange> ranges;
dl_iterate_phdr(CollectStaticTlsRanges, &ranges);
uptr len = ranges.size();
Sort(ranges.begin(), len);
// Find the range with tls_modid=1. For glibc, because libc.so uses PT_TLS,
// this module is guaranteed to exist and is one of the initially loaded
// modules.
uptr one = 0;
while (one != len && ranges[one].tls_modid != 1) ++one;
if (one == len) {
// This may happen with musl if no module uses PT_TLS.
*addr = 0;
*size = 0;
*align = 1;
return;
}
// Find the maximum consecutive ranges. We consider two modules consecutive if
// the gap is smaller than the alignment. The dynamic loader places static TLS
// blocks this way not to waste space.
uptr l = one;
*align = ranges[l].align;
while (l != 0 && ranges[l].begin < ranges[l - 1].end + ranges[l - 1].align)
*align = Max(*align, ranges[--l].align);
uptr r = one + 1;
while (r != len && ranges[r].begin < ranges[r - 1].end + ranges[r - 1].align)
*align = Max(*align, ranges[r++].align);
*addr = ranges[l].begin;
*size = ranges[r - 1].end - ranges[l].begin;
}
#endif // !SANITIZER_GO
#endif // (x86_64 || i386 || mips || ...) && SANITIZER_LINUX &&
// !SANITIZER_ANDROID
#endif // (x86_64 || i386 || MIPS) && SANITIZER_LINUX

#if SANITIZER_FREEBSD
static void **ThreadSelfSegbase() {
Expand Down Expand Up @@ -408,54 +472,18 @@ static void GetTls(uptr *addr, uptr *size) {
*size = 0;
}
#elif SANITIZER_LINUX
uptr align;
GetStaticTlsRange(addr, size, &align);
#if defined(__x86_64__) || defined(__i386__) || defined(__s390__)
if (SANITIZER_GLIBC) {
#if defined(__s390__)
align = Max<uptr>(align, 16);
#else
align = Max<uptr>(align, 64);
#endif
}
const uptr tp = RoundUpTo(*addr + *size, align);

// lsan requires the range to additionally cover the static TLS surplus
// (elf/dl-tls.c defines 1664). Otherwise there may be false positives for
// allocations only referenced by tls in dynamically loaded modules.
if (SANITIZER_GLIBC)
*size += 1644;

// Extend the range to include the thread control block. On glibc, lsan needs
// the range to include pthread::{specific_1stblock,specific} so that
// allocations only referenced by pthread_setspecific can be scanned. This may
// underestimate by at most TLS_TCB_ALIGN-1 bytes but it should be fine
// because the number of bytes after pthread::specific is larger.
*addr = tp - RoundUpTo(*size, align);
*size = tp - *addr + ThreadDescriptorSize();
#else
if (SANITIZER_GLIBC)
*size += 1664;
#if defined(__powerpc64__)
// TODO Figure out why *addr may be zero and use TlsPreTcbSize.
void *ptr = dlsym(RTLD_NEXT, "_dl_get_tls_static_info");
uptr tls_size, tls_align;
((void (*)(size_t *, size_t *))ptr)(&tls_size, &tls_align);
asm("addi %0,13,-0x7000" : "=r"(*addr));
*addr -= TlsPreTcbSize();
*size = RoundUpTo(tls_size + TlsPreTcbSize(), 16);
#elif defined(__mips__) || SANITIZER_RISCV64
const uptr pre_tcb_size = TlsPreTcbSize();
*addr -= pre_tcb_size;
*size += pre_tcb_size;
*addr = ThreadSelf();
*size = GetTlsSize();
*addr -= *size;
*addr += ThreadDescriptorSize();
#elif defined(__mips__) || defined(__aarch64__) || defined(__powerpc64__) || \
defined(__arm__) || SANITIZER_RISCV64
*addr = ThreadSelf();
*size = GetTlsSize();
#else
// arm and aarch64 reserve two words at TP, so this underestimates the range.
// However, this is sufficient for the purpose of finding the pointers to
// thread-specific data keys.
const uptr tcb_size = ThreadDescriptorSize();
*addr -= tcb_size;
*size += tcb_size;
#endif
*addr = 0;
*size = 0;
#endif
#elif SANITIZER_FREEBSD
void** segbase = ThreadSelfSegbase();
Expand Down Expand Up @@ -496,11 +524,17 @@ static void GetTls(uptr *addr, uptr *size) {

#if !SANITIZER_GO
uptr GetTlsSize() {
#if SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_NETBSD || \
#if SANITIZER_FREEBSD || SANITIZER_ANDROID || SANITIZER_NETBSD || \
SANITIZER_SOLARIS
uptr addr, size;
GetTls(&addr, &size);
return size;
#elif SANITIZER_GLIBC
#if defined(__mips__) || defined(__powerpc64__) || SANITIZER_RISCV64
return RoundUpTo(g_tls_size + TlsPreTcbSize(), 16);
#else
return g_tls_size;
#endif
#else
return 0;
#endif
Expand All @@ -523,9 +557,10 @@ void GetThreadStackAndTls(bool main, uptr *stk_addr, uptr *stk_size,
if (!main) {
// If stack and tls intersect, make them non-intersecting.
if (*tls_addr > *stk_addr && *tls_addr < *stk_addr + *stk_size) {
if (*stk_addr + *stk_size < *tls_addr + *tls_size)
*tls_size = *stk_addr + *stk_size - *tls_addr;
*stk_size = *tls_addr - *stk_addr;
CHECK_GT(*tls_addr + *tls_size, *stk_addr);
CHECK_LE(*tls_addr + *tls_size, *stk_addr + *stk_size);
*stk_size -= *tls_size;
*tls_addr = *stk_addr + *stk_size;
}
}
#endif
Expand Down
3 changes: 3 additions & 0 deletions compiler-rt/lib/sanitizer_common/sanitizer_mac.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -548,6 +548,9 @@ uptr GetTlsSize() {
return 0;
}

void InitTlsSize() {
}

uptr TlsBaseAddr() {
uptr segbase = 0;
#if defined(__x86_64__)
Expand Down
Loading

0 comments on commit f176803

Please sign in to comment.