[analyzer] Migrate ChrootChecker to CheckerV2.

llvm-svn: 126324
This commit is contained in:
Argyrios Kyrtzidis 2011-02-23 19:38:39 +00:00
parent 5d2144602f
commit da02a25ae6
5 changed files with 148 additions and 29 deletions

View File

@ -23,6 +23,7 @@
namespace clang {
class Decl;
class Stmt;
class CallExpr;
namespace ento {
class ExprEngine;
@ -31,10 +32,17 @@ namespace ento {
class CheckerContext;
class ObjCMessage;
class SVal;
class ExplodedNode;
class ExplodedNodeSet;
class ExplodedGraph;
class GRState;
class GraphExpander {
public:
virtual ~GraphExpander();
virtual void expandGraph(ExplodedNodeSet &Dst, ExplodedNode *Pred) = 0;
};
struct VoidCheckerFnParm {};
template <typename P1=VoidCheckerFnParm, typename P2=VoidCheckerFnParm,
typename P3=VoidCheckerFnParm, typename P4=VoidCheckerFnParm>
@ -177,6 +185,12 @@ public:
void runCheckersForEndAnalysis(ExplodedGraph &G, BugReporter &BR,
ExprEngine &Eng);
/// \brief Run checkers for evaluating a call.
void runCheckersForEvalCall(ExplodedNodeSet &Dst,
const ExplodedNodeSet &Src,
const CallExpr *CE, ExprEngine &Eng,
GraphExpander *defaultEval = 0);
// FIXME: Temporary until checker running is moved completely into
// CheckerManager.
void registerCheckersToEngine(ExprEngine &eng);
@ -220,6 +234,19 @@ public:
void _registerForEndAnalysis(CheckEndAnalysisFunc checkfn);
class EvalCallFunc {
typedef bool (*Func)(void *, const CallExpr *, CheckerContext &);
Func Fn;
public:
void *Checker;
EvalCallFunc(void *checker, Func fn) : Fn(fn), Checker(checker) { }
bool operator()(const CallExpr *CE, CheckerContext &C) {
return Fn(Checker, CE, C);
}
};
void _registerForEvalCall(EvalCallFunc checkfn);
//===----------------------------------------------------------------------===//
// Implementation details.
//===----------------------------------------------------------------------===//
@ -287,6 +314,8 @@ private:
std::vector<CheckLocationFunc> LocationCheckers;
std::vector<CheckEndAnalysisFunc> EndAnalysisCheckers;
std::vector<EvalCallFunc> EvalCallCheckers;
};
} // end ento namespace

View File

@ -162,6 +162,24 @@ public:
} // end check namespace
namespace eval {
class Call {
template <typename CHECKER>
static bool _evalCall(void *checker, const CallExpr *CE, CheckerContext &C) {
return ((const CHECKER *)checker)->evalCall(CE, C);
}
public:
template <typename CHECKER>
static void _register(CHECKER *checker, CheckerManager &mgr) {
mgr._registerForEvalCall(
CheckerManager::EvalCallFunc(checker, _evalCall<CHECKER>));
}
};
} // end eval namespace
template <typename CHECK1, typename CHECK2=check::_VoidCheck,
typename CHECK3=check::_VoidCheck, typename CHECK4=check::_VoidCheck,
typename CHECK5=check::_VoidCheck, typename CHECK6=check::_VoidCheck,

View File

