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) {}
|
#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)
|
||||||
|
|
|
@ -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";
|
||||||
|
|
|
@ -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, \
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -27,6 +27,7 @@ enum ReportType {
|
||||||
ReportTypeThreadLeak,
|
ReportTypeThreadLeak,
|
||||||
ReportTypeMutexDestroyLocked,
|
ReportTypeMutexDestroyLocked,
|
||||||
ReportTypeMutexDoubleLock,
|
ReportTypeMutexDoubleLock,
|
||||||
|
ReportTypeMutexInvalidAccess,
|
||||||
ReportTypeMutexBadUnlock,
|
ReportTypeMutexBadUnlock,
|
||||||
ReportTypeMutexBadReadLock,
|
ReportTypeMutexBadReadLock,
|
||||||
ReportTypeMutexBadReadUnlock,
|
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 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.
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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