Untitled diff

Created Diff never expires
242 removals
335 lines
385 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")
SVal OldLState = state->getSVal(PthreadLockMutexStateTrait, LockR);
AcquireLock(C, CE, state->getSVal(CE->getArg(0), LCtx),
if (OldLState.isConstant(Locked) || OldLState.isConstant(Unlocked))
false, XNUSemantics);
state = state->bindLoc(PthreadLockMutexStateTrait, LockR, UndefinedVal());
else if (FName == "pthread_mutex_trylock" ||
else
FName == "pthread_rwlock_tryrdlock" ||
state = state->bindLoc(PthreadLockMutexStateTrait, LockR, Unlocked);
FName == "pthread_rwlock_trywrlock")
C.addTransition(state);
AcquireLock(C, CE, state->getSVal(CE->getArg(0), LCtx),
return;
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,
auto AcquireI = AcquireLockFunctions.find(FName);
SVal lock, bool isTryLock,
if (AcquireI != AcquireLockFunctions.end()) {
enum LockingSemantics semantics) const {
const auto &Spec = AcquireI->second;

const MemRegion *LockR = C.getSVal(CE->getArg(Spec.ArgNo)).getAsRegion();
const MemRegion *lockR = lock.getAsRegion();
if (!LockR)
if (!lockR)
return;
return;


ProgramStateRef state = C.getState();
SVal X = C.getSVal(CE);

SVal X = state->getSVal(CE, C.getLocationContext());
if (X.isUnknownOrUndef())
if (X.isUnknownOrUndef())
return;
return;

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


if (const LockState *LState = state->get<LockMap>(lockR)) {
if (LState->isLocked()) {
if (!BT_doublelock)
BT_doublelock.reset(new BugType(this, "Double locking",
"Lock checker"));
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;
} else if (LState->isDestroyed()) {
reportUseDestroyedBug(C, CE);
return;
}
}

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);
assert(lockFail && lockSucc);
C.addTransition(lockFail);
C.addTransition(lockFail);

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

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

lockSucc = lockSucc->bindLoc(PthreadLockMutexStateTrait, LockR, Locked);
// Record that the lock was acquired.
lockSucc = lockSucc->add<LockSet>(lockR);
lockSucc = lockSucc->set<LockMap>(lockR, LockState::getLocked());
C.addTransition(lockSucc);
C.addTransition(lockSucc);
return;
}
}


void PthreadLockChecker::ReleaseLock(CheckerContext &C, const CallExpr *CE,
auto ReleaseI = ReleaseLockFunctions.find(FName);
SVal lock) const {
if (ReleaseI != ReleaseLockFunctions.end()) {

const auto &Spec = ReleaseI->second;
const MemRegion *lockR = lock.getAsRegion();
const MemRegion *LockR = C.getSVal(CE->getArg(Spec.ArgNo)).getAsRegion();
if (!lockR)
if (!LockR)
return;
return;


ProgramStateRef state = C.getState();
SVal OldLState = state->getSVal(PthreadLockMutexStateTrait, LockR);

if (OldLState.isConstant(Destroyed))
if (const LockState *LState = state->get<LockMap>(lockR)) {
state = state->bindLoc(PthreadLockMutexStateTrait, LockR, UndefinedVal());
if (LState->isUnlocked()) {
else
if (!BT_doubleunlock)
state = state->bindLoc(PthreadLockMutexStateTrait, LockR, Unlocked);
BT_doubleunlock.reset(new BugType(this, "Double unlocking",
C.addTransition(state);
"Lock checker"));
ExplodedNode *N = C.generateErrorNode();
if (!N)
return;
return;
auto Report = llvm::make_unique<BugReport>(
}
*BT_doubleunlock, "This lock has already been unlocked", N);

Report->addRange(CE->getArg(0)->getSourceRange());
auto DestroyI = DestroyLockFunctions.find(FName);
C.emitReport(std::move(Report));
if (DestroyI != DestroyLockFunctions.end()) {
const auto &Spec = DestroyI->second;
const MemRegion *LockR = C.getSVal(CE->getArg(Spec.ArgNo)).getAsRegion();
if (!LockR)
return;
return;
} else if (LState->isDestroyed()) {

reportUseDestroyedBug(C, CE);
state = state->bindLoc(PthreadLockMutexStateTrait, LockR, Destroyed);
C.addTransition(state);
return;
return;
}
}
}
}


LockSetTy LS = state->get<LockSet>();


// FIXME: Better analysis requires IPA for wrappers.
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