@ -12,9 +12,10 @@
//===----------------------------------------------------------------------===//
#include "ClangSACheckers.h"
#include "clang/StaticAnalyzer/Core/CheckerV2.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerVisitor.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/GRState.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/GRStateTrait.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"
@ -37,38 +38,30 @@ bool isRootChanged(intptr_t k) { return k == ROOT_CHANGED; }
// ROOT_CHANGED<--chdir(..)-- JAIL_ENTERED<--chdir(..)--
// | |
// bug<--foo()-- JAIL_ENTERED<--foo()--
class ChrootChecker : public CheckerVisitor<ChrootChecker> {
IdentifierInfo *II_chroot, *II_chdir;
class ChrootChecker : public CheckerV2<eval::Call, check::PreStmt<CallExpr> > {
mutable IdentifierInfo *II_chroot, *II_chdir;
// This bug refers to possibly break out of a chroot() jail.
BuiltinBug *BT_BreakJail;
mutable llvm::OwningPtr<BuiltinBug> BT_BreakJail;
public:
ChrootChecker() : II_chroot(0), II_chdir(0), BT_BreakJail(0) {}
ChrootChecker() : II_chroot(0), II_chdir(0) {}
static void *getTag() {
static int x;
return &x;
}
virtual bool evalCallExpr(CheckerContext &C, const CallExpr *CE);
virtual void PreVisitCallExpr(CheckerContext &C, const CallExpr *CE);
bool evalCall(const CallExpr *CE, CheckerContext &C) const;
void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
private:
void Chroot(CheckerContext &C, const CallExpr *CE);
void Chdir(CheckerContext &C, const CallExpr *CE);
void Chroot(CheckerContext &C, const CallExpr *CE) const;
void Chdir(CheckerContext &C, const CallExpr *CE) const;
};
} // end anonymous namespace
static void RegisterChrootChecker(ExprEngine &Eng) {
Eng.registerCheck(new ChrootChecker());
}
void ento::registerChrootChecker(CheckerManager &mgr) {
mgr.addCheckerRegisterFunction(RegisterChrootChecker);
}
bool ChrootChecker::evalCallExpr(CheckerContext &C, const CallExpr *CE) {
bool ChrootChecker::evalCall(const CallExpr *CE, CheckerContext &C) const {
const GRState *state = C.getState();
const Expr *Callee = CE->getCallee();
SVal L = state->getSVal(Callee);
@ -94,7 +87,7 @@ bool ChrootChecker::evalCallExpr(CheckerContext &C, const CallExpr *CE) {
return false;
}
void ChrootChecker::Chroot(CheckerContext &C, const CallExpr *CE) {
void ChrootChecker::Chroot(CheckerContext &C, const CallExpr *CE) const {
const GRState *state = C.getState();
GRStateManager &Mgr = state->getStateManager();
@ -104,7 +97,7 @@ void ChrootChecker::Chroot(CheckerContext &C, const CallExpr *CE) {
C.addTransition(state);
}
void ChrootChecker::Chdir(CheckerContext &C, const CallExpr *CE) {
void ChrootChecker::Chdir(CheckerContext &C, const CallExpr *CE) const {
const GRState *state = C.getState();
GRStateManager &Mgr = state->getStateManager();
@ -131,7 +124,7 @@ void ChrootChecker::Chdir(CheckerContext &C, const CallExpr *CE) {
}
// Check the jail state before any function call except chroot and chdir().
void ChrootChecker::PreVisitCallExpr(CheckerContext &C, const CallExpr *CE) {
void ChrootChecker::checkPreStmt(const CallExpr *CE, CheckerContext &C) const {
const GRState *state = C.getState();
const Expr *Callee = CE->getCallee();
SVal L = state->getSVal(Callee);
@ -155,9 +148,9 @@ void ChrootChecker::PreVisitCallExpr(CheckerContext &C, const CallExpr *CE) {
if (isRootChanged((intptr_t) *k))
if (ExplodedNode *N = C.generateNode()) {
if (!BT_BreakJail)
BT_BreakJail = new BuiltinBug("Break out of jail",
BT_BreakJail.reset(new BuiltinBug("Break out of jail",
"No call of chdir(\"/\") immediately "
"after chroot");
"after chroot"));
BugReport *R = new BugReport(*BT_BreakJail,
BT_BreakJail->getDescription(), N);
C.EmitReport(R);
@ -165,3 +158,7 @@ void ChrootChecker::PreVisitCallExpr(CheckerContext &C, const CallExpr *CE) {
return;
}
void ento::registerChrootChecker(CheckerManager &mgr) {
mgr.registerChecker<ChrootChecker>();
}

View File

@ -238,11 +238,25 @@ bool ExprEngine::CheckerEvalCall(const CallExpr *CE,
DstTmp.clear();
}
if (evaluated)
if (evaluated) {
Dst.insert(DstTmp);
else
Dst.insert(Pred);
return evaluated;
}
class DefaultEval : public GraphExpander {
bool &Evaluated;
public:
DefaultEval(bool &evaluated) : Evaluated(evaluated) { }
virtual void expandGraph(ExplodedNodeSet &Dst, ExplodedNode *Pred) {
Evaluated = false;
Dst.insert(Pred);
}
};
evaluated = true;
DefaultEval defaultEval(evaluated);
getCheckerManager().runCheckersForEvalCall(Dst, Pred, CE, *this,
&defaultEval);
return evaluated;
}

View File

@ -66,7 +66,9 @@ static void runPathSensitiveCheckers(CHECK_CTX checkCtx,
ExplodedNodeSet &Dst,
ExplodedNodeSet &Src) {
if (checkCtx.Checkers.empty()) {
typename CHECK_CTX::CheckersTy::const_iterator
I = checkCtx.checkers_begin(), E = checkCtx.checkers_end();
if (I == E) {
Dst.insert(Src);
return;
}
@ -74,8 +76,7 @@ static void runPathSensitiveCheckers(CHECK_CTX checkCtx,
ExplodedNodeSet Tmp;
ExplodedNodeSet *PrevSet = &Src;
for (typename CHECK_CTX::CheckersTy::const_iterator
I= checkCtx.Checkers.begin(), E= checkCtx.Checkers.end(); I!=E; ++I) {
for (; I != E; ++I) {
ExplodedNodeSet *CurrSet = 0;
if (I+1 == E)
CurrSet = &Dst;
@ -101,6 +102,9 @@ namespace {
const Stmt *S;
ExprEngine &Eng;
CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); }
CheckersTy::const_iterator checkers_end() { return Checkers.end(); }
CheckStmtContext(bool isPreVisit, const CheckersTy &checkers,
const Stmt *s, ExprEngine &eng)
: IsPreVisit(isPreVisit), Checkers(checkers), S(s), Eng(eng) { }
@ -135,6 +139,9 @@ namespace {
const ObjCMessage &Msg;
ExprEngine &Eng;
CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); }
CheckersTy::const_iterator checkers_end() { return Checkers.end(); }
CheckObjCMessageContext(bool isPreVisit, const CheckersTy &checkers,
const ObjCMessage &msg, ExprEngine &eng)
: IsPreVisit(isPreVisit), Checkers(checkers), Msg(msg), Eng(eng) { }
@ -173,6 +180,9 @@ namespace {
const GRState *State;
ExprEngine &Eng;
CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); }
CheckersTy::const_iterator checkers_end() { return Checkers.end(); }
CheckLocationContext(const CheckersTy &checkers,
SVal loc, bool isLoad, const Stmt *s,
const GRState *state, ExprEngine &eng)
@ -207,6 +217,50 @@ void CheckerManager::runCheckersForEndAnalysis(ExplodedGraph &G,
EndAnalysisCheckers[i](G, BR, Eng);
}
/// \brief Run checkers for evaluating a call.
/// Only one checker will evaluate the call.
void CheckerManager::runCheckersForEvalCall(ExplodedNodeSet &Dst,
const ExplodedNodeSet &Src,
const CallExpr *CE,
ExprEngine &Eng,
GraphExpander *defaultEval) {
if (EvalCallCheckers.empty() && defaultEval == 0) {
Dst.insert(Src);
return;
}
for (ExplodedNodeSet::iterator
NI = Src.begin(), NE = Src.end(); NI != NE; ++NI) {
ExplodedNode *Pred = *NI;
bool anyEvaluated = false;
for (std::vector<EvalCallFunc>::iterator
EI = EvalCallCheckers.begin(), EE = EvalCallCheckers.end();
EI != EE; ++EI) {
ExplodedNodeSet checkDst;
CheckerContext C(checkDst, Eng.getBuilder(), Eng, Pred, EI->Checker,
ProgramPoint::PostStmtKind, 0, CE);
bool evaluated = (*EI)(CE, C);
assert(!(evaluated && anyEvaluated)
&& "There are more than one checkers evaluating the call");
if (evaluated) {
anyEvaluated = true;
Dst.insert(checkDst);
#ifdef NDEBUG
break; // on release don't check that no other checker also evals.
#endif
}
}
if (!anyEvaluated) {
if (defaultEval)
defaultEval->expandGraph(Dst, Pred);
else
Dst.insert(Pred);
}
}
}
void CheckerManager::registerCheckersToEngine(ExprEngine &eng) {
for (unsigned i = 0, e = Funcs.size(); i != e; ++i)
Funcs[i](eng);
@ -256,6 +310,10 @@ void CheckerManager::_registerForEndAnalysis(CheckEndAnalysisFunc checkfn) {
EndAnalysisCheckers.push_back(checkfn);
}
void CheckerManager::_registerForEvalCall(EvalCallFunc checkfn) {
EvalCallCheckers.push_back(checkfn);
}
//===----------------------------------------------------------------------===//
// Implementation details.
//===----------------------------------------------------------------------===//
@ -290,3 +348,6 @@ CheckerManager::~CheckerManager() {
// Anchor for the vtable.
CheckerProvider::~CheckerProvider() { }
// Anchor for the vtable.
GraphExpander::~GraphExpander() { }