2011-07-20 04:21:41 +08:00
|
|
|
//===--- PthreadLockChecker.cpp - Check for locking problems ---*- C++ -*--===//
|
2009-11-12 14:17:47 +08:00
|
|
|
//
|
2019-01-19 16:50:56 +08:00
|
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
2009-11-12 14:17:47 +08:00
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
//
|
2011-07-20 04:31:42 +08:00
|
|
|
// This defines PthreadLockChecker, a simple lock -> unlock checker.
|
|
|
|
// Also handles XNU locks, which behave similarly enough to share code.
|
2009-11-12 14:17:47 +08:00
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
[analyzer][NFC] Move CheckerRegistry from the Core directory to Frontend
ClangCheckerRegistry is a very non-obvious, poorly documented, weird concept.
It derives from CheckerRegistry, and is placed in lib/StaticAnalyzer/Frontend,
whereas it's base is located in lib/StaticAnalyzer/Core. It was, from what I can
imagine, used to circumvent the problem that the registry functions of the
checkers are located in the clangStaticAnalyzerCheckers library, but that
library depends on clangStaticAnalyzerCore. However, clangStaticAnalyzerFrontend
depends on both of those libraries.
One can make the observation however, that CheckerRegistry has no place in Core,
it isn't used there at all! The only place where it is used is Frontend, which
is where it ultimately belongs.
This move implies that since
include/clang/StaticAnalyzer/Checkers/ClangCheckers.h only contained a single function:
class CheckerRegistry;
void registerBuiltinCheckers(CheckerRegistry ®istry);
it had to re purposed, as CheckerRegistry is no longer available to
clangStaticAnalyzerCheckers. It was renamed to BuiltinCheckerRegistration.h,
which actually describes it a lot better -- it does not contain the registration
functions for checkers, but only those generated by the tblgen files.
Differential Revision: https://reviews.llvm.org/D54436
llvm-svn: 349275
2018-12-16 00:23:51 +08:00
|
|
|
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
|
2012-12-04 17:13:33 +08:00
|
|
|
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
|
2011-03-01 09:16:21 +08:00
|
|
|
#include "clang/StaticAnalyzer/Core/Checker.h"
|
2011-02-18 05:39:17 +08:00
|
|
|
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
|
2011-02-23 09:05:36 +08:00
|
|
|
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
|
2011-08-16 06:09:50 +08:00
|
|
|
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
|
2009-11-12 14:17:47 +08:00
|
|
|
|
|
|
|
using namespace clang;
|
2010-12-23 15:20:52 +08:00
|
|
|
using namespace ento;
|
2009-11-12 14:17:47 +08:00
|
|
|
|
|
|
|
namespace {
|
2014-04-01 11:40:47 +08:00
|
|
|
|
|
|
|
struct LockState {
|
2017-05-29 22:51:39 +08:00
|
|
|
enum Kind {
|
|
|
|
Destroyed,
|
|
|
|
Locked,
|
|
|
|
Unlocked,
|
|
|
|
UntouchedAndPossiblyDestroyed,
|
|
|
|
UnlockedAndPossiblyDestroyed
|
|
|
|
} K;
|
2014-04-01 11:40:47 +08:00
|
|
|
|
|
|
|
private:
|
|
|
|
LockState(Kind K) : K(K) {}
|
|
|
|
|
|
|
|
public:
|
2015-11-05 05:37:17 +08:00
|
|
|
static LockState getLocked() { return LockState(Locked); }
|
|
|
|
static LockState getUnlocked() { return LockState(Unlocked); }
|
|
|
|
static LockState getDestroyed() { return LockState(Destroyed); }
|
2017-05-29 22:51:39 +08:00
|
|
|
static LockState getUntouchedAndPossiblyDestroyed() {
|
|
|
|
return LockState(UntouchedAndPossiblyDestroyed);
|
|
|
|
}
|
|
|
|
static LockState getUnlockedAndPossiblyDestroyed() {
|
|
|
|
return LockState(UnlockedAndPossiblyDestroyed);
|
|
|
|
}
|
2014-04-01 11:40:47 +08:00
|
|
|
|
|
|
|
bool operator==(const LockState &X) const {
|
|
|
|
return K == X.K;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool isLocked() const { return K == Locked; }
|
|
|
|
bool isUnlocked() const { return K == Unlocked; }
|
|
|
|
bool isDestroyed() const { return K == Destroyed; }
|
2017-05-29 22:51:39 +08:00
|
|
|
bool isUntouchedAndPossiblyDestroyed() const {
|
|
|
|
return K == UntouchedAndPossiblyDestroyed;
|
|
|
|
}
|
|
|
|
bool isUnlockedAndPossiblyDestroyed() const {
|
|
|
|
return K == UnlockedAndPossiblyDestroyed;
|
|
|
|
}
|
2014-04-01 11:40:47 +08:00
|
|
|
|
|
|
|
void Profile(llvm::FoldingSetNodeID &ID) const {
|
|
|
|
ID.AddInteger(K);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2017-05-29 22:51:39 +08:00
|
|
|
class PthreadLockChecker
|
|
|
|
: public Checker<check::PostStmt<CallExpr>, check::DeadSymbols> {
|
2014-03-08 04:03:18 +08:00
|
|
|
mutable std::unique_ptr<BugType> BT_doublelock;
|
2014-04-01 11:40:38 +08:00
|
|
|
mutable std::unique_ptr<BugType> BT_doubleunlock;
|
2014-04-01 11:40:47 +08:00
|
|
|
mutable std::unique_ptr<BugType> BT_destroylock;
|
2014-04-01 11:40:53 +08:00
|
|
|
mutable std::unique_ptr<BugType> BT_initlock;
|
2014-03-08 04:03:18 +08:00
|
|
|
mutable std::unique_ptr<BugType> BT_lor;
|
2011-07-20 04:21:41 +08:00
|
|
|
enum LockingSemantics {
|
|
|
|
NotApplicable = 0,
|
|
|
|
PthreadSemantics,
|
|
|
|
XNUSemantics
|
|
|
|
};
|
2009-11-12 14:17:47 +08:00
|
|
|
public:
|
2011-02-23 09:05:36 +08:00
|
|
|
void checkPostStmt(const CallExpr *CE, CheckerContext &C) const;
|
2017-05-29 22:51:39 +08:00
|
|
|
void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
|
2017-10-10 19:49:09 +08:00
|
|
|
void printState(raw_ostream &Out, ProgramStateRef State,
|
|
|
|
const char *NL, const char *Sep) const override;
|
2015-09-08 11:50:52 +08:00
|
|
|
|
2011-07-20 04:21:41 +08:00
|
|
|
void AcquireLock(CheckerContext &C, const CallExpr *CE, SVal lock,
|
|
|
|
bool isTryLock, enum LockingSemantics semantics) const;
|
2015-09-08 11:50:52 +08:00
|
|
|
|
2011-07-20 04:21:41 +08:00
|
|
|
void ReleaseLock(CheckerContext &C, const CallExpr *CE, SVal lock) const;
|
2017-05-29 22:51:39 +08:00
|
|
|
void DestroyLock(CheckerContext &C, const CallExpr *CE, SVal Lock,
|
|
|
|
enum LockingSemantics semantics) const;
|
2014-04-01 11:40:53 +08:00
|
|
|
void InitLock(CheckerContext &C, const CallExpr *CE, SVal Lock) const;
|
2014-04-01 11:40:47 +08:00
|
|
|
void reportUseDestroyedBug(CheckerContext &C, const CallExpr *CE) const;
|
2017-05-29 22:51:39 +08:00
|
|
|
ProgramStateRef resolvePossiblyDestroyedMutex(ProgramStateRef state,
|
|
|
|
const MemRegion *lockR,
|
|
|
|
const SymbolRef *sym) const;
|
2009-11-12 14:17:47 +08:00
|
|
|
};
|
|
|
|
} // end anonymous namespace
|
|
|
|
|
2017-05-29 22:51:39 +08:00
|
|
|
// A stack of locks for tracking lock-unlock order.
|
2012-11-02 09:54:06 +08:00
|
|
|
REGISTER_LIST_WITH_PROGRAMSTATE(LockSet, const MemRegion *)
|
2009-11-12 14:17:47 +08:00
|
|
|
|
2017-05-29 22:51:39 +08:00
|
|
|
// An entry for tracking lock states.
|
2014-04-01 11:40:47 +08:00
|
|
|
REGISTER_MAP_WITH_PROGRAMSTATE(LockMap, const MemRegion *, LockState)
|
2011-02-18 05:39:17 +08:00
|
|
|
|
2017-05-29 22:51:39 +08:00
|
|
|
// Return values for unresolved calls to pthread_mutex_destroy().
|
|
|
|
REGISTER_MAP_WITH_PROGRAMSTATE(DestroyRetVal, const MemRegion *, SymbolRef)
|
|
|
|
|
2011-02-23 09:05:36 +08:00
|
|
|
void PthreadLockChecker::checkPostStmt(const CallExpr *CE,
|
|
|
|
CheckerContext &C) const {
|
2011-12-01 13:57:37 +08:00
|
|
|
StringRef FName = C.getCalleeName(CE);
|
|
|
|
if (FName.empty())
|
2010-11-02 07:16:05 +08:00
|
|
|
return;
|
2011-07-20 04:21:41 +08:00
|
|
|
|
2014-04-01 11:40:47 +08:00
|
|
|
if (CE->getNumArgs() != 1 && CE->getNumArgs() != 2)
|
2011-07-20 04:21:41 +08:00
|
|
|
return;
|
2011-07-20 04:31:42 +08:00
|
|
|
|
2011-07-20 04:21:41 +08:00
|
|
|
if (FName == "pthread_mutex_lock" ||
|
|
|
|
FName == "pthread_rwlock_rdlock" ||
|
|
|
|
FName == "pthread_rwlock_wrlock")
|
2018-01-18 04:27:29 +08:00
|
|
|
AcquireLock(C, CE, C.getSVal(CE->getArg(0)), false, PthreadSemantics);
|
2011-07-20 04:21:41 +08:00
|
|
|
else if (FName == "lck_mtx_lock" ||
|
|
|
|
FName == "lck_rw_lock_exclusive" ||
|
2015-09-08 11:50:52 +08:00
|
|
|
FName == "lck_rw_lock_shared")
|
2018-01-18 04:27:29 +08:00
|
|
|
AcquireLock(C, CE, C.getSVal(CE->getArg(0)), false, XNUSemantics);
|
2011-07-20 04:21:41 +08:00
|
|
|
else if (FName == "pthread_mutex_trylock" ||
|
|
|
|
FName == "pthread_rwlock_tryrdlock" ||
|
2014-01-18 00:06:43 +08:00
|
|
|
FName == "pthread_rwlock_trywrlock")
|
2018-01-18 04:27:29 +08:00
|
|
|
AcquireLock(C, CE, C.getSVal(CE->getArg(0)),
|
2012-01-07 06:09:28 +08:00
|
|
|
true, PthreadSemantics);
|
2011-07-20 04:21:41 +08:00
|
|
|
else if (FName == "lck_mtx_try_lock" ||
|
|
|
|
FName == "lck_rw_try_lock_exclusive" ||
|
|
|
|
FName == "lck_rw_try_lock_shared")
|
2018-01-18 04:27:29 +08:00
|
|
|
AcquireLock(C, CE, C.getSVal(CE->getArg(0)), true, XNUSemantics);
|
2011-07-20 04:21:41 +08:00
|
|
|
else if (FName == "pthread_mutex_unlock" ||
|
|
|
|
FName == "pthread_rwlock_unlock" ||
|
|
|
|
FName == "lck_mtx_unlock" ||
|
|
|
|
FName == "lck_rw_done")
|
2018-01-18 04:27:29 +08:00
|
|
|
ReleaseLock(C, CE, C.getSVal(CE->getArg(0)));
|
2017-05-29 22:51:39 +08:00
|
|
|
else if (FName == "pthread_mutex_destroy")
|
2018-01-18 04:27:29 +08:00
|
|
|
DestroyLock(C, CE, C.getSVal(CE->getArg(0)), PthreadSemantics);
|
2017-05-29 22:51:39 +08:00
|
|
|
else if (FName == "lck_mtx_destroy")
|
2018-01-18 04:27:29 +08:00
|
|
|
DestroyLock(C, CE, C.getSVal(CE->getArg(0)), XNUSemantics);
|
2014-04-01 11:40:53 +08:00
|
|
|
else if (FName == "pthread_mutex_init")
|
2018-01-18 04:27:29 +08:00
|
|
|
InitLock(C, CE, C.getSVal(CE->getArg(0)));
|
2009-11-12 14:17:47 +08:00
|
|
|
}
|
|
|
|
|
2017-05-29 22:51:39 +08:00
|
|
|
// When a lock is destroyed, in some semantics(like PthreadSemantics) we are not
|
|
|
|
// sure if the destroy call has succeeded or failed, and the lock enters one of
|
|
|
|
// the 'possibly destroyed' state. There is a short time frame for the
|
|
|
|
// programmer to check the return value to see if the lock was successfully
|
|
|
|
// destroyed. Before we model the next operation over that lock, we call this
|
|
|
|
// function to see if the return value was checked by now and set the lock state
|
|
|
|
// - either to destroyed state or back to its previous state.
|
|
|
|
|
|
|
|
// In PthreadSemantics, pthread_mutex_destroy() returns zero if the lock is
|
|
|
|
// successfully destroyed and it returns a non-zero value otherwise.
|
|
|
|
ProgramStateRef PthreadLockChecker::resolvePossiblyDestroyedMutex(
|
|
|
|
ProgramStateRef state, const MemRegion *lockR, const SymbolRef *sym) const {
|
|
|
|
const LockState *lstate = state->get<LockMap>(lockR);
|
|
|
|
// Existence in DestroyRetVal ensures existence in LockMap.
|
|
|
|
// Existence in Destroyed also ensures that the lock state for lockR is either
|
|
|
|
// UntouchedAndPossiblyDestroyed or UnlockedAndPossiblyDestroyed.
|
|
|
|
assert(lstate->isUntouchedAndPossiblyDestroyed() ||
|
|
|
|
lstate->isUnlockedAndPossiblyDestroyed());
|
|
|
|
|
|
|
|
ConstraintManager &CMgr = state->getConstraintManager();
|
|
|
|
ConditionTruthVal retZero = CMgr.isNull(state, *sym);
|
|
|
|
if (retZero.isConstrainedFalse()) {
|
|
|
|
if (lstate->isUntouchedAndPossiblyDestroyed())
|
|
|
|
state = state->remove<LockMap>(lockR);
|
|
|
|
else if (lstate->isUnlockedAndPossiblyDestroyed())
|
|
|
|
state = state->set<LockMap>(lockR, LockState::getUnlocked());
|
|
|
|
} else
|
|
|
|
state = state->set<LockMap>(lockR, LockState::getDestroyed());
|
|
|
|
|
|
|
|
// Removing the map entry (lockR, sym) from DestroyRetVal as the lock state is
|
|
|
|
// now resolved.
|
|
|
|
state = state->remove<DestroyRetVal>(lockR);
|
|
|
|
return state;
|
|
|
|
}
|
|
|
|
|
2017-10-10 19:49:09 +08:00
|
|
|
void PthreadLockChecker::printState(raw_ostream &Out, ProgramStateRef State,
|
|
|
|
const char *NL, const char *Sep) const {
|
|
|
|
LockMapTy LM = State->get<LockMap>();
|
|
|
|
if (!LM.isEmpty()) {
|
|
|
|
Out << Sep << "Mutex states:" << NL;
|
|
|
|
for (auto I : LM) {
|
|
|
|
I.first->dumpToStream(Out);
|
|
|
|
if (I.second.isLocked())
|
|
|
|
Out << ": locked";
|
|
|
|
else if (I.second.isUnlocked())
|
|
|
|
Out << ": unlocked";
|
|
|
|
else if (I.second.isDestroyed())
|
|
|
|
Out << ": destroyed";
|
|
|
|
else if (I.second.isUntouchedAndPossiblyDestroyed())
|
|
|
|
Out << ": not tracked, possibly destroyed";
|
|
|
|
else if (I.second.isUnlockedAndPossiblyDestroyed())
|
|
|
|
Out << ": unlocked, possibly destroyed";
|
|
|
|
Out << NL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
LockSetTy LS = State->get<LockSet>();
|
|
|
|
if (!LS.isEmpty()) {
|
|
|
|
Out << Sep << "Mutex lock order:" << NL;
|
|
|
|
for (auto I: LS) {
|
|
|
|
I->dumpToStream(Out);
|
|
|
|
Out << NL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: Dump destroyed mutex symbols?
|
|
|
|
}
|
|
|
|
|
2009-11-12 14:17:47 +08:00
|
|
|
void PthreadLockChecker::AcquireLock(CheckerContext &C, const CallExpr *CE,
|
2011-07-20 04:21:41 +08:00
|
|
|
SVal lock, bool isTryLock,
|
|
|
|
enum LockingSemantics semantics) const {
|
2015-09-08 11:50:52 +08:00
|
|
|
|
2009-11-12 14:17:47 +08:00
|
|
|
const MemRegion *lockR = lock.getAsRegion();
|
|
|
|
if (!lockR)
|
|
|
|
return;
|
2015-09-08 11:50:52 +08:00
|
|
|
|
2012-01-27 05:29:00 +08:00
|
|
|
ProgramStateRef state = C.getState();
|
2017-05-29 22:51:39 +08:00
|
|
|
const SymbolRef *sym = state->get<DestroyRetVal>(lockR);
|
|
|
|
if (sym)
|
|
|
|
state = resolvePossiblyDestroyedMutex(state, lockR, sym);
|
2015-09-08 11:50:52 +08:00
|
|
|
|
2018-01-18 04:27:29 +08:00
|
|
|
SVal X = C.getSVal(CE);
|
2009-11-12 14:17:47 +08:00
|
|
|
if (X.isUnknownOrUndef())
|
|
|
|
return;
|
2015-09-08 11:50:52 +08:00
|
|
|
|
2013-02-20 13:52:05 +08:00
|
|
|
DefinedSVal retVal = X.castAs<DefinedSVal>();
|
2011-07-20 04:21:41 +08:00
|
|
|
|
2014-04-01 11:40:47 +08:00
|
|
|
if (const LockState *LState = state->get<LockMap>(lockR)) {
|
|
|
|
if (LState->isLocked()) {
|
|
|
|
if (!BT_doublelock)
|
|
|
|
BT_doublelock.reset(new BugType(this, "Double locking",
|
|
|
|
"Lock checker"));
|
2015-09-17 06:03:05 +08:00
|
|
|
ExplodedNode *N = C.generateErrorNode();
|
2014-04-01 11:40:47 +08:00
|
|
|
if (!N)
|
|
|
|
return;
|
2015-06-23 21:15:32 +08:00
|
|
|
auto report = llvm::make_unique<BugReport>(
|
|
|
|
*BT_doublelock, "This lock has already been acquired", N);
|
2014-04-01 11:40:47 +08:00
|
|
|
report->addRange(CE->getArg(0)->getSourceRange());
|
2015-06-23 21:15:32 +08:00
|
|
|
C.emitReport(std::move(report));
|
2011-07-20 04:21:41 +08:00
|
|
|
return;
|
2014-04-01 11:40:47 +08:00
|
|
|
} else if (LState->isDestroyed()) {
|
|
|
|
reportUseDestroyedBug(C, CE);
|
|
|
|
return;
|
|
|
|
}
|
2011-07-20 04:21:41 +08:00
|
|
|
}
|
|
|
|
|
2012-01-27 05:29:00 +08:00
|
|
|
ProgramStateRef lockSucc = state;
|
2009-11-12 14:17:47 +08:00
|
|
|
if (isTryLock) {
|
2011-07-20 04:21:41 +08:00
|
|
|
// Bifurcate the state, and allow a mode where the lock acquisition fails.
|
2012-01-27 05:29:00 +08:00
|
|
|
ProgramStateRef lockFail;
|
2011-07-20 04:21:41 +08:00
|
|
|
switch (semantics) {
|
|
|
|
case PthreadSemantics:
|
2014-03-02 21:01:17 +08:00
|
|
|
std::tie(lockFail, lockSucc) = state->assume(retVal);
|
2011-07-20 04:21:41 +08:00
|
|
|
break;
|
|
|
|
case XNUSemantics:
|
2014-03-02 21:01:17 +08:00
|
|
|
std::tie(lockSucc, lockFail) = state->assume(retVal);
|
2011-07-20 04:21:41 +08:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
llvm_unreachable("Unknown tryLock locking semantics");
|
|
|
|
}
|
2009-11-12 14:17:47 +08:00
|
|
|
assert(lockFail && lockSucc);
|
2011-10-27 05:06:34 +08:00
|
|
|
C.addTransition(lockFail);
|
2011-07-20 04:21:41 +08:00
|
|
|
|
|
|
|
} else if (semantics == PthreadSemantics) {
|
|
|
|
// Assume that the return value was 0.
|
2010-12-02 06:16:56 +08:00
|
|
|
lockSucc = state->assume(retVal, false);
|
2009-11-12 14:17:47 +08:00
|
|
|
assert(lockSucc);
|
2011-07-20 04:21:41 +08:00
|
|
|
|
|
|
|
} else {
|
|
|
|
// XNU locking semantics return void on non-try locks
|
|
|
|
assert((semantics == XNUSemantics) && "Unknown locking semantics");
|
|
|
|
lockSucc = state;
|
2009-11-12 14:17:47 +08:00
|
|
|
}
|
2015-09-08 11:50:52 +08:00
|
|
|
|
|
|
|
// Record that the lock was acquired.
|
2009-11-12 14:17:47 +08:00
|
|
|
lockSucc = lockSucc->add<LockSet>(lockR);
|
2014-04-01 11:40:47 +08:00
|
|
|
lockSucc = lockSucc->set<LockMap>(lockR, LockState::getLocked());
|
2011-10-27 05:06:34 +08:00
|
|
|
C.addTransition(lockSucc);
|
2009-11-12 14:17:47 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void PthreadLockChecker::ReleaseLock(CheckerContext &C, const CallExpr *CE,
|
2011-02-23 09:05:36 +08:00
|
|
|
SVal lock) const {
|
2009-11-12 14:17:47 +08:00
|
|
|
|
|
|
|
const MemRegion *lockR = lock.getAsRegion();
|
|
|
|
if (!lockR)
|
|
|
|
return;
|
2015-09-08 11:50:52 +08:00
|
|
|
|
2012-01-27 05:29:00 +08:00
|
|
|
ProgramStateRef state = C.getState();
|
2017-05-29 22:51:39 +08:00
|
|
|
const SymbolRef *sym = state->get<DestroyRetVal>(lockR);
|
|
|
|
if (sym)
|
|
|
|
state = resolvePossiblyDestroyedMutex(state, lockR, sym);
|
2009-11-12 14:17:47 +08:00
|
|
|
|
2014-04-01 11:40:47 +08:00
|
|
|
if (const LockState *LState = state->get<LockMap>(lockR)) {
|
|
|
|
if (LState->isUnlocked()) {
|
|
|
|
if (!BT_doubleunlock)
|
|
|
|
BT_doubleunlock.reset(new BugType(this, "Double unlocking",
|
|
|
|
"Lock checker"));
|
2015-09-17 06:03:05 +08:00
|
|
|
ExplodedNode *N = C.generateErrorNode();
|
2014-04-01 11:40:47 +08:00
|
|
|
if (!N)
|
|
|
|
return;
|
2015-06-23 21:15:32 +08:00
|
|
|
auto Report = llvm::make_unique<BugReport>(
|
|
|
|
*BT_doubleunlock, "This lock has already been unlocked", N);
|
2014-04-01 11:40:47 +08:00
|
|
|
Report->addRange(CE->getArg(0)->getSourceRange());
|
2015-06-23 21:15:32 +08:00
|
|
|
C.emitReport(std::move(Report));
|
2011-07-20 04:21:41 +08:00
|
|
|
return;
|
2014-04-01 11:40:47 +08:00
|
|
|
} else if (LState->isDestroyed()) {
|
|
|
|
reportUseDestroyedBug(C, CE);
|
|
|
|
return;
|
|
|
|
}
|
2011-07-20 04:21:41 +08:00
|
|
|
}
|
|
|
|
|
2014-04-01 11:40:38 +08:00
|
|
|
LockSetTy LS = state->get<LockSet>();
|
|
|
|
|
|
|
|
// FIXME: Better analysis requires IPA for wrappers.
|
|
|
|
|
|
|
|
if (!LS.isEmpty()) {
|
|
|
|
const MemRegion *firstLockR = LS.getHead();
|
|
|
|
if (firstLockR != lockR) {
|
|
|
|
if (!BT_lor)
|
|
|
|
BT_lor.reset(new BugType(this, "Lock order reversal", "Lock checker"));
|
2015-09-17 06:03:05 +08:00
|
|
|
ExplodedNode *N = C.generateErrorNode();
|
2014-04-01 11:40:38 +08:00
|
|
|
if (!N)
|
|
|
|
return;
|
2015-06-23 21:15:32 +08:00
|
|
|
auto report = llvm::make_unique<BugReport>(
|
|
|
|
*BT_lor, "This was not the most recently acquired lock. Possible "
|
|
|
|
"lock order reversal", N);
|
2014-04-01 11:40:38 +08:00
|
|
|
report->addRange(CE->getArg(0)->getSourceRange());
|
2015-06-23 21:15:32 +08:00
|
|
|
C.emitReport(std::move(report));
|
2014-04-01 11:40:38 +08:00
|
|
|
return;
|
|
|
|
}
|
2014-04-01 11:40:47 +08:00
|
|
|
// Record that the lock was released.
|
|
|
|
state = state->set<LockSet>(LS.getTail());
|
2014-04-01 11:40:38 +08:00
|
|
|
}
|
|
|
|
|
2014-04-01 11:40:47 +08:00
|
|
|
state = state->set<LockMap>(lockR, LockState::getUnlocked());
|
2011-10-27 05:06:34 +08:00
|
|
|
C.addTransition(state);
|
2009-11-12 14:17:47 +08:00
|
|
|
}
|
2011-02-23 09:05:36 +08:00
|
|
|
|
2014-04-01 11:40:47 +08:00
|
|
|
void PthreadLockChecker::DestroyLock(CheckerContext &C, const CallExpr *CE,
|
2017-05-29 22:51:39 +08:00
|
|
|
SVal Lock,
|
|
|
|
enum LockingSemantics semantics) const {
|
2014-04-01 11:40:47 +08:00
|
|
|
|
|
|
|
const MemRegion *LockR = Lock.getAsRegion();
|
|
|
|
if (!LockR)
|
|
|
|
return;
|
|
|
|
|
|
|
|
ProgramStateRef State = C.getState();
|
|
|
|
|
2017-05-29 22:51:39 +08:00
|
|
|
const SymbolRef *sym = State->get<DestroyRetVal>(LockR);
|
|
|
|
if (sym)
|
|
|
|
State = resolvePossiblyDestroyedMutex(State, LockR, sym);
|
|
|
|
|
2014-04-01 11:40:47 +08:00
|
|
|
const LockState *LState = State->get<LockMap>(LockR);
|
2017-05-29 22:51:39 +08:00
|
|
|
// Checking the return value of the destroy method only in the case of
|
|
|
|
// PthreadSemantics
|
|
|
|
if (semantics == PthreadSemantics) {
|
|
|
|
if (!LState || LState->isUnlocked()) {
|
|
|
|
SymbolRef sym = C.getSVal(CE).getAsSymbol();
|
|
|
|
if (!sym) {
|
|
|
|
State = State->remove<LockMap>(LockR);
|
|
|
|
C.addTransition(State);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
State = State->set<DestroyRetVal>(LockR, sym);
|
|
|
|
if (LState && LState->isUnlocked())
|
|
|
|
State = State->set<LockMap>(
|
|
|
|
LockR, LockState::getUnlockedAndPossiblyDestroyed());
|
|
|
|
else
|
|
|
|
State = State->set<LockMap>(
|
|
|
|
LockR, LockState::getUntouchedAndPossiblyDestroyed());
|
|
|
|
C.addTransition(State);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (!LState || LState->isUnlocked()) {
|
|
|
|
State = State->set<LockMap>(LockR, LockState::getDestroyed());
|
|
|
|
C.addTransition(State);
|
|
|
|
return;
|
|
|
|
}
|
2014-04-01 11:40:47 +08:00
|
|
|
}
|
|
|
|
StringRef Message;
|
|
|
|
|
|
|
|
if (LState->isLocked()) {
|
|
|
|
Message = "This lock is still locked";
|
|
|
|
} else {
|
|
|
|
Message = "This lock has already been destroyed";
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!BT_destroylock)
|
|
|
|
BT_destroylock.reset(new BugType(this, "Destroy invalid lock",
|
|
|
|
"Lock checker"));
|
2015-09-17 06:03:05 +08:00
|
|
|
ExplodedNode *N = C.generateErrorNode();
|
2014-04-01 11:40:47 +08:00
|
|
|
if (!N)
|
|
|
|
return;
|
2015-06-23 21:15:32 +08:00
|
|
|
auto Report = llvm::make_unique<BugReport>(*BT_destroylock, Message, N);
|
2014-04-01 11:40:47 +08:00
|
|
|
Report->addRange(CE->getArg(0)->getSourceRange());
|
2015-06-23 21:15:32 +08:00
|
|
|
C.emitReport(std::move(Report));
|
2014-04-01 11:40:47 +08:00
|
|
|
}
|
|
|
|
|
2014-04-01 11:40:53 +08:00
|
|
|
void PthreadLockChecker::InitLock(CheckerContext &C, const CallExpr *CE,
|
|
|
|
SVal Lock) const {
|
|
|
|
|
|
|
|
const MemRegion *LockR = Lock.getAsRegion();
|
|
|
|
if (!LockR)
|
|
|
|
return;
|
|
|
|
|
|
|
|
ProgramStateRef State = C.getState();
|
|
|
|
|
2017-05-29 22:51:39 +08:00
|
|
|
const SymbolRef *sym = State->get<DestroyRetVal>(LockR);
|
|
|
|
if (sym)
|
|
|
|
State = resolvePossiblyDestroyedMutex(State, LockR, sym);
|
|
|
|
|
2014-04-01 11:40:53 +08:00
|
|
|
const struct LockState *LState = State->get<LockMap>(LockR);
|
|
|
|
if (!LState || LState->isDestroyed()) {
|
|
|
|
State = State->set<LockMap>(LockR, LockState::getUnlocked());
|
|
|
|
C.addTransition(State);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
StringRef Message;
|
|
|
|
|
|
|
|
if (LState->isLocked()) {
|
|
|
|
Message = "This lock is still being held";
|
|
|
|
} else {
|
|
|
|
Message = "This lock has already been initialized";
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!BT_initlock)
|
|
|
|
BT_initlock.reset(new BugType(this, "Init invalid lock",
|
|
|
|
"Lock checker"));
|
2015-09-17 06:03:05 +08:00
|
|
|
ExplodedNode *N = C.generateErrorNode();
|
2014-04-01 11:40:53 +08:00
|
|
|
if (!N)
|
|
|
|
return;
|
2015-06-23 21:15:32 +08:00
|
|
|
auto Report = llvm::make_unique<BugReport>(*BT_initlock, Message, N);
|
2014-04-01 11:40:53 +08:00
|
|
|
Report->addRange(CE->getArg(0)->getSourceRange());
|
2015-06-23 21:15:32 +08:00
|
|
|
C.emitReport(std::move(Report));
|
2014-04-01 11:40:53 +08:00
|
|
|
}
|
|
|
|
|
2014-04-01 11:40:47 +08:00
|
|
|
void PthreadLockChecker::reportUseDestroyedBug(CheckerContext &C,
|
|
|
|
const CallExpr *CE) const {
|
|
|
|
if (!BT_destroylock)
|
|
|
|
BT_destroylock.reset(new BugType(this, "Use destroyed lock",
|
|
|
|
"Lock checker"));
|
2015-09-17 06:03:05 +08:00
|
|
|
ExplodedNode *N = C.generateErrorNode();
|
2014-04-01 11:40:47 +08:00
|
|
|
if (!N)
|
|
|
|
return;
|
2015-06-23 21:15:32 +08:00
|
|
|
auto Report = llvm::make_unique<BugReport>(
|
|
|
|
*BT_destroylock, "This lock has already been destroyed", N);
|
2014-04-01 11:40:47 +08:00
|
|
|
Report->addRange(CE->getArg(0)->getSourceRange());
|
2015-06-23 21:15:32 +08:00
|
|
|
C.emitReport(std::move(Report));
|
2014-04-01 11:40:47 +08:00
|
|
|
}
|
|
|
|
|
2017-05-29 22:51:39 +08:00
|
|
|
void PthreadLockChecker::checkDeadSymbols(SymbolReaper &SymReaper,
|
|
|
|
CheckerContext &C) const {
|
|
|
|
ProgramStateRef State = C.getState();
|
|
|
|
|
|
|
|
// TODO: Clean LockMap when a mutex region dies.
|
|
|
|
|
|
|
|
DestroyRetValTy TrackedSymbols = State->get<DestroyRetVal>();
|
|
|
|
for (DestroyRetValTy::iterator I = TrackedSymbols.begin(),
|
|
|
|
E = TrackedSymbols.end();
|
|
|
|
I != E; ++I) {
|
|
|
|
const SymbolRef Sym = I->second;
|
|
|
|
const MemRegion *lockR = I->first;
|
|
|
|
bool IsSymDead = SymReaper.isDead(Sym);
|
|
|
|
// Remove the dead symbol from the return value symbols map.
|
|
|
|
if (IsSymDead)
|
|
|
|
State = resolvePossiblyDestroyedMutex(State, lockR, &Sym);
|
|
|
|
}
|
|
|
|
C.addTransition(State);
|
|
|
|
}
|
|
|
|
|
2011-02-23 09:05:36 +08:00
|
|
|
void ento::registerPthreadLockChecker(CheckerManager &mgr) {
|
|
|
|
mgr.registerChecker<PthreadLockChecker>();
|
|
|
|
}
|
2019-01-26 22:23:08 +08:00
|
|
|
|
|
|
|
bool ento::shouldRegisterPthreadLockChecker(const LangOptions &LO) {
|
|
|
|
return true;
|
|
|
|
}
|