--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
+
--- /dev/null
+// 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
+
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) {
SANITIZER_COMMON_INTERCEPTORS_INIT;
+ TSAN_INTERCEPT(setjmp);
+ TSAN_INTERCEPT(_setjmp);
+ TSAN_INTERCEPT(sigsetjmp);
+ TSAN_INTERCEPT(__sigsetjmp);
TSAN_INTERCEPT(longjmp);
TSAN_INTERCEPT(siglongjmp);
MBlockExpectRace,
MBlockSignal,
MBlockFD,
+ MBlockJmpBuf,
// This must be the last.
MBlockTypeCount
// , 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)
struct SignalContext;
+struct JmpBuf {
+ uptr sp;
+ uptr mangled_sp;
+ uptr *shadow_stack_pos;
+};
+
// This struct is stored in TLS.
struct ThreadState {
FastState fast_state;
ThreadClock clock;
#ifndef TSAN_GO
AllocatorCache alloc_cache;
+ Vector<JmpBuf> jmp_bufs;
#endif
u64 stat[StatCnt];
const int tid;
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
return &end_[-1];
}
+ void PopBack() {
+ DCHECK_GT(end_, begin_);
+ end_--;
+ }
+
void Resize(uptr size) {
uptr old_size = Size();
EnsureSize(size);