Diff
checker
Text
Text
Bilder
Dokumente
Excel
Ordner
Legal
Enterprise
Desktop-App
Preise
Einloggen
Diffchecker Desktop herunterladen
Texte vergleichen
Finde den Unterschied zwischen zwei Textdateien
Werkzeuge
Verlauf
Live-Editor
Gleiches ausblenden
Zeilenumbruch aus
Ansicht
Zweispaltig
Einspaltig
Vergleichsgenauigkeit
Intelligent
Wort
Zeichen
Syntaxhervorhebung
Syntax auswählen
Ignorieren
Text umwandeln
Zur ersten Änderung
Eingabe bearbeiten
Diffchecker Desktop
Der sicherste Weg, Diffchecker zu nutzen. Hol dir die Desktop-App: Deine Diffs verlassen nie deinen Computer!
Desktop holen
Untitled diff
Erstellt
vor 10 Jahren
Diff läuft nie ab
Löschen
Exportieren
Teilen
Erklären
253 Entfernungen
Zeilen
Gesamt
Entfernt
Zeichen
Gesamt
Entfernt
Um diese Funktion weiterhin zu nutzen, aktualisiere auf
Diff
checker
Pro
Preise anzeigen
335 Zeilen
Kopieren
377 Hinzufügungen
Zeilen
Gesamt
Hinzugefügt
Zeichen
Gesamt
Hinzugefügt
Um diese Funktion weiterhin zu nutzen, aktualisiere auf
Diff
checker
Pro
Preise anzeigen
432 Zeilen
Kopieren
//===--- 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"
Kopieren
Kopiert
Kopieren
Kopiert
#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 {
Kopieren
Kopiert
Kopieren
Kopiert
enum PthreadMutexLockState { Destroyed, Locked, Unlocked };
enum LockingSemantics { NotApplicable = 0, PthreadSemantics, XNUSemantics };
Kopieren
Kopiert
Kopieren
Kopiert
struct LockState {
SmartStateTrait PthreadLockMutexStateTrait;
enum Kind { Destroyed, Locked, Unlocked } K;
SmartStateTrait PthreadLockStackPointerTrait;
Kopieren
Kopiert
Kopieren
Kopiert
private:
struct InitLockSpec {
LockState(Kind K) : K(K) {}
unsigned ArgNo;
};
llvm::StringMap<InitLockSpec> InitLockFunctions = {
{ "pthread_mutex_init" , { 0 } }
};
Kopieren
Kopiert
Kopieren
Kopiert
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 } },
Kopieren
Kopiert
Kopieren
Kopiert
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 } },
Kopieren
Kopiert
Kopieren
Kopiert
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 } },
Kopieren
Kopiert
Kopieren
Kopiert
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 } }
};
};
Kopieren
Kopiert
Kopieren
Kopiert
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:
Kopieren
Kopiert
Kopieren
Kopiert
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;
Kopieren
Kopiert
Kopieren
Kopiert
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
Kopieren
Kopiert
Kopieren
Kopiert
// 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);
}
Kopieren
Kopiert
Kopieren
Kopiert
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();
Kopieren
Kopiert
Kopieren
Kopiert
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;
Kopieren
Kopiert
Kopieren
Kopiert
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();
Kopieren
Kopiert
Kopieren
Kopiert
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;
Kopieren
Kopiert
Kopieren
Kopiert
}
DefinedSVal retVal = X.castAs<DefinedSVal>();
Kopieren
Kopiert
Kopieren
Kopiert
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;
Kopieren
Kopiert
Kopieren
Kopiert
} else if (LState->isDestroyed()) {
reportUseDestroyedBug(C, CE);
SVal X = C.getSVal(CE);
if (X.isUnknownOrUndef())
return;
return;
Kopieren
Kopiert
Kopieren
Kopiert
}
DefinedSVal retVal = X.castAs<DefinedSVal>();
}
Kopieren
Kopiert
Kopieren
Kopiert
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;
}
}
Kopieren
Kopiert
Kopieren
Kopiert
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;
}
}
Kopieren
Kopiert
Kopieren
Kopiert
// 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 {
Kopieren
Kopiert
Kopieren
Kopiert
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;
Kopieren
Kopiert
Kopieren
Kopiert
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;
}
}
}
Kopieren
Kopiert
Kopieren
Kopiert
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;
Kopieren
Kopiert
Kopieren
Kopiert
}
// Record that the lock was released.
state = state->bindLoc(PthreadLockMutexStateTrait, LockR, Destroyed);
state = state->set<LockSet>(LS.getTail());
C.addTransition(state);
return;
}
}
Kopieren
Kopiert
Kopieren
Kopiert
}
Kopieren
Kopiert
Kopieren
Kopiert
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));
}
}
Kopieren
Kopiert
Kopieren
Kopiert
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;
Kopieren
Kopiert
Kopieren
Kopiert
const MemRegion *LockR = Lock.getAsRegion();
if (CE->getNumArgs() != 1 && CE->getNumArgs() != 2)
if (!LockR)
return;
return;
Kopieren
Kopiert
Kopieren
Kopiert
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)
);
Kopieren
Kopiert
Kopieren
Kopiert
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;
}
}
Kopieren
Kopiert
Kopieren
Kopiert
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;
Kopieren
Kopiert
Kopieren
Kopiert
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;
}
}
Kopieren
Kopiert
Kopieren
Kopiert
}
Kopieren
Kopiert
Kopieren
Kopiert
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;
Kopieren
Kopiert
Kopieren
Kopiert
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));
}
}
Kopieren
Kopiert
Kopieren
Kopiert
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;
Kopieren
Kopiert
Kopieren
Kopiert
const MemRegion *LockR = Lock.getAsRegion();
if (CE->getNumArgs() != 1 && CE->getNumArgs() != 2)
if (!LockR)
return;
return;
Kopieren
Kopiert
Kopieren
Kopiert
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;
Kopieren
Kopiert
Kopieren
Kopiert
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;
}
}
Kopieren
Kopiert
Kopieren
Kopiert
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;
Kopieren
Kopiert
Kopieren
Kopiert
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;
}
}
Kopieren
Kopiert
Kopieren
Kopiert
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)
);
}
Kopieren
Kopiert
Kopieren
Kopiert
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;
Kopieren
Kopiert
Kopieren
Kopiert
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));
}
}
Kopieren
Kopiert
Kopieren
Kopiert
void ento::registerPthreadLockChecker
(CheckerManager &mgr) {
void ento::registerPthreadLockChecker
V2
(CheckerManager &mgr) {
mgr.registerChecker<PthreadLockMutexStateModel>();
mgr.registerChecker<PthreadLockStackModel>();
mgr.registerChecker<PthreadLockChecker>();
mgr.registerChecker<PthreadLockChecker>();
}
}
Gespeicherte Diffs
Originaltext
Datei öffnen
//===--- 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>(); }
Bearbeitung
Datei öffnen
//===--- 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>(); }
Unterschied finden