Untitled diff

Created Diff never expires
264 removals
335 lines
370 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 } }
};
};


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" ||
auto AcquireI = AcquireLockFunctions.find(FName);
FName == "lck_rw_try_lock_shared")
if (AcquireI != AcquireLockFunctions.end()) {
AcquireLock(C, CE, state->getSVal(CE->getArg(0), LCtx),
const auto &Spec = AcquireI->second;
true, XNUSemantics);
const MemRegion *LockR = C.getSVal(CE->getArg(Spec.ArgNo)).getAsRegion();
else if (FName == "pthread_mutex_unlock" ||
if (!LockR)
FName == "pthread_rwlock_unlock" ||
return;
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 X = C.getSVal(CE);
SVal lock, bool isTryLock,
if (X.isUnknownOrUndef())
enum LockingSemantics semantics) const {
return;
DefinedSVal retVal = X.castAs<DefinedSVal>();


const MemRegion *lockR = lock.getAsRegion();
ProgramStateRef lockSucc = state;
if (!lockR)
if (Spec.IsTrylock) {
// Bifurcate the state, and allow a mode where the lock acquisition fails.
ProgramStateRef lockFail;
switch (Spec.Semantics) {
case PthreadSemantics:
std::tie(lockFail, lockSucc) = state->assume(retVal);
break;
case XNUSemantics:
std::tie(lockSucc, lockFail) = state->assume(retVal);
break;
default:
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;
}
lockSucc = lockSucc->bindLoc(PthreadLockMutexStateTrait, LockR, Locked);
C.addTransition(lockSucc);
return;
return;
}


ProgramStateRef state = C.getState();
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;


SVal X = state->getSVal(CE, C.getLocationContext());
SVal OldLState = state->getSVal(PthreadLockMutexStateTrait, LockR);
if (X.isUnknownOrUndef())
if (OldLState.isConstant(Destroyed))
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 DestroyI = DestroyLockFunctions.find(FName);
if (LState->isLocked()) {
if (DestroyI != DestroyLockFunctions.end()) {
if (!BT_doublelock)
const auto &Spec = DestroyI->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;
} else if (LState->isDestroyed()) {
reportUseDestroyedBug(C, CE);
return;
return;
}

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


ProgramStateRef lockSucc = state;
if (isTryLock) {
// Bifurcate the state, and allow a mode where the lock acquisition fails.
ProgramStateRef lockFail;
switch (semantics) {
case PthreadSemantics:
std::tie(lockFail, lockSucc) = state->assume(retVal);
break;
case XNUSemantics:
std::tie(lockSucc, lockFail) = state->assume(retVal);
break;
default:
llvm_unreachable("Unknown tryLock locking semantics");
}
assert(lockFail && lockSucc);
C.addTransition(lockFail);


} else if (semantics == PthreadSemantics) {
namespace {
// Assume that the return value was 0.
class PthreadLockStackModel
lockSucc = state->assume(retVal, false);
: public Checker<check::ASTDecl<TranslationUnitDecl>,
assert(lockSucc);
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


} else {
void PthreadLockStackModel::checkASTDecl(const TranslationUnitDecl *D,
// XNU locking semantics return void on non-try locks
AnalysisManager &AMgr,
assert((semantics == XNUSemantics) && "Unknown locking semantics");
BugReporter &BR) const {
lockSucc = state;
ASTContext &ACtx = AMgr.getASTContext();
}
PthreadLockStackPointerTrait.initialize("PthreadLockStackPointer",

ACtx.getPointerType(ACtx.VoidPtrTy));
// Record that the lock was acquired.
lockSucc = lockSucc->add<LockSet>(lockR);
lockSucc = lockSucc->set<LockMap>(lockR, LockState::getLocked());
C.addTransition(lockSucc);
}
}


void PthreadLockChecker::ReleaseLock(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));


if (const LockState *LState = state->get<LockMap>(lockR)) {
// Record that the lock was acquired:
if (LState->isUnlocked()) {
SValBuilder &SVB = C.getSValBuilder();
if (!BT_doubleunlock)
// 1. Increment the current stack pointer location.
BT_doubleunlock.reset(new BugType(this, "Double unlocking",
// In order to simplify code, we start at offset 1.
"Lock checker"));
SVal StackPointer = state->getSVal(PthreadLockStackPointerTrait);
ExplodedNode *N = C.generateErrorNode();
StackPointer = SVB.evalBinOp(state, BO_Add, StackPointer,
if (!N)
SVB.makeIntValWithPtrWidth(1, true),
return;
PthreadLockStackPointerTrait.getTraitType());
auto Report = llvm::make_unique<BugReport>(
state = state->bindLoc(PthreadLockStackPointerTrait, StackPointer);
*BT_doubleunlock, "This lock has already been unlocked", N);
// 2. Bind the mutex to the current stack pointer location,
Report->addRange(CE->getArg(0)->getSourceRange());
state = state->bindLoc(StackPointer.castAs<Loc>(), LockR);
C.emitReport(std::move(Report));
C.addTransition(state);
return;
return;
} else if (LState->isDestroyed()) {
reportUseDestroyedBug(C, CE);
return;
}
}
}


LockSetTy LS = state->get<LockSet>();
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;


// 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"));
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;
}
// Record that the lock was released.
// Record that the lock was released.
state = state->set<LockSet>(LS.getTail());
SValBuilder &SVB = C.getSValBuilder();
ASTContext &ACtx = SVB.getContext();
// 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;
}
}

state = state->set<LockMap>(lockR, LockState::getUnlocked());
C.addTransition(state);
}
}


void PthreadLockChecker::DestroyLock(CheckerContext &C, const CallExpr *CE,
SVal Lock) const {


const MemRegion *LockR = Lock.getAsRegion();
namespace {
if (!LockR)
class PthreadLockChecker : public Checker<check::PreStmt<CallExpr>> {
return;
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;


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


const LockState *LState = State->get<LockMap>(LockR);
public:
if (!LState || LState->isUnlocked()) {
void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
State = State->set<LockMap>(LockR, LockState::getDestroyed());
};
C.addTransition(State);
} // end anonymous namespace
return;
}


StringRef Message;
void PthreadLockChecker::reportBug(std::unique_ptr<BugType> &BT, const Expr *E,

CheckerContext &C, StringRef BugName,
if (LState->isLocked()) {
StringRef Message) const {
Message = "This lock is still locked";
if (!BT)
} else {
BT.reset(new BugType(this, BugName, "Lock checker"));
Message = "This lock has already been destroyed";
}

if (!BT_destroylock)
BT_destroylock.reset(new BugType(this, "Destroy invalid lock",
"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 (!BT_initlock)
if (LState.isConstant(Destroyed)) {
BT_initlock.reset(new BugType(this, "Init invalid lock",
reportBug(BT_destroylock, LockE, C, "Use destroyed lock",
"Lock checker"));
"This lock has already been destroyed");
ExplodedNode *N = C.generateErrorNode();
return;
if (!N)
}

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_initlock, Message, N);
}
Report->addRange(CE->getArg(0)->getSourceRange());

C.emitReport(std::move(Report));
auto DestroyI = DestroyLockFunctions.find(FName);
}
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;

StringRef Message;

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


void PthreadLockChecker::reportUseDestroyedBug(CheckerContext &C,
reportBug(BT_destroylock, LockE, C, "Destroy invalid lock", Message);
const CallExpr *CE) const {
if (!BT_destroylock)
BT_destroylock.reset(new BugType(this, "Use destroyed lock",
"Lock checker"));
ExplodedNode *N = C.generateErrorNode();
if (!N)
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>();
}
}