Untitled diff

Created Diff never expires
253 removals
335 lines
365 additions
432 lines
//===--- PthreadLockChecker.cpp - Check for locking problems ---*- C++ -*--===//
//===--- PthreadLockChecker.cpp - Check for locking problems ---*- C++ -*--===//
//
//
// The LLVM Compiler Infrastructure
// The LLVM Compiler Infrastructure
//
//
// This file is distributed under the University of Illinois Open Source
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
// License. See LICENSE.TXT for details.
//
//
//===----------------------------------------------------------------------===//
//===----------------------------------------------------------------------===//
//
//
// This defines PthreadLockChecker, a simple lock -> unlock checker.
// This defines PthreadLockChecker, a simple lock -> unlock checker.
// Also handles XNU locks, which behave similarly enough to share code.
// Also handles XNU locks, which behave similarly enough to share code.
//
//
//===----------------------------------------------------------------------===//
//===----------------------------------------------------------------------===//


#include "ClangSACheckers.h"
#include "ClangSACheckers.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
#include "llvm/ADT/ImmutableList.h"
#include "llvm/ADT/ImmutableList.h"


using namespace clang;
using namespace clang;
using namespace ento;
using namespace ento;


namespace {
namespace {
enum PthreadMutexLockState { Destroyed, Locked, Unlocked };
enum LockingSemantics { NotApplicable = 0, PthreadSemantics, XNUSemantics };


struct LockState {
SmartStateTrait PthreadLockMutexStateTrait;
enum Kind { Destroyed, Locked, Unlocked } K;
SmartStateTrait PthreadLockStackPointerTrait;


private:
struct InitLockSpec {
LockState(Kind K) : K(K) {}
unsigned ArgNo;
};
llvm::StringMap<InitLockSpec> InitLockFunctions = {
{ "pthread_mutex_init" , { 0 } }
};


public:
struct AcquireSpec {
static LockState getLocked() { return LockState(Locked); }
unsigned ArgNo;
static LockState getUnlocked() { return LockState(Unlocked); }
bool IsTrylock;
static LockState getDestroyed() { return LockState(Destroyed); }
LockingSemantics Semantics;
};
llvm::StringMap<AcquireSpec> AcquireLockFunctions = {
{ "pthread_mutex_lock", { 0, false, PthreadSemantics } },
{ "pthread_rwlock_rdlock", { 0, false, PthreadSemantics } },
{ "pthread_rwlock_wrlock", { 0, false, PthreadSemantics } },


bool operator==(const LockState &X) const {
{ "lck_mtx_lock", { 0, false, XNUSemantics } },
return K == X.K;
{ "lck_rw_lock_exclusive", { 0, false, XNUSemantics } },
}
{ "lck_rw_lock_shared", { 0, false, XNUSemantics } },


bool isLocked() const { return K == Locked; }
{ "pthread_mutex_trylock", { 0, true, PthreadSemantics } },
bool isUnlocked() const { return K == Unlocked; }
{ "pthread_rwlock_tryrdlock", { 0, true, PthreadSemantics } },
bool isDestroyed() const { return K == Destroyed; }
{ "pthread_rwlock_trywrlock", { 0, true, PthreadSemantics } },


void Profile(llvm::FoldingSetNodeID &ID) const {
{ "lck_mtx_try_lock", { 0, true, XNUSemantics } },
ID.AddInteger(K);
{ "lck_rw_try_lock_exclusive", { 0, true, XNUSemantics } },
}
{ "lck_rw_try_lock_shared", { 0, true, XNUSemantics } }
};
};


Text moved with changes to lines 272-277 (99.2% similarity)
class PthreadLockChecker : public Checker< check::PostStmt<CallExpr> > {
struct ReleaseLockSpec {
mutable std::unique_ptr<BugType> BT_doublelock;
unsigned ArgNo;
mutable std::unique_ptr<BugType> BT_doubleunlock;
};
mutable std::unique_ptr<BugType> BT_destroylock;
llvm::StringMap<ReleaseLockSpec> ReleaseLockFunctions = {
mutable std::unique_ptr<BugType> BT_initlock;
{ "pthread_mutex_unlock", { 0 } },
mutable std::unique_ptr<BugType> BT_lor;
{ "pthread_rwlock_unlock", { 0 } },
enum LockingSemantics {
{ "lck_mtx_unlock", { 0 } },
NotApplicable = 0,
{ "lck_rw_done", { 0 } }
PthreadSemantics,
};
XNUSemantics

};
struct DestroyLockSpec {
unsigned ArgNo;
};
llvm::StringMap<DestroyLockSpec> DestroyLockFunctions = {
{ "pthread_mutex_destroy", { 0 } },
{ "lck_mtx_destroy", { 0 } },
};
} // end anonymous namespace


namespace {
class PthreadLockMutexStateModel
: public Checker<check::ASTDecl<TranslationUnitDecl>,
check::PostStmt<CallExpr>> {
public:
public:
void checkASTDecl(const TranslationUnitDecl *D, AnalysisManager &AMgr,
BugReporter &BR) const;
void checkPostStmt(const CallExpr *CE, CheckerContext &C) const;
void checkPostStmt(const CallExpr *CE, CheckerContext &C) const;

void AcquireLock(CheckerContext &C, const CallExpr *CE, SVal lock,
bool isTryLock, enum LockingSemantics semantics) const;

void ReleaseLock(CheckerContext &C, const CallExpr *CE, SVal lock) const;
void DestroyLock(CheckerContext &C, const CallExpr *CE, SVal Lock) const;
void InitLock(CheckerContext &C, const CallExpr *CE, SVal Lock) const;
void reportUseDestroyedBug(CheckerContext &C, const CallExpr *CE) const;
};
};
} // end anonymous namespace
} // end anonymous namespace


