tsan: intercept setjmp/longjmp

llvm-svn: 177858
This commit is contained in:
Dmitry Vyukov 2013-03-25 10:10:44 +00:00
parent 510ad11800
commit 4adf49d253
10 changed files with 392 additions and 8 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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);

View File

@ -63,6 +63,7 @@ enum MBlockType {
MBlockExpectRace,
MBlockSignal,
MBlockFD,
MBlockJmpBuf,
// This must be the last.
MBlockTypeCount

View File

@ -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)

View File

@ -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;

View File

@ -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

View File

@ -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);