if (!LS.isEmpty()) {
void PthreadLockStackModel::checkASTDecl(const TranslationUnitDecl *D,
const MemRegion *firstLockR = LS.getHead();
AnalysisManager &AMgr,
if (firstLockR != lockR) {
BugReporter &BR) const {
if (!BT_lor)
ASTContext &ACtx = AMgr.getASTContext();
BT_lor.reset(new BugType(this, "Lock order reversal", "Lock checker"));
PthreadLockStackPointerTrait.initialize("PthreadLockStackPointer",
ExplodedNode *N = C.generateErrorNode();
ACtx.getPointerType(ACtx.VoidPtrTy));
if (!N)
}

void PthreadLockStackModel::checkPostStmt(const CallExpr *CE,
CheckerContext &C) const {
ProgramStateRef state = C.getState();
StringRef FName = C.getCalleeName(CE);
if (FName.empty())
return;
return;
auto report = llvm::make_unique<BugReport>(

*BT_lor, "This was not the most recently acquired lock. Possible "
if (CE->getNumArgs() != 1 && CE->getNumArgs() != 2)
"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->set<LockSet>(LS.getTail());
}


state = state->set<LockMap>(lockR, LockState::getUnlocked());
auto AcquireI = AcquireLockFunctions.find(FName);
if (AcquireI != AcquireLockFunctions.end()) {
const auto &Spec = AcquireI->second;
SVal LockR = C.getSVal(CE->getArg(Spec.ArgNo));

// Record that the lock was acquired:
SValBuilder &SVB = C.getSValBuilder();
// 1. Increment the current stack pointer location.
// 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);
C.addTransition(state);
return;
}
}


void PthreadLockChecker::DestroyLock(CheckerContext &C, const CallExpr *CE,
auto ReleaseI = ReleaseLockFunctions.find(FName);
SVal Lock) const {
if (ReleaseI != ReleaseLockFunctions.end()) {

const auto &Spec = ReleaseI->second;
const MemRegion *LockR = Lock.getAsRegion();
const MemRegion *LockR = C.getSVal(CE->getArg(Spec.ArgNo)).getAsRegion();
if (!LockR)
if (!LockR)
return;
return;


ProgramStateRef State = C.getState();
// Record that the lock was released.

SValBuilder &SVB = C.getSValBuilder();
const LockState *LState = State->get<LockMap>(LockR);
ASTContext &ACtx = SVB.getContext();
if (!LState || LState->isUnlocked()) {
// Essentially, just decrement the current stack pointer location.
State = State->set<LockMap>(LockR, LockState::getDestroyed());
SVal StackPointer = state->getSVal(PthreadLockStackPointerTrait);
C.addTransition(State);
StackPointer = SVB.evalBinOp(state, BO_Sub, StackPointer,
SVB.makeIntValWithPtrWidth(1, true),
ACtx.getPointerType(ACtx.VoidPtrTy));
state = state->bindLoc(PthreadLockStackPointerTrait, StackPointer);
C.addTransition(state);
return;
return;
}
}
Text moved with changes to lines 413-419 (94.5% similarity)

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",
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)
return;

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)
if (!LockR)
return;
return;


ProgramStateRef State = C.getState();
SVal LState = state->getSVal(PthreadLockMutexStateTrait, LockR);

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


StringRef Message;
StringRef Message;


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


if (!BT_initlock)
reportBug(BT_initlock, LockE, C, "Init invalid lock", Message);
BT_initlock.reset(new BugType(this, "Init invalid lock",
}
"Lock checker"));

ExplodedNode *N = C.generateErrorNode();
auto AcquireI = AcquireLockFunctions.find(FName);
if (!N)
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;
auto Report = llvm::make_unique<BugReport>(*BT_initlock, Message, N);
Report->addRange(CE->getArg(0)->getSourceRange());
C.emitReport(std::move(Report));
}
}


void PthreadLockChecker::reportUseDestroyedBug(CheckerContext &C,
auto ReleaseI = ReleaseLockFunctions.find(FName);
const CallExpr *CE) const {
if (ReleaseI != ReleaseLockFunctions.end()) {
if (!BT_destroylock)
const auto &Spec = ReleaseI->second;
BT_destroylock.reset(new BugType(this, "Use destroyed lock",
const Expr *LockE = CE->getArg(Spec.ArgNo);
"Lock checker"));
const MemRegion *LockR = C.getSVal(LockE).getAsRegion();
ExplodedNode *N = C.generateErrorNode();
if (!LockR)
if (!N)
return;

SVal LState = state->getSVal(PthreadLockMutexStateTrait, LockR);
if (LState.isConstant(Unlocked)) {
reportBug(BT_doubleunlock, LockE, C, "Double unlocking",
"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;
return;
auto Report = llvm::make_unique<BugReport>(
}
*BT_destroylock, "This lock has already been destroyed", N);

Report->addRange(CE->getArg(0)->getSourceRange());
auto DestroyI = DestroyLockFunctions.find(FName);
C.emitReport(std::move(Report));
if (DestroyI != DestroyLockFunctions.end()) {
const auto &Spec = DestroyI->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() || LState.isConstant(Unlocked))
return;
Text moved with changes from lines 263-269 (94.5% similarity)

StringRef Message;

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


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