// GDM Entry for tracking lock state.
void PthreadLockMutexStateModel::checkASTDecl(const TranslationUnitDecl *D,
REGISTER_LIST_WITH_PROGRAMSTATE(LockSet, const MemRegion *)
AnalysisManager &AMgr,

BugReporter &BR) const {
REGISTER_MAP_WITH_PROGRAMSTATE(LockMap, const MemRegion *, LockState)
PthreadLockMutexStateTrait.initialize("PthreadLockMutexState",
AMgr.getASTContext().IntTy);
}


void PthreadLockChecker::checkPostStmt(const CallExpr *CE,
void PthreadLockMutexStateModel::checkPostStmt(const CallExpr *CE,
CheckerContext &C) const {
CheckerContext &C) const {
ProgramStateRef state = C.getState();
ProgramStateRef state = C.getState();
const LocationContext *LCtx = C.getLocationContext();
StringRef FName = C.getCalleeName(CE);
StringRef FName = C.getCalleeName(CE);
if (FName.empty())
if (FName.empty())
return;
return;


if (CE->getNumArgs() != 1 && CE->getNumArgs() != 2)
if (CE->getNumArgs() != 1 && CE->getNumArgs() != 2)
return;
return;


if (FName == "pthread_mutex_lock" ||
auto InitI = InitLockFunctions.find(FName);
FName == "pthread_rwlock_rdlock" ||
if (InitI != InitLockFunctions.end()) {
FName == "pthread_rwlock_wrlock")
const auto &Spec = InitI->second;
AcquireLock(C, CE, state->getSVal(CE->getArg(0), LCtx),
const MemRegion *LockR = C.getSVal(CE->getArg(Spec.ArgNo)).getAsRegion();
false, PthreadSemantics);
if (!LockR)
else if (FName == "lck_mtx_lock" ||
return;
FName == "lck_rw_lock_exclusive" ||
FName == "lck_rw_lock_shared")
AcquireLock(C, CE, state->getSVal(CE->getArg(0), LCtx),
false, XNUSemantics);
else if (FName == "pthread_mutex_trylock" ||
FName == "pthread_rwlock_tryrdlock" ||
FName == "pthread_rwlock_trywrlock")
AcquireLock(C, CE, state->getSVal(CE->getArg(0), LCtx),
true, PthreadSemantics);
else if (FName == "lck_mtx_try_lock" ||
FName == "lck_rw_try_lock_exclusive" ||
FName == "lck_rw_try_lock_shared")
AcquireLock(C, CE, state->getSVal(CE->getArg(0), LCtx),
true, XNUSemantics);
else if (FName == "pthread_mutex_unlock" ||
FName == "pthread_rwlock_unlock" ||
FName == "lck_mtx_unlock" ||
FName == "lck_rw_done")
ReleaseLock(C, CE, state->getSVal(CE->getArg(0), LCtx));
else if (FName == "pthread_mutex_destroy" ||
FName == "lck_mtx_destroy")
DestroyLock(C, CE, state->getSVal(CE->getArg(0), LCtx));
else if (FName == "pthread_mutex_init")
InitLock(C, CE, state->getSVal(CE->getArg(0), LCtx));
}

void PthreadLockChecker::AcquireLock(CheckerContext &C, const CallExpr *CE,
SVal lock, bool isTryLock,
enum LockingSemantics semantics) const {

const MemRegion *lockR = lock.getAsRegion();
if (!lockR)
return;

ProgramStateRef state = C.getState();


SVal X = state->getSVal(CE, C.getLocationContext());
SVal OldLState = state->getSVal(PthreadLockMutexStateTrait, LockR);
if (X.isUnknownOrUndef())
if (OldLState.isConstant(Locked) || OldLState.isConstant(Unlocked))
state = state->bindLoc(PthreadLockMutexStateTrait, LockR, UndefinedVal());
else
state = state->bindLoc(PthreadLockMutexStateTrait, LockR, Unlocked);
C.addTransition(state);
return;
return;

}
DefinedSVal retVal = X.castAs<DefinedSVal>();


if (const LockState *LState = state->get<LockMap>(lockR)) {
auto AcquireI = AcquireLockFunctions.find(FName);
if (LState->isLocked()) {
if (AcquireI != AcquireLockFunctions.end()) {
if (!BT_doublelock)
const auto &Spec = AcquireI->second;
BT_doublelock.reset(new BugType(this, "Double locking",
const MemRegion *LockR = C.getSVal(CE->getArg(Spec.ArgNo)).getAsRegion();
"Lock checker"));
if (!LockR)
ExplodedNode *N = C.generateErrorNode();
if (!N)
return;
auto report = llvm::make_unique<BugReport>(
*BT_doublelock, "This lock has already been acquired", N);
report->addRange(CE->getArg(0)->getSourceRange());
C.emitReport(std::move(report));
return;
return;
} else if (LState->isDestroyed()) {

reportUseDestroyedBug(C, CE);
SVal X = C.getSVal(CE);
if (X.isUnknownOrUndef())
return;
return;
}
DefinedSVal retVal = X.castAs<DefinedSVal>();
}


ProgramStateRef lockSucc = state;
ProgramStateRef lockSucc = state;
if (isTryLock) {
if (Spec.IsTrylock) {
// Bifurcate the state, and allow a mode where the lock acquisition fails.
// Bifurcate the state, and allow a mode where the lock acquisition fails.
ProgramStateRef lockFail;
ProgramStateRef lockFail;
switch (semantics) {
switch (Spec.Semantics) {
case PthreadSemantics:
case PthreadSemantics:
std::tie(lockFail, lockSucc) = state->assume(retVal);
std::tie(lockFail, lockSucc) = state->assume(retVal);
break;
break;
case XNUSemantics:
case XNUSemantics:
std::tie(lockSucc, lockFail) = state->assume(retVal);
std::tie(lockSucc, lockFail) = state->assume(retVal);
break;
break;
default:
default:
llvm_unreachable("Unknown tryLock locking semantics");
llvm_unreachable("Unknown tryLock locking semantics");
}
assert(lockFail && lockSucc);
C.addTransition(lockFail);
} else if (Spec.Semantics == PthreadSemantics) {
// Assume that the return value was 0.
lockSucc = state->assume(retVal, false);
assert(lockSucc);
} else {
// XNU locking semantics return void on non-try locks
assert((Spec.Semantics == XNUSemantics) && "Unknown locking semantics");
lockSucc = state;
}
}
assert(lockFail && lockSucc);
lockSucc = lockSucc->bindLoc(PthreadLockMutexStateTrait, LockR, Locked);
C.addTransition(lockFail);
C.addTransition(lockSucc);

return;
} else if (semantics == PthreadSemantics) {
// Assume that the return value was 0.
lockSucc = state->assume(retVal, false);
assert(lockSucc);

} else {
// XNU locking semantics return void on non-try locks
assert((semantics == XNUSemantics) && "Unknown locking semantics");
lockSucc = state;
}
}


// Record that the lock was acquired.
auto ReleaseI = ReleaseLockFunctions.find(FName);
lockSucc = lockSucc->add<LockSet>(lockR);
if (ReleaseI != ReleaseLockFunctions.end()) {
lockSucc = lockSucc->set<LockMap>(lockR, LockState::getLocked());
const auto &Spec = ReleaseI->second;
C.addTransition(lockSucc);
const MemRegion *LockR = C.getSVal(CE->getArg(Spec.ArgNo)).getAsRegion();
}
if (!LockR)

return;
void PthreadLockChecker::ReleaseLock(CheckerContext &C, const CallExpr *CE,
SVal lock) const {


const MemRegion *lockR = lock.getAsRegion();
SVal OldLState = state->getSVal(PthreadLockMutexStateTrait, LockR);
if (!lockR)
if (OldLState.isConstant(Destroyed))
state = state->bindLoc(PthreadLockMutexStateTrait, LockR, UndefinedVal());
else
state = state->bindLoc(PthreadLockMutexStateTrait, LockR, Unlocked);
C.addTransition(state);
return;
return;

ProgramStateRef state = C.getState();

if (const LockState *LState = state->get<LockMap>(lockR)) {
if (LState->isUnlocked()) {
if (!BT_doubleunlock)
BT_doubleunlock.reset(new BugType(this, "Double unlocking",
"Lock checker"));
ExplodedNode *N = C.generateErrorNode();
if (!N)
return;
auto Report = llvm::make_unique<BugReport>(
*BT_doubleunlock, "This lock has already been unlocked", N);
Report->addRange(CE->getArg(0)->getSourceRange());
C.emitReport(std::move(Report));
return;
} else if (LState->isDestroyed()) {
reportUseDestroyedBug(C, CE);
return;
}
}
}


LockSetTy LS = state->get<LockSet>();
auto DestroyI = DestroyLockFunctions.find(FName);

if (DestroyI != DestroyLockFunctions.end()) {
// FIXME: Better analysis requires IPA for wrappers.
const auto &Spec = DestroyI->second;

const MemRegion *LockR = C.getSVal(CE->getArg(Spec.ArgNo)).getAsRegion();
if (!LS.isEmpty()) {
if (!LockR)
const MemRegion *firstLockR = LS.getHead();
if (firstLockR != lockR) {
if (!BT_lor)
BT_lor.reset(new BugType(this, "Lock order reversal", "Lock checker"));
ExplodedNode *N = C.generateErrorNode();
if (!N)
return;
auto report = llvm::make_unique<BugReport>(
*BT_lor, "This was not the most recently acquired lock. Possible "
"lock order reversal", N);
report->addRange(CE->getArg(0)->getSourceRange());
C.emitReport(std::move(report));
return;
return;
}

// Record that the lock was released.
state = state->bindLoc(PthreadLockMutexStateTrait, LockR, Destroyed);
state = state->set<LockSet>(LS.getTail());
C.addTransition(state);
return;
}
}
}


state = state->set<LockMap>(lockR, LockState::getUnlocked());

C.addTransition(state);
namespace {
class PthreadLockStackModel
: public Checker<check::ASTDecl<TranslationUnitDecl>,
check::PostStmt<CallExpr>> {
public:
void checkASTDecl(const TranslationUnitDecl *D, AnalysisManager &AMgr,
BugReporter &BR) const;
void checkPostStmt(const CallExpr *CE, CheckerContext &C) const;
};
} // end anonymous namespace

void PthreadLockStackModel::checkASTDecl(const TranslationUnitDecl *D,
AnalysisManager &AMgr,
BugReporter &BR) const {
ASTContext &ACtx = AMgr.getASTContext();
PthreadLockStackPointerTrait.initialize("PthreadLockStackPointer",
ACtx.getPointerType(ACtx.VoidPtrTy));
}
}


