Comparing sensitive data, confidential files or internal emails?

Most legal and privacy policies prohibit uploading sensitive data online. Diffchecker Desktop ensures your confidential information never leaves your computer. Work offline and compare documents securely.

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>();
}
}