Diff
checker
Texte
Texte
Images
Documents
Excel
Dossiers
Legal
Enterprise
Application de bureau
Prix
Se connecter
Télécharger Diffchecker Desktop
Comparer le texte
Trouver la différence entre deux fichiers texte
Outils
Historique
Éditeur live
Cacher identiques
Sans retour à la ligne
Vue
Divisé
Unifié
Niveau de précision
Intelligent
Mot
Caractère
Coloration syntaxique
Choisir la syntaxe
Ignorer
Transformer le texte
Aller au premier écart
Modifier l'entrée
Diffchecker Desktop
La façon la plus sécurisée d'utiliser Diffchecker. Obtenez l'application Diffchecker Desktop : vos diffs ne quittent jamais votre ordinateur !
Obtenir Desktop
Untitled diff
Créé
il y a 10 ans
Le diff n'expire jamais
Effacer
Exporter
Partager
Expliquer
253 suppressions
Lignes
Total
Supprimé
Caractères
Total
Supprimé
Pour continuer à utiliser cette fonctionnalité, passez à
Diff
checker
Pro
Voir les prix
335 lignes
Copier tout
377 ajouts
Lignes
Total
Ajouté
Caractères
Total
Ajouté
Pour continuer à utiliser cette fonctionnalité, passez à
Diff
checker
Pro
Voir les prix
432 lignes
Copier tout
//===--- 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"
Copier
Copié
Copier
Copié
#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 {
Copier
Copié
Copier
Copié
enum PthreadMutexLockState { Destroyed, Locked, Unlocked };
enum LockingSemantics { NotApplicable = 0, PthreadSemantics, XNUSemantics };
Copier
Copié
Copier
Copié
struct LockState {
SmartStateTrait PthreadLockMutexStateTrait;
enum Kind { Destroyed, Locked, Unlocked } K;
SmartStateTrait PthreadLockStackPointerTrait;
Copier
Copié
Copier
Copié
private:
struct InitLockSpec {
LockState(Kind K) : K(K) {}
unsigned ArgNo;
};
llvm::StringMap<InitLockSpec> InitLockFunctions = {
{ "pthread_mutex_init" , { 0 } }
};
Copier
Copié
Copier
Copié
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 } },
Copier
Copié
Copier
Copié
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 } },
Copier
Copié
Copier
Copié
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 } },
Copier
Copié
Copier
Copié
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 } }
};
};
Copier
Copié
Copier
Copié
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:
Copier
Copié
Copier
Copié
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;
Copier
Copié
Copier
Copié
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
Copier
Copié
Copier
Copié
// 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);
}
Copier
Copié
Copier
Copié
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();
Copier
Copié
Copier
Copié
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;
Copier
Copié
Copier
Copié
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 (!
L
ockR)
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 (!
l
ockR)
return;
ProgramStateRef state = C.getState();
Copier
Copié
Copier
Copié
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;
Copier
Copié
Copier
Copié
}
DefinedSVal retVal = X.castAs<DefinedSVal>();
Copier
Copié
Copier
Copié
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;
Copier
Copié
Copier
Copié
} else if (LState->isDestroyed()) {
reportUseDestroyedBug(C, CE);
SVal X = C.getSVal(CE);
if (X.isUnknownOrUndef())
return;
return;
Copier
Copié
Copier
Copié
}
DefinedSVal retVal = X.castAs<DefinedSVal>();
}
Copier
Copié
Copier
Copié
ProgramStateRef lockSucc = state;
ProgramStateRef lockSucc = state;
if (
i
sTry
L
ock) {
if (
Spec.I
sTry
l
ock) {
// 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 (
s
emantics) {
switch (
Spec.S
emantics) {
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;
}
}
Copier
Copié
Copier
Copié
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;
}
}
Copier
Copié
Copier
Copié
// 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 {
Copier
Copié
Copier
Copié
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;
Copier
Copié
Copier
Copié
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;
}
}
}
Copier
Copié
Copier
Copié
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 *
first
LockR =
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;
Copier
Copié
Copier
Copié
}
// Record that the lock was released.
state = state->bindLoc(PthreadLockMutexStateTrait, LockR, Destroyed);
state = state->set<LockSet>(LS.getTail());
C.addTransition(state);
return;
}
}
Copier
Copié
Copier
Copié
}
Copier
Copié
Copier
Copié
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));
}
}
Copier
Copié
Copier
Copié
void PthreadLock
Checker::DestroyLock(CheckerContext &C,
const CallExpr *CE,
void PthreadLock
StackModel::checkPostStmt(
const CallExpr *CE,
SVal Lock
) const {
CheckerContext &C
) const {
ProgramStateRef state = C.getState();
StringRef FName = C.getCalleeName(CE);
if (FName.empty())
return;
Copier
Copié
Copier
Copié
const MemRegion *LockR = Lock.getAsRegion();
if (CE->getNumArgs() != 1 && CE->getNumArgs() != 2)
if (!LockR)
return;
return;
Copier
Copié
Copier
Copié
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)
);
Copier
Copié
Copier
Copié
const LockState *LState = S
tate->
get<LockMap>(LockR);
// Record that the lock was acquired:
if (!LS
tate
|| LS
tate->
isUnlocked()) {
SValBuilder &SVB = C.getSValBuilder();
S
tate =
S
tate->
set<LockMap>(LockR, LockState::getDestroyed()
);
// 1. Increment the current stack pointer location.
C.addTransition(
S
tate);
// In order to simplify code, we start at offset 1.
SVal StackPointer = s
tate->
getSVal(PthreadLockStackPointerTrait);
StackPointer = SVB.evalBinOp(state, BO_Add, StackPointer,
SVB.makeIntValWithPtrWidth(1, true),
PthreadLockStackPointerTrait.getTraitType());
s
tate
= s
tate->
bindLoc(PthreadLockStackPointerTrait, StackPointer);
// 2. Bind the mutex to the current stack pointer location,
s
tate =
s
tate->
bindLoc(StackPointer.castAs<Loc>(), LockR
);
C.addTransition(
s
tate);
return;
return;
}
}
Copier
Copié
Copier
Copié
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;
Copier
Copié
Copier
Copié
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;
}
}
Copier
Copié
Copier
Copié
}
Copier
Copié
Copier
Copié
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;
Copier
Copié
Copier
Copié
auto
R
eport = llvm::make_unique<BugReport>(*BT
_destroylock
, Message, N);
auto
r
eport = llvm::make_unique<BugReport>(*BT
, Message, N);
R
eport->addRange(
CE->getArg(0)
->getSourceRange());
r
eport->addRange(
E
->getSourceRange());
C.emitReport(std::move(
R
eport));
C.emitReport(std::move(
r
eport));
}
}
Copier
Copié
Copier
Copié
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;
Copier
Copié
Copier
Copié
const MemRegion *LockR = Lock.getAsRegion();
if (CE->getNumArgs() != 1 && CE->getNumArgs() != 2)
if (!LockR)
return;
return;
Copier
Copié
Copier
Copié
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;
Copier
Copié
Copier
Copié
const struct LockState *
LState =
S
tate->
get<LockMap>(
LockR);
SVal
LState =
s
tate->
getSVal(PthreadLockMutexStateTrait,
LockR);
if (!LState
|| LState
->is
Destroyed
()) {
if (!LState
.isConstant()
|| LState
.isConstant(
Destroyed
))
State =
S
tate->
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 L
State =
s
tate->
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;
}
}
Copier
Copié
Copier
Copié
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;
Copier
Copié
Copier
Copié
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;
}
}
Copier
Copié
Copier
Copié
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)
);
}
Copier
Copié
Copier
Copié
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;
Copier
Copié
Copier
Copié
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));
}
}
Copier
Copié
Copier
Copié
void ento::registerPthreadLockChecker
(CheckerManager &mgr) {
void ento::registerPthreadLockChecker
V2
(CheckerManager &mgr) {
mgr.registerChecker<PthreadLockMutexStateModel>();
mgr.registerChecker<PthreadLockStackModel>();
mgr.registerChecker<PthreadLockChecker>();
mgr.registerChecker<PthreadLockChecker>();
}
}
Différences enregistrées
Texte d'origine
Ouvrir un fichier
//===--- PthreadLockChecker.cpp - Check for locking problems ---*- C++ -*--===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This defines PthreadLockChecker, a simple lock -> unlock checker. // Also handles XNU locks, which behave similarly enough to share code. // //===----------------------------------------------------------------------===// #include "ClangSACheckers.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" #include "llvm/ADT/ImmutableList.h" using namespace clang; using namespace ento; namespace { struct LockState { enum Kind { Destroyed, Locked, Unlocked } K; private: LockState(Kind K) : K(K) {} public: static LockState getLocked() { return LockState(Locked); } static LockState getUnlocked() { return LockState(Unlocked); } static LockState getDestroyed() { return LockState(Destroyed); } bool operator==(const LockState &X) const { return K == X.K; } bool isLocked() const { return K == Locked; } bool isUnlocked() const { return K == Unlocked; } bool isDestroyed() const { return K == Destroyed; } void Profile(llvm::FoldingSetNodeID &ID) const { ID.AddInteger(K); } }; class PthreadLockChecker : public Checker< check::PostStmt<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; enum LockingSemantics { NotApplicable = 0, PthreadSemantics, XNUSemantics }; public: 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 // GDM Entry for tracking lock state. REGISTER_LIST_WITH_PROGRAMSTATE(LockSet, const MemRegion *) REGISTER_MAP_WITH_PROGRAMSTATE(LockMap, const MemRegion *, LockState) void PthreadLockChecker::checkPostStmt(const CallExpr *CE, CheckerContext &C) const { ProgramStateRef state = C.getState(); const LocationContext *LCtx = C.getLocationContext(); StringRef FName = C.getCalleeName(CE); if (FName.empty()) return; if (CE->getNumArgs() != 1 && CE->getNumArgs() != 2) return; if (FName == "pthread_mutex_lock" || FName == "pthread_rwlock_rdlock" || FName == "pthread_rwlock_wrlock") AcquireLock(C, CE, state->getSVal(CE->getArg(0), LCtx), false, PthreadSemantics); else if (FName == "lck_mtx_lock" || 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()); if (X.isUnknownOrUndef()) return; 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; 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) { // 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. lockSucc = lockSucc->add<LockSet>(lockR); lockSucc = lockSucc->set<LockMap>(lockR, LockState::getLocked()); C.addTransition(lockSucc); } void PthreadLockChecker::ReleaseLock(CheckerContext &C, const CallExpr *CE, SVal lock) const { const MemRegion *lockR = lock.getAsRegion(); if (!lockR) 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>(); // 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. state = state->set<LockSet>(LS.getTail()); } 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(); if (!LockR) return; ProgramStateRef State = C.getState(); const LockState *LState = State->get<LockMap>(LockR); if (!LState || LState->isUnlocked()) { State = State->set<LockMap>(LockR, LockState::getDestroyed()); C.addTransition(State); return; } StringRef Message; if (LState->isLocked()) { Message = "This lock is still locked"; } else { Message = "This lock has already been destroyed"; } if (!BT_destroylock) BT_destroylock.reset(new BugType(this, "Destroy invalid lock", "Lock checker")); ExplodedNode *N = C.generateErrorNode(); if (!N) return; auto Report = llvm::make_unique<BugReport>(*BT_destroylock, Message, N); Report->addRange(CE->getArg(0)->getSourceRange()); C.emitReport(std::move(Report)); } void PthreadLockChecker::InitLock(CheckerContext &C, const CallExpr *CE, SVal Lock) const { const MemRegion *LockR = Lock.getAsRegion(); if (!LockR) return; ProgramStateRef State = C.getState(); const struct LockState *LState = State->get<LockMap>(LockR); if (!LState || LState->isDestroyed()) { State = State->set<LockMap>(LockR, LockState::getUnlocked()); C.addTransition(State); return; } StringRef Message; if (LState->isLocked()) { Message = "This lock is still being held"; } else { Message = "This lock has already been initialized"; } if (!BT_initlock) BT_initlock.reset(new BugType(this, "Init invalid lock", "Lock checker")); ExplodedNode *N = C.generateErrorNode(); if (!N) 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, 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; 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) { mgr.registerChecker<PthreadLockChecker>(); }
Texte modifié
Ouvrir un fichier
//===--- PthreadLockChecker.cpp - Check for locking problems ---*- C++ -*--===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This defines PthreadLockChecker, a simple lock -> unlock checker. // Also handles XNU locks, which behave similarly enough to share code. // //===----------------------------------------------------------------------===// #include "ClangSACheckers.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/Checker.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/ProgramStateTrait.h" #include "llvm/ADT/ImmutableList.h" using namespace clang; using namespace ento; namespace { enum PthreadMutexLockState { Destroyed, Locked, Unlocked }; enum LockingSemantics { NotApplicable = 0, PthreadSemantics, XNUSemantics }; SmartStateTrait PthreadLockMutexStateTrait; SmartStateTrait PthreadLockStackPointerTrait; struct InitLockSpec { unsigned ArgNo; }; llvm::StringMap<InitLockSpec> InitLockFunctions = { { "pthread_mutex_init" , { 0 } } }; struct AcquireSpec { unsigned ArgNo; bool IsTrylock; LockingSemantics Semantics; }; llvm::StringMap<AcquireSpec> AcquireLockFunctions = { { "pthread_mutex_lock", { 0, false, PthreadSemantics } }, { "pthread_rwlock_rdlock", { 0, false, PthreadSemantics } }, { "pthread_rwlock_wrlock", { 0, false, PthreadSemantics } }, { "lck_mtx_lock", { 0, false, XNUSemantics } }, { "lck_rw_lock_exclusive", { 0, false, XNUSemantics } }, { "lck_rw_lock_shared", { 0, false, XNUSemantics } }, { "pthread_mutex_trylock", { 0, true, PthreadSemantics } }, { "pthread_rwlock_tryrdlock", { 0, true, PthreadSemantics } }, { "pthread_rwlock_trywrlock", { 0, true, PthreadSemantics } }, { "lck_mtx_try_lock", { 0, true, XNUSemantics } }, { "lck_rw_try_lock_exclusive", { 0, true, XNUSemantics } }, { "lck_rw_try_lock_shared", { 0, true, XNUSemantics } } }; struct ReleaseLockSpec { unsigned ArgNo; }; llvm::StringMap<ReleaseLockSpec> ReleaseLockFunctions = { { "pthread_mutex_unlock", { 0 } }, { "pthread_rwlock_unlock", { 0 } }, { "lck_mtx_unlock", { 0 } }, { "lck_rw_done", { 0 } } }; 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: void checkASTDecl(const TranslationUnitDecl *D, AnalysisManager &AMgr, BugReporter &BR) const; void checkPostStmt(const CallExpr *CE, CheckerContext &C) const; }; } // end anonymous namespace void PthreadLockMutexStateModel::checkASTDecl(const TranslationUnitDecl *D, AnalysisManager &AMgr, BugReporter &BR) const { PthreadLockMutexStateTrait.initialize("PthreadLockMutexState", AMgr.getASTContext().IntTy); } void PthreadLockMutexStateModel::checkPostStmt(const CallExpr *CE, CheckerContext &C) const { ProgramStateRef state = C.getState(); StringRef FName = C.getCalleeName(CE); if (FName.empty()) return; if (CE->getNumArgs() != 1 && CE->getNumArgs() != 2) return; auto InitI = InitLockFunctions.find(FName); if (InitI != InitLockFunctions.end()) { const auto &Spec = InitI->second; const MemRegion *LockR = C.getSVal(CE->getArg(Spec.ArgNo)).getAsRegion(); if (!LockR) return; SVal OldLState = state->getSVal(PthreadLockMutexStateTrait, LockR); if (OldLState.isConstant(Locked) || OldLState.isConstant(Unlocked)) state = state->bindLoc(PthreadLockMutexStateTrait, LockR, UndefinedVal()); else state = state->bindLoc(PthreadLockMutexStateTrait, LockR, Unlocked); C.addTransition(state); return; } auto AcquireI = AcquireLockFunctions.find(FName); if (AcquireI != AcquireLockFunctions.end()) { const auto &Spec = AcquireI->second; const MemRegion *LockR = C.getSVal(CE->getArg(Spec.ArgNo)).getAsRegion(); if (!LockR) return; SVal X = C.getSVal(CE); if (X.isUnknownOrUndef()) return; DefinedSVal retVal = X.castAs<DefinedSVal>(); ProgramStateRef lockSucc = state; 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; } 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 OldLState = state->getSVal(PthreadLockMutexStateTrait, LockR); if (OldLState.isConstant(Destroyed)) state = state->bindLoc(PthreadLockMutexStateTrait, LockR, UndefinedVal()); else state = state->bindLoc(PthreadLockMutexStateTrait, LockR, Unlocked); C.addTransition(state); return; } auto DestroyI = DestroyLockFunctions.find(FName); if (DestroyI != DestroyLockFunctions.end()) { const auto &Spec = DestroyI->second; const MemRegion *LockR = C.getSVal(CE->getArg(Spec.ArgNo)).getAsRegion(); if (!LockR) return; state = state->bindLoc(PthreadLockMutexStateTrait, LockR, Destroyed); C.addTransition(state); return; } } 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 PthreadLockStackModel::checkPostStmt(const CallExpr *CE, CheckerContext &C) const { ProgramStateRef state = C.getState(); StringRef FName = C.getCalleeName(CE); if (FName.empty()) return; if (CE->getNumArgs() != 1 && CE->getNumArgs() != 2) return; 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); return; } 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; // Record that the lock was released. 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; } } namespace { 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(); if (!N) return; auto report = llvm::make_unique<BugReport>(*BT, Message, N); report->addRange(E->getSourceRange()); C.emitReport(std::move(report)); } void PthreadLockChecker::checkPreStmt(const CallExpr *CE, CheckerContext &C) const { ProgramStateRef state = C.getState(); StringRef FName = C.getCalleeName(CE); if (FName.empty()) return; 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) return; SVal LState = state->getSVal(PthreadLockMutexStateTrait, LockR); if (!LState.isConstant() || LState.isConstant(Destroyed)) return; 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; } 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; 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; } 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"; } reportBug(BT_destroylock, LockE, C, "Destroy invalid lock", Message); return; } } void ento::registerPthreadLockCheckerV2(CheckerManager &mgr) { mgr.registerChecker<PthreadLockMutexStateModel>(); mgr.registerChecker<PthreadLockStackModel>(); mgr.registerChecker<PthreadLockChecker>(); }
Trouver la différence