void PthreadLockChecker::DestroyLock(CheckerContext &C, const CallExpr *CE,
void PthreadLockStackModel::checkPostStmt(const CallExpr *CE,
SVal Lock) const {
CheckerContext &C) const {
ProgramStateRef state = C.getState();
StringRef FName = C.getCalleeName(CE);
if (FName.empty())
return;


const MemRegion *LockR = Lock.getAsRegion();
if (CE->getNumArgs() != 1 && CE->getNumArgs() != 2)
if (!LockR)
return;
return;


ProgramStateRef State = C.getState();
auto AcquireI = AcquireLockFunctions.find(FName);
if (AcquireI != AcquireLockFunctions.end()) {
const auto &Spec = AcquireI->second;
SVal LockR = C.getSVal(CE->getArg(Spec.ArgNo));


const LockState *LState = State->get<LockMap>(LockR);
// Record that the lock was acquired:
if (!LState || LState->isUnlocked()) {
SValBuilder &SVB = C.getSValBuilder();
State = State->set<LockMap>(LockR, LockState::getDestroyed());
// 1. Increment the current stack pointer location.
C.addTransition(State);
// In order to simplify code, we start at offset 1.
SVal StackPointer = state->getSVal(PthreadLockStackPointerTrait);
StackPointer = SVB.evalBinOp(state, BO_Add, StackPointer,
SVB.makeIntValWithPtrWidth(1, true),
PthreadLockStackPointerTrait.getTraitType());
state = state->bindLoc(PthreadLockStackPointerTrait, StackPointer);
// 2. Bind the mutex to the current stack pointer location,
state = state->bindLoc(StackPointer.castAs<Loc>(), LockR);
C.addTransition(state);
return;
return;
}
}


StringRef Message;
auto ReleaseI = ReleaseLockFunctions.find(FName);
if (ReleaseI != ReleaseLockFunctions.end()) {
const auto &Spec = ReleaseI->second;
const MemRegion *LockR = C.getSVal(CE->getArg(Spec.ArgNo)).getAsRegion();
if (!LockR)
return;


if (LState->isLocked()) {
// Record that the lock was released.
Message = "This lock is still locked";
SValBuilder &SVB = C.getSValBuilder();
} else {
ASTContext &ACtx = SVB.getContext();
Message = "This lock has already been destroyed";
// Essentially, just decrement the current stack pointer location.
SVal StackPointer = state->getSVal(PthreadLockStackPointerTrait);
StackPointer = SVB.evalBinOp(state, BO_Sub, StackPointer,
SVB.makeIntValWithPtrWidth(1, true),
ACtx.getPointerType(ACtx.VoidPtrTy));
state = state->bindLoc(PthreadLockStackPointerTrait, StackPointer);
C.addTransition(state);
return;
}
}
}


if (!BT_destroylock)

BT_destroylock.reset(new BugType(this, "Destroy invalid lock",
namespace {
Text moved with changes from lines 52-57 (99.2% similarity)
"Lock checker"));
class PthreadLockChecker : public Checker<check::PreStmt<CallExpr>> {
mutable std::unique_ptr<BugType> BT_doublelock;
mutable std::unique_ptr<BugType> BT_doubleunlock;
mutable std::unique_ptr<BugType> BT_destroylock;
mutable std::unique_ptr<BugType> BT_initlock;
mutable std::unique_ptr<BugType> BT_lor;

void reportBug(std::unique_ptr<BugType> &BT, const Expr *E, CheckerContext &C,
StringRef BugName, StringRef Message) const;

public:
void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
};
} // end anonymous namespace

void PthreadLockChecker::reportBug(std::unique_ptr<BugType> &BT, const Expr *E,
CheckerContext &C, StringRef BugName,
StringRef Message) const {
if (!BT)
BT.reset(new BugType(this, BugName, "Lock checker"));
ExplodedNode *N = C.generateErrorNode();
ExplodedNode *N = C.generateErrorNode();
if (!N)
if (!N)
return;
return;
auto Report = llvm::make_unique<BugReport>(*BT_destroylock, Message, N);
auto report = llvm::make_unique<BugReport>(*BT, Message, N);
Report->addRange(CE->getArg(0)->getSourceRange());
report->addRange(E->getSourceRange());
C.emitReport(std::move(Report));
C.emitReport(std::move(report));
}
}


