From: Argyrios Kyrtzidis Date: Wed, 23 Feb 2011 19:38:39 +0000 (+0000) Subject: [analyzer] Migrate ChrootChecker to CheckerV2. X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=e1bfb7ae0dd0762c88e1fd94746e973c37f2e04e;p=clang [analyzer] Migrate ChrootChecker to CheckerV2. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@126324 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/StaticAnalyzer/Core/CheckerManager.h b/include/clang/StaticAnalyzer/Core/CheckerManager.h index c8d8c6681a..6a875571c7 100644 --- a/include/clang/StaticAnalyzer/Core/CheckerManager.h +++ b/include/clang/StaticAnalyzer/Core/CheckerManager.h @@ -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 @@ -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 LocationCheckers; std::vector EndAnalysisCheckers; + + std::vector EvalCallCheckers; }; } // end ento namespace diff --git a/include/clang/StaticAnalyzer/Core/CheckerV2.h b/include/clang/StaticAnalyzer/Core/CheckerV2.h index 45c5b73fcc..5133db4435 100644 --- a/include/clang/StaticAnalyzer/Core/CheckerV2.h +++ b/include/clang/StaticAnalyzer/Core/CheckerV2.h @@ -162,6 +162,24 @@ public: } // end check namespace +namespace eval { + +class Call { + template + static bool _evalCall(void *checker, const CallExpr *CE, CheckerContext &C) { + return ((const CHECKER *)checker)->evalCall(CE, C); + } + +public: + template + static void _register(CHECKER *checker, CheckerManager &mgr) { + mgr._registerForEvalCall( + CheckerManager::EvalCallFunc(checker, _evalCall)); + } +}; + +} // end eval namespace + template { - IdentifierInfo *II_chroot, *II_chdir; +class ChrootChecker : public CheckerV2 > { + mutable IdentifierInfo *II_chroot, *II_chdir; // This bug refers to possibly break out of a chroot() jail. - BuiltinBug *BT_BreakJail; + mutable llvm::OwningPtr 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(); +} diff --git a/lib/StaticAnalyzer/Checkers/ExprEngine.cpp b/lib/StaticAnalyzer/Checkers/ExprEngine.cpp index 01aad7a4be..283f930d4a 100644 --- a/lib/StaticAnalyzer/Checkers/ExprEngine.cpp +++ b/lib/StaticAnalyzer/Checkers/ExprEngine.cpp @@ -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; } diff --git a/lib/StaticAnalyzer/Core/CheckerManager.cpp b/lib/StaticAnalyzer/Core/CheckerManager.cpp index 0ff29757a6..ba52c4014d 100644 --- a/lib/StaticAnalyzer/Core/CheckerManager.cpp +++ b/lib/StaticAnalyzer/Core/CheckerManager.cpp @@ -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::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() { }