[tsan] Detect uses of uninitialized, destroyed and invalid mutexes

This patch adds a new TSan report type, ReportTypeMutexInvalidAccess, which is triggered when pthread_mutex_lock or pthread_mutex_unlock returns EINVAL (this means the mutex is invalid, uninitialized or already destroyed).

Differential Revision: http://reviews.llvm.org/D18132

llvm-svn: 263641
This commit is contained in:
Kuba Brecka 2016-03-16 15:39:20 +00:00
parent 60228bdb80
commit 46bf454d18
9 changed files with 54 additions and 1 deletions

View File

@ -91,6 +91,10 @@
#define COMMON_INTERCEPTOR_MUTEX_REPAIR(ctx, m) {} #define COMMON_INTERCEPTOR_MUTEX_REPAIR(ctx, m) {}
#endif #endif
#ifndef COMMON_INTERCEPTOR_MUTEX_INVALID
#define COMMON_INTERCEPTOR_MUTEX_INVALID(ctx, m) {}
#endif
#ifndef COMMON_INTERCEPTOR_HANDLE_RECVMSG #ifndef COMMON_INTERCEPTOR_HANDLE_RECVMSG
#define COMMON_INTERCEPTOR_HANDLE_RECVMSG(ctx, msg) ((void)(msg)) #define COMMON_INTERCEPTOR_HANDLE_RECVMSG(ctx, msg) ((void)(msg))
#endif #endif
@ -3346,6 +3350,8 @@ INTERCEPTOR(int, pthread_mutex_lock, void *m) {
COMMON_INTERCEPTOR_MUTEX_REPAIR(ctx, m); COMMON_INTERCEPTOR_MUTEX_REPAIR(ctx, m);
if (res == 0 || res == errno_EOWNERDEAD) if (res == 0 || res == errno_EOWNERDEAD)
COMMON_INTERCEPTOR_MUTEX_LOCK(ctx, m); COMMON_INTERCEPTOR_MUTEX_LOCK(ctx, m);
if (res == errno_EINVAL)
COMMON_INTERCEPTOR_MUTEX_INVALID(ctx, m);
return res; return res;
} }
@ -3353,7 +3359,10 @@ INTERCEPTOR(int, pthread_mutex_unlock, void *m) {
void *ctx; void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, pthread_mutex_unlock, m); COMMON_INTERCEPTOR_ENTER(ctx, pthread_mutex_unlock, m);
COMMON_INTERCEPTOR_MUTEX_UNLOCK(ctx, m); COMMON_INTERCEPTOR_MUTEX_UNLOCK(ctx, m);
return REAL(pthread_mutex_unlock)(m); int res = REAL(pthread_mutex_unlock)(m);
if (res == errno_EINVAL)
COMMON_INTERCEPTOR_MUTEX_INVALID(ctx, m);
return res;
} }
#define INIT_PTHREAD_MUTEX_LOCK COMMON_INTERCEPT_FUNCTION(pthread_mutex_lock) #define INIT_PTHREAD_MUTEX_LOCK COMMON_INTERCEPT_FUNCTION(pthread_mutex_lock)

View File