void PthreadLockChecker::InitLock(CheckerContext &C, const CallExpr *CE,
void PthreadLockChecker::checkPreStmt(const CallExpr *CE,
SVal Lock) const {
CheckerContext &C) const {
ProgramStateRef state = C.getState();
StringRef FName = C.getCalleeName(CE);
if (FName.empty())
return;


const MemRegion *LockR = Lock.getAsRegion();
if (CE->getNumArgs() != 1 && CE->getNumArgs() != 2)
if (!LockR)
return;
return;


ProgramStateRef State = C.getState();
auto InitI = InitLockFunctions.find(FName);
if (InitI != InitLockFunctions.end()) {
const auto &Spec = InitI->second;
const Expr *LockE = CE->getArg(Spec.ArgNo);
const MemRegion *LockR = C.getSVal(LockE).getAsRegion();
if (!LockR)
return;


const struct LockState *LState = State->get<LockMap>(LockR);
SVal LState = state->getSVal(PthreadLockMutexStateTrait, LockR);
if (!LState || LState->isDestroyed()) {
if (!LState.isConstant() || LState.isConstant(Destroyed))
State = State->set<LockMap>(LockR, LockState::getUnlocked());
return;
C.addTransition(State);

StringRef Message;

if (LState.isConstant(Locked)) {
Message = "This lock is still being held";
} else {
Message = "This lock has already been initialized";
}

reportBug(BT_initlock, LockE, C, "Init invalid lock", Message);
}

auto AcquireI = AcquireLockFunctions.find(FName);
if (AcquireI != AcquireLockFunctions.end()) {
const auto &Spec = AcquireI->second;
const Expr *LockE = CE->getArg(Spec.ArgNo);
const MemRegion *LockR = C.getSVal(LockE).getAsRegion();
if (!LockR)
return;

SVal LState = state->getSVal(PthreadLockMutexStateTrait, LockR);
if (LState.isConstant(Locked)) {
reportBug(BT_doublelock, LockE, C, "Double locking",
"This lock has already been acquired");
return;
}
if (LState.isConstant(Destroyed)) {
reportBug(BT_destroylock, LockE, C, "Use destroyed lock",
"This lock has already been destroyed");
return;
}
return;
return;
}
}


StringRef Message;
auto ReleaseI = ReleaseLockFunctions.find(FName);
if (ReleaseI != ReleaseLockFunctions.end()) {
const auto &Spec = ReleaseI->second;
const Expr *LockE = CE->getArg(Spec.ArgNo);
const MemRegion *LockR = C.getSVal(LockE).getAsRegion();
if (!LockR)
return;


if (LState->isLocked()) {
SVal LState = state->getSVal(PthreadLockMutexStateTrait, LockR);
Message = "This lock is still being held";
if (LState.isConstant(Unlocked)) {
} else {
reportBug(BT_doubleunlock, LockE, C, "Double unlocking",
Message = "This lock has already been initialized";
"This lock has already been unlocked");
return;
}

if (LState.isConstant(Destroyed)) {
reportBug(BT_destroylock, LockE, C, "Use destroyed lock",
"This lock has already been destroyed");
return;
}

const MemRegion *StackPointerR =
state->getSVal(PthreadLockStackPointerTrait).getAsRegion();
assert(StackPointerR);

// Below offset 0 on our stack lies the stack frame of the caller function
// of the top-level function from which we've started our analysis. While
// we've no problem symbolicating the mutex we last locked in that imaginary
// caller code, our store model, which thinks of all different base regions
// as certainly-different, would make us think we're unlocking a different
// mutex, which is most likely incorrect, so we avoid this check.
RegionOffset RO = StackPointerR->getAsOffset();
assert(!RO.hasSymbolicOffset());
if (RO.getOffset() <= 0) // Recall that we start from 1.
return;

const MemRegion *firstLockR = state->getSVal(StackPointerR).getAsRegion();
if (firstLockR && firstLockR != LockR) {
reportBug(BT_lor, LockE, C, "Lock order reversal",
"This was not the most recently acquired lock. Possible "
"lock order reversal");
return;
}

return;
}
}


if (!BT_initlock)
auto DestroyI = DestroyLockFunctions.find(FName);
BT_initlock.reset(new BugType(this, "Init invalid lock",
if (DestroyI != DestroyLockFunctions.end()) {
"Lock checker"));
const auto &Spec = DestroyI->second;
ExplodedNode *N = C.generateErrorNode();
const Expr *LockE = CE->getArg(Spec.ArgNo);
if (!N)
const MemRegion *LockR = C.getSVal(LockE).getAsRegion();
return;
if (!LockR)
auto Report = llvm::make_unique<BugReport>(*BT_initlock, Message, N);
return;
Report->addRange(CE->getArg(0)->getSourceRange());
C.emitReport(std::move(Report));
}


void PthreadLockChecker::reportUseDestroyedBug(CheckerContext &C,
SVal LState = state->getSVal(PthreadLockMutexStateTrait, LockR);
const CallExpr *CE) const {
if (!LState.isConstant() || LState.isConstant(Unlocked))
if (!BT_destroylock)
return;
BT_destroylock.reset(new BugType(this, "Use destroyed lock",

"Lock checker"));
StringRef Message;
ExplodedNode *N = C.generateErrorNode();

if (!N)
if (LState.isConstant(Locked)) {
Message = "This lock is still locked";
} else {
Message = "This lock has already been destroyed";
}

reportBug(BT_destroylock, LockE, C, "Destroy invalid lock", Message);
return;
return;
auto Report = llvm::make_unique<BugReport>(
}
*BT_destroylock, "This lock has already been destroyed", N);
Report->addRange(CE->getArg(0)->getSourceRange());
C.emitReport(std::move(Report));
}
}


void ento::registerPthreadLockChecker(CheckerManager &mgr) {
void ento::registerPthreadLockCheckerV2(CheckerManager &mgr) {
mgr.registerChecker<PthreadLockMutexStateModel>();
mgr.registerChecker<PthreadLockStackModel>();
mgr.registerChecker<PthreadLockChecker>();
mgr.registerChecker<PthreadLockChecker>();
}
}