forked from OSchip/llvm-project
[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:
parent
60228bdb80
commit
46bf454d18
|
@ -91,6 +91,10 @@
|
|||
#define COMMON_INTERCEPTOR_MUTEX_REPAIR(ctx, m) {}
|
||||
#endif
|
||||
|
||||
#ifndef COMMON_INTERCEPTOR_MUTEX_INVALID
|
||||
#define COMMON_INTERCEPTOR_MUTEX_INVALID(ctx, m) {}
|
||||
#endif
|
||||
|
||||
#ifndef COMMON_INTERCEPTOR_HANDLE_RECVMSG
|
||||
#define COMMON_INTERCEPTOR_HANDLE_RECVMSG(ctx, msg) ((void)(msg))
|
||||
#endif
|
||||
|
@ -3346,6 +3350,8 @@ INTERCEPTOR(int, pthread_mutex_lock, void *m) {
|
|||
COMMON_INTERCEPTOR_MUTEX_REPAIR(ctx, m);
|
||||
if (res == 0 || res == errno_EOWNERDEAD)
|
||||
COMMON_INTERCEPTOR_MUTEX_LOCK(ctx, m);
|
||||
if (res == errno_EINVAL)
|
||||
COMMON_INTERCEPTOR_MUTEX_INVALID(ctx, m);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
@ -3353,7 +3359,10 @@ INTERCEPTOR(int, pthread_mutex_unlock, void *m) {
|
|||
void *ctx;
|
||||
COMMON_INTERCEPTOR_ENTER(ctx, pthread_mutex_unlock, 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)
|
||||
|
|
|
@ -25,6 +25,7 @@ static const char *ReportTypeDescription(ReportType typ) {
|
|||
if (typ == ReportTypeThreadLeak) return "thread-leak";
|
||||
if (typ == ReportTypeMutexDestroyLocked) return "locked-mutex-destroy";
|
||||
if (typ == ReportTypeMutexDoubleLock) return "mutex-double-lock";
|
||||
if (typ == ReportTypeMutexInvalidAccess) return "mutex-invalid-access";
|
||||
if (typ == ReportTypeMutexBadUnlock) return "mutex-bad-unlock";
|
||||
if (typ == ReportTypeMutexBadReadLock) return "mutex-bad-read-lock";
|
||||
if (typ == ReportTypeMutexBadReadUnlock) return "mutex-bad-read-unlock";
|
||||
|
|
|
@ -2409,6 +2409,10 @@ static void HandleRecvmsg(ThreadState *thr, uptr pc,
|
|||
MutexRepair(((TsanInterceptorContext *)ctx)->thr, \
|
||||
((TsanInterceptorContext *)ctx)->pc, (uptr)m)
|
||||
|
||||
#define COMMON_INTERCEPTOR_MUTEX_INVALID(ctx, m) \
|
||||
MutexInvalidAccess(((TsanInterceptorContext *)ctx)->thr, \
|
||||
((TsanInterceptorContext *)ctx)->pc, (uptr)m)
|
||||
|
||||
#if !SANITIZER_MAC
|
||||
#define COMMON_INTERCEPTOR_HANDLE_RECVMSG(ctx, msg) \
|
||||
HandleRecvmsg(((TsanInterceptorContext *)ctx)->thr, \
|
||||
|
|
|
@ -96,6 +96,8 @@ static const char *ReportTypeString(ReportType typ) {
|
|||
return "destroy of a locked mutex";
|
||||
if (typ == ReportTypeMutexDoubleLock)
|
||||
return "double lock of a mutex";
|
||||
if (typ == ReportTypeMutexInvalidAccess)
|
||||
return "use of an invalid mutex (e.g. uninitialized or destroyed)";
|
||||
if (typ == ReportTypeMutexBadUnlock)
|
||||
return "unlock of an unlocked mutex (or by a wrong thread)";
|
||||
if (typ == ReportTypeMutexBadReadLock)
|
||||
|
|
|
@ -27,6 +27,7 @@ enum ReportType {
|
|||
ReportTypeThreadLeak,
|
||||
ReportTypeMutexDestroyLocked,
|
||||
ReportTypeMutexDoubleLock,
|
||||
ReportTypeMutexInvalidAccess,
|
||||
ReportTypeMutexBadUnlock,
|
||||
ReportTypeMutexBadReadLock,
|
||||
ReportTypeMutexBadReadUnlock,
|
||||
|
|
|
@ -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 MutexReadOrWriteUnlock(ThreadState *thr, uptr pc, uptr addr);
|
||||
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);
|
||||
// AcquireGlobal synchronizes the current thread with all other threads.
|
||||
|
|
|
@ -350,6 +350,14 @@ void MutexRepair(ThreadState *thr, uptr pc, uptr addr) {
|
|||
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) {
|
||||
DPrintf("#%d: Acquire %zx\n", thr->tid, addr);
|
||||
if (thr->ignore_sync)
|
||||
|
|
|
@ -80,6 +80,8 @@ static const char *conv(ReportType typ) {
|
|||
return kSuppressionMutex;
|
||||
else if (typ == ReportTypeMutexDoubleLock)
|
||||
return kSuppressionMutex;
|
||||
else if (typ == ReportTypeMutexInvalidAccess)
|
||||
return kSuppressionMutex;
|
||||
else if (typ == ReportTypeMutexBadUnlock)
|
||||
return kSuppressionMutex;
|
||||
else if (typ == ReportTypeMutexBadReadLock)
|
||||
|
|
|
@ -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;
|
||||
}
|
Loading…
Reference in New Issue