@ -25,6 +25,7 @@ static const char *ReportTypeDescription(ReportType typ) {
if (typ == ReportTypeThreadLeak) return "thread-leak"; if (typ == ReportTypeThreadLeak) return "thread-leak";
if (typ == ReportTypeMutexDestroyLocked) return "locked-mutex-destroy"; if (typ == ReportTypeMutexDestroyLocked) return "locked-mutex-destroy";
if (typ == ReportTypeMutexDoubleLock) return "mutex-double-lock"; if (typ == ReportTypeMutexDoubleLock) return "mutex-double-lock";
if (typ == ReportTypeMutexInvalidAccess) return "mutex-invalid-access";
if (typ == ReportTypeMutexBadUnlock) return "mutex-bad-unlock"; if (typ == ReportTypeMutexBadUnlock) return "mutex-bad-unlock";
if (typ == ReportTypeMutexBadReadLock) return "mutex-bad-read-lock"; if (typ == ReportTypeMutexBadReadLock) return "mutex-bad-read-lock";
if (typ == ReportTypeMutexBadReadUnlock) return "mutex-bad-read-unlock"; if (typ == ReportTypeMutexBadReadUnlock) return "mutex-bad-read-unlock";

View File

@ -2409,6 +2409,10 @@ static void HandleRecvmsg(ThreadState *thr, uptr pc,
MutexRepair(((TsanInterceptorContext *)ctx)->thr, \ MutexRepair(((TsanInterceptorContext *)ctx)->thr, \
((TsanInterceptorContext *)ctx)->pc, (uptr)m) ((TsanInterceptorContext *)ctx)->pc, (uptr)m)
#define COMMON_INTERCEPTOR_MUTEX_INVALID(ctx, m) \
MutexInvalidAccess(((TsanInterceptorContext *)ctx)->thr, \
((TsanInterceptorContext *)ctx)->pc, (uptr)m)
#if !SANITIZER_MAC #if !SANITIZER_MAC
#define COMMON_INTERCEPTOR_HANDLE_RECVMSG(ctx, msg) \ #define COMMON_INTERCEPTOR_HANDLE_RECVMSG(ctx, msg) \
HandleRecvmsg(((TsanInterceptorContext *)ctx)->thr, \ HandleRecvmsg(((TsanInterceptorContext *)ctx)->thr, \

View File

@ -96,6 +96,8 @@ static const char *ReportTypeString(ReportType typ) {
return "destroy of a locked mutex"; return "destroy of a locked mutex";
if (typ == ReportTypeMutexDoubleLock) if (typ == ReportTypeMutexDoubleLock)
return "double lock of a mutex"; return "double lock of a mutex";
if (typ == ReportTypeMutexInvalidAccess)
return "use of an invalid mutex (e.g. uninitialized or destroyed)";
if (typ == ReportTypeMutexBadUnlock) if (typ == ReportTypeMutexBadUnlock)
return "unlock of an unlocked mutex (or by a wrong thread)"; return "unlock of an unlocked mutex (or by a wrong thread)";
if (typ == ReportTypeMutexBadReadLock) if (typ == ReportTypeMutexBadReadLock)

View File

@ -27,6 +27,7 @@ enum ReportType {
ReportTypeThreadLeak, ReportTypeThreadLeak,
ReportTypeMutexDestroyLocked, ReportTypeMutexDestroyLocked,
ReportTypeMutexDoubleLock, ReportTypeMutexDoubleLock,
ReportTypeMutexInvalidAccess,
ReportTypeMutexBadUnlock, ReportTypeMutexBadUnlock,
ReportTypeMutexBadReadLock, ReportTypeMutexBadReadLock,
ReportTypeMutexBadReadUnlock, ReportTypeMutexBadReadUnlock,

View File

@ -695,6 +695,7 @@ void MutexReadLock(ThreadState *thr, uptr pc, uptr addr, bool try_lock = false);
void MutexReadUnlock(ThreadState *thr, uptr pc, uptr addr); void MutexReadUnlock(ThreadState *thr, uptr pc, uptr addr);
void MutexReadOrWriteUnlock(ThreadState *thr, uptr pc, uptr addr); void MutexReadOrWriteUnlock(ThreadState *thr, uptr pc, uptr addr);
void MutexRepair(ThreadState *thr, uptr pc, uptr addr); // call on EOWNERDEAD void MutexRepair(ThreadState *thr, uptr pc, uptr addr); // call on EOWNERDEAD
void MutexInvalidAccess(ThreadState *thr, uptr pc, uptr addr);
void Acquire(ThreadState *thr, uptr pc, uptr addr); void Acquire(ThreadState *thr, uptr pc, uptr addr);
// AcquireGlobal synchronizes the current thread with all other threads. // AcquireGlobal synchronizes the current thread with all other threads.

View File

@ -350,6 +350,14 @@ void MutexRepair(ThreadState *thr, uptr pc, uptr addr) {
s->mtx.Unlock(); s->mtx.Unlock();
} }
void MutexInvalidAccess(ThreadState *thr, uptr pc, uptr addr) {
DPrintf("#%d: MutexInvalidAccess %zx\n", thr->tid, addr);
SyncVar *s = ctx->metamap.GetOrCreateAndLock(thr, pc, addr, true);
u64 mid = s->GetId();
s->mtx.Unlock();
ReportMutexMisuse(thr, pc, ReportTypeMutexInvalidAccess, addr, mid);
}
void Acquire(ThreadState *thr, uptr pc, uptr addr) { void Acquire(ThreadState *thr, uptr pc, uptr addr) {
DPrintf("#%d: Acquire %zx\n", thr->tid, addr); DPrintf("#%d: Acquire %zx\n", thr->tid, addr);
if (thr->ignore_sync) if (thr->ignore_sync)

View File

@ -80,6 +80,8 @@ static const char *conv(ReportType typ) {
return kSuppressionMutex; return kSuppressionMutex;
else if (typ == ReportTypeMutexDoubleLock) else if (typ == ReportTypeMutexDoubleLock)
return kSuppressionMutex; return kSuppressionMutex;
else if (typ == ReportTypeMutexInvalidAccess)
return kSuppressionMutex;
else if (typ == ReportTypeMutexBadUnlock) else if (typ == ReportTypeMutexBadUnlock)
return kSuppressionMutex; return kSuppressionMutex;
else if (typ == ReportTypeMutexBadReadLock) else if (typ == ReportTypeMutexBadReadLock)

View File

@ -0,0 +1,25 @@
// RUN: %clangxx_tsan %s -o %t
// RUN: %deflake %run %t | FileCheck %s
// RUN: %deflake %run %t 1 | FileCheck %s
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[]) {
pthread_mutex_t *m = (pthread_mutex_t *)malloc(sizeof(pthread_mutex_t));
pthread_mutex_init(m, 0);
pthread_mutex_lock(m);
pthread_mutex_unlock(m);
pthread_mutex_destroy(m);
if (argc > 1 && argv[1][0] == '1')
free(m);
pthread_mutex_lock(m);
// CHECK: WARNING: ThreadSanitizer: use of an invalid mutex (e.g. uninitialized or destroyed)
// CHECK: #0 pthread_mutex_lock
// CHECK: #1 main {{.*}}mutex_lock_destroyed.cc:[[@LINE-3]]
return 0;
}