Skip to content

Commit

Permalink
[hwasan] Reset current thread pointer on thread exit.
Browse files Browse the repository at this point in the history
Summary:
This is necessary to handle calls to free() after __hwasan_thread_exit,
which is possible in glibc.

Also, add a null check to GetCurrentThread, otherwise the logic in
GetThreadByBufferAddress turns it into a non-null value. This means that
all of the checks for GetCurrentThread() != nullptr do not have any
effect at all right now!

Reviewers: pcc, hctim

Subscribers: #sanitizers, llvm-commits

Tags: #sanitizers

Differential Revision: https://reviews.llvm.org/D79608
  • Loading branch information
eugenis committed May 8, 2020
1 parent fcf10d1 commit eaea9ed
Show file tree
Hide file tree
Showing 4 changed files with 34 additions and 3 deletions.
2 changes: 1 addition & 1 deletion compiler-rt/lib/hwasan/hwasan.cpp
Expand Up @@ -186,7 +186,7 @@ void __sanitizer::BufferedStackTrace::UnwindImpl(
uptr pc, uptr bp, void *context, bool request_fast, u32 max_depth) {
Thread *t = GetCurrentThread();
if (!t) {
// the thread is still being created.
// The thread is still being created, or has already been destroyed.
size = 0;
return;
}
Expand Down
7 changes: 5 additions & 2 deletions compiler-rt/lib/hwasan/hwasan_linux.cpp
Expand Up @@ -354,8 +354,11 @@ void AndroidTestTlsSlot() {}
#endif

Thread *GetCurrentThread() {
auto *R = (StackAllocationsRingBuffer *)GetCurrentThreadLongPtr();
return hwasanThreadList().GetThreadByBufferAddress((uptr)(R->Next()));
uptr *ThreadLongPtr = GetCurrentThreadLongPtr();
if (UNLIKELY(*ThreadLongPtr == 0))
return nullptr;
auto *R = (StackAllocationsRingBuffer *)ThreadLongPtr;
return hwasanThreadList().GetThreadByBufferAddress((uptr)R->Next());
}

struct AccessInfo {
Expand Down
6 changes: 6 additions & 0 deletions compiler-rt/lib/hwasan/hwasan_thread.cpp
Expand Up @@ -90,6 +90,12 @@ void Thread::Destroy() {
if (heap_allocations_)
heap_allocations_->Delete();
DTLS_Destroy();
// Unregister this as the current thread.
// Instrumented code can not run on this thread from this point onwards, but
// malloc/free can still be served. Glibc may call free() very late, after all
// TSD destructors are done.
CHECK_EQ(GetCurrentThread(), this);
*GetCurrentThreadLongPtr() = 0;
}

void Thread::Print(const char *Prefix) {
Expand Down
22 changes: 22 additions & 0 deletions compiler-rt/test/hwasan/TestCases/libc_thread_freeres.c
@@ -0,0 +1,22 @@
// RUN: %clang_hwasan %s -o %t && %env_hwasan_opts=random_tags=1 %run %t
// REQUIRES: stable-runtime

#include <pthread.h>
#include <sanitizer/hwasan_interface.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void *ThreadFn(void *) {
strerror_l(-1, 0);
__hwasan_enable_allocator_tagging();
// This will trigger memory deallocation in __strerror_thread_freeres,
// at a point when HwasanThread is already gone.
}

int main() {
pthread_t t;
pthread_create(&t, NULL, ThreadFn, NULL);
pthread_join(t, NULL);
return 0;
}

0 comments on commit eaea9ed

Please sign in to comment.