Diff
checker
Testo
Testo
Immagini
Documenti
Excel
Cartelle
Legal
Enterprise
Applicazione per desktop
Prezzi
Accedi
Scarica Diffchecker Desktop
Confronta il testo
Trova la differenza tra due file di testo
Strumenti
Cronologia
Visualizzazione
Layout
Diviso
Unificato
Editor live
Nascondi spazi bianchi
Comprimi invariate
Senza a capo
Livello di dettaglio
Intelligente
Sintassi
Nessuno
Modifica aspetto
Elaborazione
Ignora
Trasforma testo
Vai alla prima modifica
Modifica input
Diffchecker Desktop
Il modo più sicuro per usare Diffchecker. Ottieni l'app Diffchecker Desktop: i tuoi diff non lasciano mai il tuo computer!
Ottieni Desktop
Untitled diff
Creato
10 anni fa
Il diff non scade mai
Eliminare
Esporta
Condividere
Spiegare
243 rimozioni
Linee
Totale
Rimosso
Caratteri
Totale
Rimosso
Per continuare a utilizzare questa funzione, aggiorna a
Diff
checker
Pro
Visualizza prezzi
335 linee
Copia tutti
377 aggiunte
Linee
Totale
Aggiunto
Caratteri
Totale
Aggiunto
Per continuare a utilizzare questa funzione, aggiorna a
Diff
checker
Pro
Visualizza prezzi
432 linee
Copia tutti
//===--- 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"
Copia
Copiato
Copia
Copiato
#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 {
Copia
Copiato
Copia
Copiato
enum PthreadMutexLockState { Destroyed, Locked, Unlocked };
enum LockingSemantics { NotApplicable = 0, PthreadSemantics, XNUSemantics };
Copia
Copiato
Copia
Copiato
struct LockState {
SmartStateTrait PthreadLockMutexStateTrait;
enum Kind { Destroyed, Locked, Unlocked } K;
SmartStateTrait PthreadLockStackPointerTrait;
Copia
Copiato
Copia
Copiato
private:
struct InitLockSpec {
LockState(Kind K) : K(K) {}
unsigned ArgNo;
};
llvm::StringMap<InitLockSpec> InitLockFunctions = {
{ "pthread_mutex_init" , { 0 } }
};
Copia
Copiato
Copia
Copiato
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 } },
Copia
Copiato
Copia
Copiato
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 } },
Copia
Copiato
Copia
Copiato
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 } },
Copia
Copiato
Copia
Copiato
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 } }
};
};
Copia
Copiato
Copia
Copiato
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 } },
};
};
Copia
Copiato
Copia
Copiato
} // end anonymous namespace
namespace {
class PthreadLockMutexStateModel
: public Checker<check::ASTDecl<TranslationUnitDecl>,
check::PostStmt<CallExpr>> {
public:
public:
Copia
Copiato
Copia
Copiato
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;
Copia
Copiato
Copia
Copiato
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 salvati
Testo originale
Apri file
//===--- 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>(); }
Testo modificato
Apri file
//===--- 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>(); }
Trovare la differenza