diff options
author | Dmitry Vyukov <dvyukov@google.com> | 2013-03-25 10:10:44 +0000 |
---|---|---|
committer | Dmitry Vyukov <dvyukov@google.com> | 2013-03-25 10:10:44 +0000 |
commit | 4adf49d2535ed63e0358b4063f8743f9568eb3bf (patch) | |
tree | c3d5a9bb26c82ee62db12b1228a35283467af274 /compiler-rt/lib | |
parent | 510ad118001e5e04200cba12310fb20a83f1573a (diff) | |
download | bcm5719-llvm-4adf49d2535ed63e0358b4063f8743f9568eb3bf.tar.gz bcm5719-llvm-4adf49d2535ed63e0358b4063f8743f9568eb3bf.zip |
tsan: intercept setjmp/longjmp
llvm-svn: 177858
Diffstat (limited to 'compiler-rt/lib')
-rw-r--r-- | compiler-rt/lib/tsan/lit_tests/longjmp.cc | 22 | ||||
-rw-r--r-- | compiler-rt/lib/tsan/lit_tests/longjmp2.cc | 24 | ||||
-rw-r--r-- | compiler-rt/lib/tsan/lit_tests/longjmp3.cc | 48 | ||||
-rw-r--r-- | compiler-rt/lib/tsan/lit_tests/longjmp4.cc | 51 | ||||
-rw-r--r-- | compiler-rt/lib/tsan/rtl/tsan_interceptors.cc | 102 | ||||
-rw-r--r-- | compiler-rt/lib/tsan/rtl/tsan_mman.h | 1 | ||||
-rw-r--r-- | compiler-rt/lib/tsan/rtl/tsan_rtl.cc | 3 | ||||
-rw-r--r-- | compiler-rt/lib/tsan/rtl/tsan_rtl.h | 7 | ||||
-rw-r--r-- | compiler-rt/lib/tsan/rtl/tsan_rtl_amd64.S | 137 | ||||
-rw-r--r-- | compiler-rt/lib/tsan/rtl/tsan_vector.h | 5 |
10 files changed, 392 insertions, 8 deletions
diff --git a/compiler-rt/lib/tsan/lit_tests/longjmp.cc b/compiler-rt/lib/tsan/lit_tests/longjmp.cc new file mode 100644 index 00000000000..d9ca4ca5e6e --- /dev/null +++ b/compiler-rt/lib/tsan/lit_tests/longjmp.cc @@ -0,0 +1,22 @@ +// RUN: %clang_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +#include <stdio.h> +#include <stdlib.h> +#include <setjmp.h> + +int foo(jmp_buf env) { + longjmp(env, 42); +} + +int main() { + jmp_buf env; + if (setjmp(env) == 42) { + printf("JUMPED\n"); + return 0; + } + foo(env); + printf("FAILED\n"); + return 0; +} + +// CHECK-NOT: FAILED +// CHECK: JUMPED diff --git a/compiler-rt/lib/tsan/lit_tests/longjmp2.cc b/compiler-rt/lib/tsan/lit_tests/longjmp2.cc new file mode 100644 index 00000000000..0d551fa19d9 --- /dev/null +++ b/compiler-rt/lib/tsan/lit_tests/longjmp2.cc @@ -0,0 +1,24 @@ +// RUN: %clang_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +#include <stdio.h> +#include <stdlib.h> +#include <setjmp.h> + +int foo(sigjmp_buf env) { + printf("env=%p\n", env); + siglongjmp(env, 42); +} + +int main() { + sigjmp_buf env; + printf("env=%p\n", env); + if (sigsetjmp(env, 1) == 42) { + printf("JUMPED\n"); + return 0; + } + foo(env); + printf("FAILED\n"); + return 0; +} + +// CHECK-NOT: FAILED +// CHECK: JUMPED diff --git a/compiler-rt/lib/tsan/lit_tests/longjmp3.cc b/compiler-rt/lib/tsan/lit_tests/longjmp3.cc new file mode 100644 index 00000000000..87fabd0b3be --- /dev/null +++ b/compiler-rt/lib/tsan/lit_tests/longjmp3.cc @@ -0,0 +1,48 @@ +// RUN: %clang_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdio.h> +#include <stdlib.h> +#include <setjmp.h> + +void bar(jmp_buf env) { + volatile int x = 42; + longjmp(env, 42); + x++; +} + +void foo(jmp_buf env) { + volatile int x = 42; + bar(env); + x++; +} + +void badguy() { + pthread_mutex_t mtx; + pthread_mutex_init(&mtx, 0); + pthread_mutex_lock(&mtx); + pthread_mutex_destroy(&mtx); +} + +void mymain() { + jmp_buf env; + if (setjmp(env) == 42) { + badguy(); + return; + } + foo(env); + printf("FAILED\n"); +} + +int main() { + volatile int x = 42; + mymain(); + return x; +} + +// CHECK-NOT: FAILED +// CHECK: WARNING: ThreadSanitizer: destroy of a locked mutex +// CHECK: #0 pthread_mutex_destroy +// CHECK: #1 badguy +// CHECK: #2 mymain +// CHECK: #3 main + diff --git a/compiler-rt/lib/tsan/lit_tests/longjmp4.cc b/compiler-rt/lib/tsan/lit_tests/longjmp4.cc new file mode 100644 index 00000000000..a8764dda5a6 --- /dev/null +++ b/compiler-rt/lib/tsan/lit_tests/longjmp4.cc @@ -0,0 +1,51 @@ +// RUN: %clang_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +#include <pthread.h> +#include <stdio.h> +#include <stdlib.h> +#include <setjmp.h> +#include <string.h> + +void bar(jmp_buf env) { + volatile int x = 42; + jmp_buf env2; + memcpy(env2, env, sizeof(jmp_buf)); + longjmp(env2, 42); + x++; +} + +void foo(jmp_buf env) { + volatile int x = 42; + bar(env); + x++; +} + +void badguy() { + pthread_mutex_t mtx; + pthread_mutex_init(&mtx, 0); + pthread_mutex_lock(&mtx); + pthread_mutex_destroy(&mtx); +} + +void mymain() { + jmp_buf env; + if (setjmp(env) == 42) { + badguy(); + return; + } + foo(env); + printf("FAILED\n"); +} + +int main() { + volatile int x = 42; + mymain(); + return x; +} + +// CHECK-NOT: FAILED +// CHECK: WARNING: ThreadSanitizer: destroy of a locked mutex +// CHECK: #0 pthread_mutex_destroy +// CHECK: #1 badguy +// CHECK: #2 mymain +// CHECK: #3 main + diff --git a/compiler-rt/lib/tsan/rtl/tsan_interceptors.cc b/compiler-rt/lib/tsan/rtl/tsan_interceptors.cc index f32b666d514..240005e3960 100644 --- a/compiler-rt/lib/tsan/rtl/tsan_interceptors.cc +++ b/compiler-rt/lib/tsan/rtl/tsan_interceptors.cc @@ -319,16 +319,98 @@ TSAN_INTERCEPTOR(int, __cxa_atexit, void (*f)(void *a), void *arg, void *dso) { return atexit_ctx->atexit(thr, pc, false, (void(*)())f, arg); } -TSAN_INTERCEPTOR(void, longjmp, void *env, int val) { - SCOPED_TSAN_INTERCEPTOR(longjmp, env, val); - Printf("ThreadSanitizer: longjmp() is not supported\n"); - Die(); +// Cleanup old bufs. +static void JmpBufGarbageCollect(ThreadState *thr, uptr sp) { + for (uptr i = 0; i < thr->jmp_bufs.Size(); i++) { + JmpBuf *buf = &thr->jmp_bufs[i]; + if (buf->sp <= sp) { + uptr sz = thr->jmp_bufs.Size(); + thr->jmp_bufs[i] = thr->jmp_bufs[sz - 1]; + thr->jmp_bufs.PopBack(); + i--; + } + } } -TSAN_INTERCEPTOR(void, siglongjmp, void *env, int val) { - SCOPED_TSAN_INTERCEPTOR(siglongjmp, env, val); - Printf("ThreadSanitizer: siglongjmp() is not supported\n"); - Die(); +static void SetJmp(ThreadState *thr, uptr sp, uptr mangled_sp) { + if (thr->shadow_stack_pos == 0) // called from libc guts during bootstrap + return; + // Cleanup old bufs. + JmpBufGarbageCollect(thr, sp); + // Remember the buf. + JmpBuf *buf = thr->jmp_bufs.PushBack(); + buf->sp = sp; + buf->mangled_sp = mangled_sp; + buf->shadow_stack_pos = thr->shadow_stack_pos; +} + +static void LongJmp(ThreadState *thr, uptr *env) { + uptr mangled_sp = env[6]; + // Find the saved buf by mangled_sp. + for (uptr i = 0; i < thr->jmp_bufs.Size(); i++) { + JmpBuf *buf = &thr->jmp_bufs[i]; + if (buf->mangled_sp == mangled_sp) { + CHECK_GE(thr->shadow_stack_pos, buf->shadow_stack_pos); + // Unwind the stack. + while (thr->shadow_stack_pos > buf->shadow_stack_pos) + FuncExit(thr); + JmpBufGarbageCollect(thr, buf->sp - 1); // do not collect buf->sp + return; + } + } + Printf("ThreadSanitizer: can't find longjmp buf\n"); + CHECK(0); +} + +extern "C" void __tsan_setjmp(uptr sp, uptr mangled_sp) { + ScopedInRtl in_rtl; + SetJmp(cur_thread(), sp, mangled_sp); +} + +// Not called. Merely to satisfy TSAN_INTERCEPT(). +extern "C" int __interceptor_setjmp(void *env) { + CHECK(0); + return 0; +} + +extern "C" int __interceptor__setjmp(void *env) { + CHECK(0); + return 0; +} + +extern "C" int __interceptor_sigsetjmp(void *env) { + CHECK(0); + return 0; +} + +extern "C" int __interceptor___sigsetjmp(void *env) { + CHECK(0); + return 0; +} + +extern "C" int setjmp(void *env); +extern "C" int _setjmp(void *env); +extern "C" int sigsetjmp(void *env); +extern "C" int __sigsetjmp(void *env); +DEFINE_REAL(int, setjmp, void *env) +DEFINE_REAL(int, _setjmp, void *env) +DEFINE_REAL(int, sigsetjmp, void *env) +DEFINE_REAL(int, __sigsetjmp, void *env) + +TSAN_INTERCEPTOR(void, longjmp, uptr *env, int val) { + { + SCOPED_TSAN_INTERCEPTOR(longjmp, env, val); + } + LongJmp(cur_thread(), env); + REAL(longjmp)(env, val); +} + +TSAN_INTERCEPTOR(void, siglongjmp, uptr *env, int val) { + { + SCOPED_TSAN_INTERCEPTOR(siglongjmp, env, val); + } + LongJmp(cur_thread(), env); + REAL(siglongjmp)(env, val); } TSAN_INTERCEPTOR(void*, malloc, uptr size) { @@ -1853,6 +1935,10 @@ void InitializeInterceptors() { SANITIZER_COMMON_INTERCEPTORS_INIT; + TSAN_INTERCEPT(setjmp); + TSAN_INTERCEPT(_setjmp); + TSAN_INTERCEPT(sigsetjmp); + TSAN_INTERCEPT(__sigsetjmp); TSAN_INTERCEPT(longjmp); TSAN_INTERCEPT(siglongjmp); diff --git a/compiler-rt/lib/tsan/rtl/tsan_mman.h b/compiler-rt/lib/tsan/rtl/tsan_mman.h index 4a9240f660f..19d555437f3 100644 --- a/compiler-rt/lib/tsan/rtl/tsan_mman.h +++ b/compiler-rt/lib/tsan/rtl/tsan_mman.h @@ -63,6 +63,7 @@ enum MBlockType { MBlockExpectRace, MBlockSignal, MBlockFD, + MBlockJmpBuf, // This must be the last. MBlockTypeCount diff --git a/compiler-rt/lib/tsan/rtl/tsan_rtl.cc b/compiler-rt/lib/tsan/rtl/tsan_rtl.cc index 19722d0472f..d67c9125f7b 100644 --- a/compiler-rt/lib/tsan/rtl/tsan_rtl.cc +++ b/compiler-rt/lib/tsan/rtl/tsan_rtl.cc @@ -88,6 +88,9 @@ ThreadState::ThreadState(Context *ctx, int tid, int unique_id, u64 epoch, // , fast_ignore_writes() // , in_rtl() , shadow_stack_pos(&shadow_stack[0]) +#ifndef TSAN_GO + , jmp_bufs(MBlockJmpBuf) +#endif , tid(tid) , unique_id(unique_id) , stk_addr(stk_addr) diff --git a/compiler-rt/lib/tsan/rtl/tsan_rtl.h b/compiler-rt/lib/tsan/rtl/tsan_rtl.h index 3af0402dafc..80ec3024f3d 100644 --- a/compiler-rt/lib/tsan/rtl/tsan_rtl.h +++ b/compiler-rt/lib/tsan/rtl/tsan_rtl.h @@ -384,6 +384,12 @@ class Shadow : public FastState { struct SignalContext; +struct JmpBuf { + uptr sp; + uptr mangled_sp; + uptr *shadow_stack_pos; +}; + // This struct is stored in TLS. struct ThreadState { FastState fast_state; @@ -418,6 +424,7 @@ struct ThreadState { ThreadClock clock; #ifndef TSAN_GO AllocatorCache alloc_cache; + Vector<JmpBuf> jmp_bufs; #endif u64 stat[StatCnt]; const int tid; diff --git a/compiler-rt/lib/tsan/rtl/tsan_rtl_amd64.S b/compiler-rt/lib/tsan/rtl/tsan_rtl_amd64.S index af878563573..11c75c72dbe 100644 --- a/compiler-rt/lib/tsan/rtl/tsan_rtl_amd64.S +++ b/compiler-rt/lib/tsan/rtl/tsan_rtl_amd64.S @@ -160,6 +160,143 @@ __tsan_report_race_thunk: ret .cfi_endproc +.hidden __tsan_setjmp +.comm _ZN14__interception11real_setjmpE,8,8 +.globl setjmp +.type setjmp, @function +setjmp: + .cfi_startproc + // save env parameter + push %rdi + .cfi_adjust_cfa_offset 8 + .cfi_rel_offset %rdi, 0 + // obtain %rsp + lea 16(%rsp), %rdi + mov %rdi, %rsi + xor %fs:0x30, %rsi // magic mangling of rsp (see libc setjmp) + rol $0x11, %rsi + // call tsan interceptor + call __tsan_setjmp + // restore env parameter + pop %rdi + .cfi_adjust_cfa_offset -8 + .cfi_restore %rdi + // tail jump to libc setjmp + movl $0, %eax + movq _ZN14__interception11real_setjmpE@GOTPCREL(%rip), %rdx + jmp *(%rdx) + .cfi_endproc +.size setjmp, .-setjmp + +.comm _ZN14__interception12real__setjmpE,8,8 +.globl _setjmp +.type _setjmp, @function +_setjmp: + .cfi_startproc + // save env parameter + push %rdi + .cfi_adjust_cfa_offset 8 + .cfi_rel_offset %rdi, 0 + // obtain %rsp + lea 16(%rsp), %rdi + mov %rdi, %rsi + xor %fs:0x30, %rsi // magic mangling of rsp (see libc setjmp) + rol $0x11, %rsi + // call tsan interceptor + call __tsan_setjmp + // restore env parameter + pop %rdi + .cfi_adjust_cfa_offset -8 + .cfi_restore %rdi + // tail jump to libc setjmp + movl $0, %eax + movq _ZN14__interception12real__setjmpE@GOTPCREL(%rip), %rdx + jmp *(%rdx) + .cfi_endproc +.size _setjmp, .-_setjmp + +.comm _ZN14__interception14real_sigsetjmpE,8,8 +.globl sigsetjmp +.type sigsetjmp, @function +sigsetjmp: + .cfi_startproc + // save env parameter + push %rdi + .cfi_adjust_cfa_offset 8 + .cfi_rel_offset %rdi, 0 + // save savesigs parameter + push %rsi + .cfi_adjust_cfa_offset 8 + .cfi_rel_offset %rsi, 0 + // align stack frame + sub $8, %rsp + .cfi_adjust_cfa_offset 8 + // obtain %rsp + lea 32(%rsp), %rdi + mov %rdi, %rsi + xor %fs:0x30, %rsi // magic mangling of rsp (see libc setjmp) + rol $0x11, %rsi + // call tsan interceptor + call __tsan_setjmp + // unalign stack frame + add $8, %rsp + .cfi_adjust_cfa_offset -8 + // restore savesigs parameter + pop %rsi + .cfi_adjust_cfa_offset -8 + .cfi_restore %rsi + // restore env parameter + pop %rdi + .cfi_adjust_cfa_offset -8 + .cfi_restore %rdi + // tail jump to libc sigsetjmp + movl $0, %eax + movq _ZN14__interception14real_sigsetjmpE@GOTPCREL(%rip), %rdx + jmp *(%rdx) + .cfi_endproc +.size sigsetjmp, .-sigsetjmp + +.comm _ZN14__interception16real___sigsetjmpE,8,8 +.globl __sigsetjmp +.type __sigsetjmp, @function +__sigsetjmp: + .cfi_startproc + // save env parameter + push %rdi + .cfi_adjust_cfa_offset 8 + .cfi_rel_offset %rdi, 0 + // save savesigs parameter + push %rsi + .cfi_adjust_cfa_offset 8 + .cfi_rel_offset %rsi, 0 + // align stack frame + sub $8, %rsp + .cfi_adjust_cfa_offset 8 + // obtain %rsp + lea 32(%rsp), %rdi + mov %rdi, %rsi + xor %fs:0x30, %rsi // magic mangling of rsp (see libc setjmp) + rol $0x11, %rsi + // call tsan interceptor + call __tsan_setjmp + // unalign stack frame + add $8, %rsp + .cfi_adjust_cfa_offset -8 + // restore savesigs parameter + pop %rsi + .cfi_adjust_cfa_offset -8 + .cfi_restore %rsi + // restore env parameter + pop %rdi + .cfi_adjust_cfa_offset -8 + .cfi_restore %rdi + // tail jump to libc sigsetjmp + movl $0, %eax + movq _ZN14__interception16real___sigsetjmpE@GOTPCREL(%rip), %rdx + jmp *(%rdx) + .cfi_endproc +.size __sigsetjmp, .-__sigsetjmp + #ifdef __linux__ /* We do not need executable stack. */ .section .note.GNU-stack,"",@progbits diff --git a/compiler-rt/lib/tsan/rtl/tsan_vector.h b/compiler-rt/lib/tsan/rtl/tsan_vector.h index 64328d0b5b0..fa236b1f1e4 100644 --- a/compiler-rt/lib/tsan/rtl/tsan_vector.h +++ b/compiler-rt/lib/tsan/rtl/tsan_vector.h @@ -64,6 +64,11 @@ class Vector { return &end_[-1]; } + void PopBack() { + DCHECK_GT(end_, begin_); + end_--; + } + void Resize(uptr size) { uptr old_size = Size(); EnsureSize(size); |