Diff
checker
テキスト
テキスト
画像
ドキュメント
Excel
フォルダ
Legal
Enterprise
デスクトップ
料金
ログイン
Diffchecker デスクトップのダウンロード
テキスト比較
2 つのテキスト ファイルの違いを見つける
ツール
履歴
ライブエディター
未変更行を折りたたむ
折り返しなし
レイアウト
分割
統合
比較精度
スマート
単語
文字
シンタックスハイライト
構文を選択
無視
テキスト変換
最初の差分へ移動
入力を編集
Diffchecker Desktop
Diffcheckerを実行する最も安全な方法。Diffchecker Desktopアプリを入手:あなたの差分はコンピューターから出ることはありません!
Desktopを入手
Untitled diff
作成日
10 年前
差分は期限切れになりません
クリア
エクスポート
共有
説明
253 削除
行
合計
削除
文字
合計
削除
この機能を引き続き使用するには、アップグレードしてください
Diff
checker
Pro
価格を見る
335 行
すべてコピー
377 追加
行
合計
追加
文字
合計
追加
この機能を引き続き使用するには、アップグレードしてください
Diff
checker
Pro
価格を見る
432 行
すべてコピー
//===--- PthreadLockChecker.cpp - Check for locking problems ---*- C++ -*--===//
//===--- PthreadLockChecker.cpp - Check for locking problems ---*- C++ -*--===//
//
//
// The LLVM Compiler Infrastructure
// The LLVM Compiler Infrastructure
//
//
// This file is distributed under the University of Illinois Open Source
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
// License. See LICENSE.TXT for details.
//
//
//===----------------------------------------------------------------------===//
//===----------------------------------------------------------------------===//
//
//
// This defines PthreadLockChecker, a simple lock -> unlock checker.
// This defines PthreadLockChecker, a simple lock -> unlock checker.
// Also handles XNU locks, which behave similarly enough to share code.
// Also handles XNU locks, which behave similarly enough to share code.
//
//
//===----------------------------------------------------------------------===//
//===----------------------------------------------------------------------===//
#include "ClangSACheckers.h"
#include "ClangSACheckers.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
コピー
コピー済み
コピー
コピー済み
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
#include "llvm/ADT/ImmutableList.h"
#include "llvm/ADT/ImmutableList.h"
using namespace clang;
using namespace clang;
using namespace ento;
using namespace ento;
namespace {
namespace {
コピー
コピー済み
コピー
コピー済み
enum PthreadMutexLockState { Destroyed, Locked, Unlocked };
enum LockingSemantics { NotApplicable = 0, PthreadSemantics, XNUSemantics };
コピー
コピー済み
コピー
コピー済み
struct LockState {
SmartStateTrait PthreadLockMutexStateTrait;
enum Kind { Destroyed, Locked, Unlocked } K;
SmartStateTrait PthreadLockStackPointerTrait;
コピー
コピー済み
コピー
コピー済み
private:
struct InitLockSpec {
LockState(Kind K) : K(K) {}
unsigned ArgNo;
};
llvm::StringMap<InitLockSpec> InitLockFunctions = {
{ "pthread_mutex_init" , { 0 } }
};
コピー
コピー済み
コピー
コピー済み
public:
struct AcquireSpec {
static LockState getLocked() { return LockState(Locked); }
unsigned ArgNo;
static LockState getUnlocked() { return LockState(Unlocked); }
bool IsTrylock;
static LockState getDestroyed() { return LockState(Destroyed); }
LockingSemantics Semantics;
};
llvm::StringMap<AcquireSpec> AcquireLockFunctions = {
{ "pthread_mutex_lock", { 0, false, PthreadSemantics } },
{ "pthread_rwlock_rdlock", { 0, false, PthreadSemantics } },
{ "pthread_rwlock_wrlock", { 0, false, PthreadSemantics } },
コピー
コピー済み
コピー
コピー済み
bool operator==(const LockState &X) const {
{ "lck_mtx_lock", { 0, false, XNUSemantics } },
return K == X.K;
{ "lck_rw_lock_exclusive", { 0, false, XNUSemantics } },
}
{ "lck_rw_lock_shared", { 0, false, XNUSemantics } },
コピー
コピー済み
コピー
コピー済み
bool isLocked() const
{
return K == Locked; }
{ "pthread_mutex_trylock",
{
0, true, PthreadSemantics } },
bool isUnlocked() const { return K == Unlocked; }
{ "pthread_rwlock_tryrdlock", { 0, true, PthreadSemantics } },
bool isDestroyed() const
{
return K == Destroyed; }
{ "pthread_rwlock_trywrlock",
{
0, true, PthreadSemantics } },
コピー
コピー済み
コピー
コピー済み
void Profile(llvm::FoldingSetNodeID &ID) const {
{ "lck_mtx_try_lock", { 0, true, XNUSemantics } },
ID.AddInteger(K);
{ "lck_rw_try_lock_exclusive", { 0, true, XNUSemantics } },
}
{ "lck_rw_try_lock_shared", { 0, true, XNUSemantics } }
};
};
コピー
コピー済み
コピー
コピー済み
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:
コピー
コピー済み
コピー
コピー済み
void checkASTDecl(const TranslationUnitDecl *D, AnalysisManager &AMgr,
BugReporter &BR) const;
void checkPostStmt(const CallExpr *CE, CheckerContext &C) const;
void checkPostStmt(const CallExpr *CE, CheckerContext &C) const;
コピー
コピー済み
コピー
コピー済み
void AcquireLock(CheckerContext &C, const CallExpr *CE, SVal lock,
bool isTryLock, enum LockingSemantics semantics) const;
void ReleaseLock(CheckerContext &C, const CallExpr *CE, SVal lock) const;
void DestroyLock(CheckerContext &C, const CallExpr *CE, SVal Lock) const;
void InitLock(CheckerContext &C, const CallExpr *CE, SVal Lock) const;
void reportUseDestroyedBug(CheckerContext &C, const CallExpr *CE) const;
};
};
} // end anonymous namespace
} // end anonymous namespace
コピー
コピー済み
コピー
コピー済み
// GDM Entry for tracking lock state.
void PthreadLockMutexStateModel::checkASTDecl(const TranslationUnitDecl *D,
REGISTER_LIST_WITH_PROGRAMSTATE(LockSet, const MemRegion *)
AnalysisManager &AMgr,
BugReporter &BR) const {
REGISTER_MAP_WITH_PROGRAMSTATE(LockMap, const MemRegion *, LockState)
PthreadLockMutexStateTrait.initialize("PthreadLockMutexState",
AMgr.getASTContext().IntTy);
}
コピー
コピー済み
コピー
コピー済み
void
PthreadLockChecker
::checkPostStmt(const CallExpr *CE,
void
PthreadLockMutexStateModel
::checkPostStmt(const CallExpr *CE,
CheckerContext &C) const {
CheckerContext &C) const {
ProgramStateRef state = C.getState();
ProgramStateRef state = C.getState();
コピー
コピー済み
コピー
コピー済み
const LocationContext *LCtx = C.getLocationContext();
StringRef FName = C.getCalleeName(CE);
StringRef FName = C.getCalleeName(CE);
if (FName.empty())
if (FName.empty())
return;
return;
if (CE->getNumArgs() != 1 && CE->getNumArgs() != 2)
if (CE->getNumArgs() != 1 && CE->getNumArgs() != 2)
return;
return;
コピー
コピー済み
コピー
コピー済み
if (FName == "pthread_mutex_lock" ||
auto InitI = InitLockFunctions.find(FName);
FName == "pthread_rwlock_rdlock" ||
if (InitI != InitLockFunctions.end()) {
FName == "pthread_rwlock_wrlock")
const auto &Spec = InitI->second;
AcquireLock(C, CE, state->getSVal(CE->getArg(0), LCtx),
const MemRegion *LockR = C.getSVal(CE->getArg(Spec.ArgNo))
.getAsRegion();
false, PthreadSemantics);
if (!
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();
コピー
コピー済み
コピー
コピー済み
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;
コピー
コピー済み
コピー
コピー済み
}
DefinedSVal retVal = X.castAs<DefinedSVal>();
コピー
コピー済み
コピー
コピー済み
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;
コピー
コピー済み
コピー
コピー済み
} else if (LState->isDestroyed()) {
reportUseDestroyedBug(C, CE);
SVal X = C.getSVal(CE);
if (X.isUnknownOrUndef())
return;
return;
コピー
コピー済み
コピー
コピー済み
}
DefinedSVal retVal = X.castAs<DefinedSVal>();
}
コピー
コピー済み
コピー
コピー済み
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;
}
}
コピー
コピー済み
コピー
コピー済み
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;
}
}
コピー
コピー済み
コピー
コピー済み
// 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 {
コピー
コピー済み
コピー
コピー済み
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;
コピー
コピー済み
コピー
コピー済み
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>();
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;
コピー
コピー済み
コピー
コピー済み
}
// Record that the lock was released.
state = state->bindLoc(PthreadLockMutexStateTrait, LockR, Destroyed);
state = state->set<LockSet>(LS.getTail());
C.addTransition(state);
return;
}
}
コピー
コピー済み
コピー
コピー済み
}
コピー
コピー済み
コピー
コピー済み
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));
}
}
コピー
コピー済み
コピー
コピー済み
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;
コピー
コピー済み
コピー
コピー済み
const MemRegion *LockR = Lock.getAsRegion();
if (CE->getNumArgs() != 1 && CE->getNumArgs() != 2)
if (!LockR)
return;
return;
コピー
コピー済み
コピー
コピー済み
ProgramStateRef State = C.getState(
);
auto AcquireI = AcquireLockFunctions.find(FName);
if (AcquireI != AcquireLockFunctions.end()) {
const auto &Spec = AcquireI->second;
SVal LockR = C.getSVal(CE->getArg(Spec.ArgNo)
);
コピー
コピー済み
コピー
コピー済み
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;
}
}
コピー
コピー済み
コピー
コピー済み
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;
コピー
コピー済み
コピー
コピー済み
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;
}
}
コピー
コピー済み
コピー
コピー済み
}
コピー
コピー済み
コピー
コピー済み
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;
コピー
コピー済み
コピー
コピー済み
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));
}
}
コピー
コピー済み
コピー
コピー済み
void PthreadLockChecker::
InitLock(CheckerContext &C,
const CallExpr *CE,
void PthreadLockChecker::
checkPreStmt(
const CallExpr *CE,
SVal Lock
) const {
CheckerContext &C
) const {
ProgramStateRef state = C.getState();
StringRef FName = C.getCalleeName(CE);
if (FName.empty())
return;
コピー
コピー済み
コピー
コピー済み
const MemRegion *LockR = Lock.getAsRegion();
if (CE->getNumArgs() != 1 && CE->getNumArgs() != 2)
if (!LockR)
return;
return;
コピー
コピー済み
コピー
コピー済み
ProgramStateRef State = C.getState
();
auto InitI = InitLockFunctions.find(FName);
if (InitI != InitLockFunctions.end()) {
const auto &Spec = InitI->second;
const Expr *LockE = CE->getArg(Spec.ArgNo);
const MemRegion *LockR = C.getSVal(LockE).getAsRegion
();
if (!LockR)
return;
コピー
コピー済み
コピー
コピー済み
const struct LockState *
LState =
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;
}
}
コピー
コピー済み
コピー
コピー済み
StringRef Message;
auto ReleaseI = ReleaseLockFunctions.find(FName);
if (ReleaseI != ReleaseLockFunctions.end()) {
const auto &Spec = ReleaseI->second;
const Expr *LockE = CE->getArg(Spec.ArgNo);
const MemRegion *LockR = C.getSVal(LockE).getAsRegion();
if (!LockR)
return;
コピー
コピー済み
コピー
コピー済み
if (LState
->isLocked(
)) {
SVal LState = state->getSVal(PthreadLockMutexStateTrait, LockR);
Message = "This lock is still being held";
if (LState
.isConstant(Unlocked
)) {
} else {
reportBug(BT_doubleunlock, LockE, C, "Double unlocking",
Message =
"This lock has already been
initialized";
"This lock has already been unlocked");
return;
}
if (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;
}
}
コピー
コピー済み
コピー
コピー済み
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)
);
}
コピー
コピー済み
コピー
コピー済み
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;
コピー
コピー済み
コピー
コピー済み
auto Report = llvm::make_unique<BugReport>(
}
*BT_destroylock, "This lock has already been destroyed", N);
Report->addRange(CE->getArg(0)->getSourceRange());
C.emitReport(std::move(Report));
}
}
コピー
コピー済み
コピー
コピー済み
void ento::registerPthreadLockChecker
(CheckerManager &mgr) {
void ento::registerPthreadLockChecker
V2
(CheckerManager &mgr) {
mgr.registerChecker<PthreadLockMutexStateModel>();
mgr.registerChecker<PthreadLockStackModel>();
mgr.registerChecker<PthreadLockChecker>();
mgr.registerChecker<PthreadLockChecker>();
}
}
保存された差分
原文
ファイルを開く
//===--- 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>(); }
変更されたテキスト
ファイルを開く
//===--- 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>(); }
違いを見つける