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
Affichage
Vue
Divisé
Unifié
Éditeur live
Masquer les espaces
Cacher identiques
Sans retour à la ligne
Niveau de précision
Intelligent
Syntaxe
Aucun
Modifier l’apparence
Traitement
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
243 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 } },
};
};
Copier
Copié
Copier
Copié
} // 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;
};
};
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