From: Argyrios Kyrtzidis Date: Thu, 24 Feb 2011 01:05:30 +0000 (+0000) Subject: [analyzer] Migrate CStringChecker to CheckerV2. X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=183ff98f425d470c2a0276880aaf43496c9dad14;p=clang [analyzer] Migrate CStringChecker to CheckerV2. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@126350 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/StaticAnalyzer/Core/CheckerManager.h b/include/clang/StaticAnalyzer/Core/CheckerManager.h index 9345585b8e..738661512e 100644 --- a/include/clang/StaticAnalyzer/Core/CheckerManager.h +++ b/include/clang/StaticAnalyzer/Core/CheckerManager.h @@ -37,6 +37,8 @@ namespace ento { class ExplodedGraph; class GRState; class EndOfFunctionNodeBuilder; + class MemRegion; + class SymbolReaper; class GraphExpander { public: @@ -190,6 +192,24 @@ public: /// \brief Run checkers for end of path. void runCheckersForEndPath(EndOfFunctionNodeBuilder &B, ExprEngine &Eng); + /// \brief Run checkers for live symbols. + void runCheckersForLiveSymbols(const GRState *state, + SymbolReaper &SymReaper); + + /// \brief Run checkers for dead symbols. + void runCheckersForDeadSymbols(ExplodedNodeSet &Dst, + const ExplodedNodeSet &Src, + SymbolReaper &SymReaper, const Stmt *S, + ExprEngine &Eng); + + /// \brief True if at least one checker wants to check region changes. + bool wantsRegionChangeUpdate(const GRState *state); + + /// \brief Run checkers for region changes. + const GRState *runCheckersForRegionChanges(const GRState *state, + const MemRegion * const *Begin, + const MemRegion * const *End); + /// \brief Run checkers for evaluating a call. void runCheckersForEvalCall(ExplodedNodeSet &Dst, const ExplodedNodeSet &Src, @@ -226,6 +246,8 @@ public: typedef CheckerFn CheckEndAnalysisFunc; typedef CheckerFn CheckEndPathFunc; + typedef CheckerFn CheckDeadSymbolsFunc; + typedef CheckerFn CheckLiveSymbolsFunc; typedef bool (*HandlesStmtFunc)(const Stmt *D); void _registerForPreStmt(CheckStmtFunc checkfn, @@ -242,6 +264,40 @@ public: void _registerForEndPath(CheckEndPathFunc checkfn); + void _registerForLiveSymbols(CheckLiveSymbolsFunc checkfn); + + void _registerForDeadSymbols(CheckDeadSymbolsFunc checkfn); + + class CheckRegionChangesFunc { + typedef const GRState * (*Func)(void *, const GRState *, + const MemRegion * const *, + const MemRegion * const *); + Func Fn; + public: + void *Checker; + CheckRegionChangesFunc(void *checker, Func fn) : Fn(fn), Checker(checker) {} + const GRState *operator()(const GRState *state, + const MemRegion * const *begin, + const MemRegion * const *end) { + return Fn(Checker, state, begin, end); + } + }; + + class WantsRegionChangeUpdateFunc { + typedef bool (*Func)(void *, const GRState *); + Func Fn; + public: + void *Checker; + WantsRegionChangeUpdateFunc(void *checker, Func fn) + : Fn(fn), Checker(checker) { } + bool operator()(const GRState *state) { + return Fn(Checker, state); + } + }; + + void _registerForRegionChanges(CheckRegionChangesFunc checkfn, + WantsRegionChangeUpdateFunc wantUpdateFn); + class EvalCallFunc { typedef bool (*Func)(void *, const CallExpr *, CheckerContext &); Func Fn; @@ -325,6 +381,16 @@ private: std::vector EndPathCheckers; + std::vector LiveSymbolsCheckers; + + std::vector DeadSymbolsCheckers; + + struct RegionChangesCheckerInfo { + CheckRegionChangesFunc CheckFn; + WantsRegionChangeUpdateFunc WantUpdateFn; + }; + std::vector RegionChangesCheckers; + std::vector EvalCallCheckers; }; diff --git a/include/clang/StaticAnalyzer/Core/CheckerV2.h b/include/clang/StaticAnalyzer/Core/CheckerV2.h index a58a593a6c..e080d190ab 100644 --- a/include/clang/StaticAnalyzer/Core/CheckerV2.h +++ b/include/clang/StaticAnalyzer/Core/CheckerV2.h @@ -175,6 +175,59 @@ public: } }; +class LiveSymbols { + template + static void _checkLiveSymbols(void *checker, const GRState *state, + SymbolReaper &SR) { + ((const CHECKER *)checker)->checkLiveSymbols(state, SR); + } + +public: + template + static void _register(CHECKER *checker, CheckerManager &mgr) { + mgr._registerForLiveSymbols( + CheckerManager::CheckLiveSymbolsFunc(checker, _checkLiveSymbols)); + } +}; + +class DeadSymbols { + template + static void _checkDeadSymbols(void *checker, + SymbolReaper &SR, CheckerContext &C) { + ((const CHECKER *)checker)->checkDeadSymbols(SR, C); + } + +public: + template + static void _register(CHECKER *checker, CheckerManager &mgr) { + mgr._registerForDeadSymbols( + CheckerManager::CheckDeadSymbolsFunc(checker, _checkDeadSymbols)); + } +}; + +class RegionChanges { + template + static const GRState *_checkRegionChanges(void *checker, const GRState *state, + const MemRegion * const *Begin, + const MemRegion * const *End) { + return ((const CHECKER *)checker)->checkRegionChanges(state, Begin, End); + } + template + static bool _wantsRegionChangeUpdate(void *checker, const GRState *state) { + return ((const CHECKER *)checker)->wantsRegionChangeUpdate(state); + } + +public: + template + static void _register(CHECKER *checker, CheckerManager &mgr) { + mgr._registerForRegionChanges( + CheckerManager::CheckRegionChangesFunc(checker, + _checkRegionChanges), + CheckerManager::WantsRegionChangeUpdateFunc(checker, + _wantsRegionChangeUpdate)); + } +}; + } // end check namespace namespace eval { diff --git a/lib/StaticAnalyzer/Checkers/CStringChecker.cpp b/lib/StaticAnalyzer/Checkers/CStringChecker.cpp index ddeb368f2b..2566e3cbb4 100644 --- a/lib/StaticAnalyzer/Checkers/CStringChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/CStringChecker.cpp @@ -13,9 +13,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/GRStateTrait.h" #include "llvm/ADT/StringSwitch.h" @@ -23,80 +24,86 @@ using namespace clang; using namespace ento; namespace { -class CStringChecker : public CheckerVisitor { - BugType *BT_Null, *BT_Bounds, *BT_BoundsWrite, *BT_Overlap, *BT_NotCString; +class CStringChecker : public CheckerV2< eval::Call, + check::PreStmt, + check::LiveSymbols, + check::DeadSymbols, + check::RegionChanges + > { + mutable llvm::OwningPtr BT_Null, BT_Bounds, BT_BoundsWrite, + BT_Overlap, BT_NotCString; public: - CStringChecker() - : BT_Null(0), BT_Bounds(0), BT_BoundsWrite(0), BT_Overlap(0), BT_NotCString(0) - {} static void *getTag() { static int tag; return &tag; } - bool evalCallExpr(CheckerContext &C, const CallExpr *CE); - void PreVisitDeclStmt(CheckerContext &C, const DeclStmt *DS); - void MarkLiveSymbols(const GRState *state, SymbolReaper &SR); - void evalDeadSymbols(CheckerContext &C, SymbolReaper &SR); - bool wantsRegionChangeUpdate(const GRState *state); + bool evalCall(const CallExpr *CE, CheckerContext &C) const; + void checkPreStmt(const DeclStmt *DS, CheckerContext &C) const; + void checkLiveSymbols(const GRState *state, SymbolReaper &SR) const; + void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const; + bool wantsRegionChangeUpdate(const GRState *state) const; - const GRState *EvalRegionChanges(const GRState *state, - const MemRegion * const *Begin, - const MemRegion * const *End, - bool*); + const GRState *checkRegionChanges(const GRState *state, + const MemRegion * const *Begin, + const MemRegion * const *End) const; - typedef void (CStringChecker::*FnCheck)(CheckerContext &, const CallExpr *); + typedef void (CStringChecker::*FnCheck)(CheckerContext &, + const CallExpr *) const; - void evalMemcpy(CheckerContext &C, const CallExpr *CE); - void evalMemmove(CheckerContext &C, const CallExpr *CE); - void evalBcopy(CheckerContext &C, const CallExpr *CE); + void evalMemcpy(CheckerContext &C, const CallExpr *CE) const; + void evalMemmove(CheckerContext &C, const CallExpr *CE) const; + void evalBcopy(CheckerContext &C, const CallExpr *CE) const; void evalCopyCommon(CheckerContext &C, const GRState *state, const Expr *Size, const Expr *Source, const Expr *Dest, - bool Restricted = false); + bool Restricted = false) const; - void evalMemcmp(CheckerContext &C, const CallExpr *CE); + void evalMemcmp(CheckerContext &C, const CallExpr *CE) const; - void evalstrLength(CheckerContext &C, const CallExpr *CE); - void evalstrnLength(CheckerContext &C, const CallExpr *CE); + void evalstrLength(CheckerContext &C, const CallExpr *CE) const; + void evalstrnLength(CheckerContext &C, const CallExpr *CE) const; void evalstrLengthCommon(CheckerContext &C, const CallExpr *CE, - bool IsStrnlen = false); + bool IsStrnlen = false) const; - void evalStrcpy(CheckerContext &C, const CallExpr *CE); - void evalStrncpy(CheckerContext &C, const CallExpr *CE); - void evalStpcpy(CheckerContext &C, const CallExpr *CE); + void evalStrcpy(CheckerContext &C, const CallExpr *CE) const; + void evalStrncpy(CheckerContext &C, const CallExpr *CE) const; + void evalStpcpy(CheckerContext &C, const CallExpr *CE) const; void evalStrcpyCommon(CheckerContext &C, const CallExpr *CE, bool returnEnd, - bool isStrncpy); + bool isStrncpy) const; // Utility methods std::pair - assumeZero(CheckerContext &C, const GRState *state, SVal V, QualType Ty); - - const GRState *setCStringLength(const GRState *state, const MemRegion *MR, - SVal strLength); - SVal getCStringLengthForRegion(CheckerContext &C, const GRState *&state, - const Expr *Ex, const MemRegion *MR); + static assumeZero(CheckerContext &C, + const GRState *state, SVal V, QualType Ty); + + static const GRState *setCStringLength(const GRState *state, + const MemRegion *MR, SVal strLength); + static SVal getCStringLengthForRegion(CheckerContext &C, + const GRState *&state, + const Expr *Ex, const MemRegion *MR); SVal getCStringLength(CheckerContext &C, const GRState *&state, - const Expr *Ex, SVal Buf); + const Expr *Ex, SVal Buf) const; - const GRState *InvalidateBuffer(CheckerContext &C, const GRState *state, - const Expr *Ex, SVal V); + static const GRState *InvalidateBuffer(CheckerContext &C, + const GRState *state, + const Expr *Ex, SVal V); - bool SummarizeRegion(llvm::raw_ostream& os, ASTContext& Ctx, - const MemRegion *MR); + static bool SummarizeRegion(llvm::raw_ostream& os, ASTContext& Ctx, + const MemRegion *MR); // Re-usable checks const GRState *checkNonNull(CheckerContext &C, const GRState *state, - const Expr *S, SVal l); + const Expr *S, SVal l) const; const GRState *CheckLocation(CheckerContext &C, const GRState *state, const Expr *S, SVal l, - bool IsDestination = false); + bool IsDestination = false) const; const GRState *CheckBufferAccess(CheckerContext &C, const GRState *state, const Expr *Size, const Expr *FirstBuf, const Expr *SecondBuf = NULL, - bool FirstIsDestination = false); + bool FirstIsDestination = false) const; const GRState *CheckOverlap(CheckerContext &C, const GRState *state, const Expr *Size, const Expr *First, - const Expr *Second); + const Expr *Second) const; void emitOverlapBug(CheckerContext &C, const GRState *state, - const Stmt *First, const Stmt *Second); + const Stmt *First, const Stmt *Second) const; }; class CStringLength { @@ -115,14 +122,6 @@ namespace ento { } } -static void RegisterCStringChecker(ExprEngine &Eng) { - Eng.registerCheck(new CStringChecker()); -} - -void ento::registerCStringChecker(CheckerManager &mgr) { - mgr.addCheckerRegisterFunction(RegisterCStringChecker); -} - //===----------------------------------------------------------------------===// // Individual checks and utility methods. //===----------------------------------------------------------------------===// @@ -141,7 +140,7 @@ CStringChecker::assumeZero(CheckerContext &C, const GRState *state, SVal V, const GRState *CStringChecker::checkNonNull(CheckerContext &C, const GRState *state, - const Expr *S, SVal l) { + const Expr *S, SVal l) const { // If a previous check has failed, propagate the failure. if (!state) return NULL; @@ -155,11 +154,11 @@ const GRState *CStringChecker::checkNonNull(CheckerContext &C, return NULL; if (!BT_Null) - BT_Null = new BuiltinBug("API", - "Null pointer argument in call to byte string function"); + BT_Null.reset(new BuiltinBug("API", + "Null pointer argument in call to byte string function")); // Generate a report for this bug. - BuiltinBug *BT = static_cast(BT_Null); + BuiltinBug *BT = static_cast(BT_Null.get()); EnhancedBugReport *report = new EnhancedBugReport(*BT, BT->getDescription(), N); @@ -178,7 +177,7 @@ const GRState *CStringChecker::checkNonNull(CheckerContext &C, const GRState *CStringChecker::CheckLocation(CheckerContext &C, const GRState *state, const Expr *S, SVal l, - bool IsDestination) { + bool IsDestination) const { // If a previous check has failed, propagate the failure. if (!state) return NULL; @@ -214,16 +213,16 @@ const GRState *CStringChecker::CheckLocation(CheckerContext &C, BuiltinBug *BT; if (IsDestination) { if (!BT_BoundsWrite) { - BT_BoundsWrite = new BuiltinBug("Out-of-bound array access", - "Byte string function overflows destination buffer"); + BT_BoundsWrite.reset(new BuiltinBug("Out-of-bound array access", + "Byte string function overflows destination buffer")); } - BT = static_cast(BT_BoundsWrite); + BT = static_cast(BT_BoundsWrite.get()); } else { if (!BT_Bounds) { - BT_Bounds = new BuiltinBug("Out-of-bound array access", - "Byte string function accesses out-of-bound array element"); + BT_Bounds.reset(new BuiltinBug("Out-of-bound array access", + "Byte string function accesses out-of-bound array element")); } - BT = static_cast(BT_Bounds); + BT = static_cast(BT_Bounds.get()); } // FIXME: It would be nice to eventually make this diagnostic more clear, @@ -248,7 +247,7 @@ const GRState *CStringChecker::CheckBufferAccess(CheckerContext &C, const Expr *Size, const Expr *FirstBuf, const Expr *SecondBuf, - bool FirstIsDestination) { + bool FirstIsDestination) const { // If a previous check has failed, propagate the failure. if (!state) return NULL; @@ -311,7 +310,7 @@ const GRState *CStringChecker::CheckOverlap(CheckerContext &C, const GRState *state, const Expr *Size, const Expr *First, - const Expr *Second) { + const Expr *Second) const { // Do a simple check for overlap: if the two arguments are from the same // buffer, see if the end of the first is greater than the start of the second // or vice versa. @@ -418,13 +417,13 @@ const GRState *CStringChecker::CheckOverlap(CheckerContext &C, } void CStringChecker::emitOverlapBug(CheckerContext &C, const GRState *state, - const Stmt *First, const Stmt *Second) { + const Stmt *First, const Stmt *Second) const { ExplodedNode *N = C.generateSink(state); if (!N) return; if (!BT_Overlap) - BT_Overlap = new BugType("Unix API", "Improper arguments"); + BT_Overlap.reset(new BugType("Unix API", "Improper arguments")); // Generate a report for this bug. RangedBugReport *report = @@ -485,13 +484,14 @@ SVal CStringChecker::getCStringLengthForRegion(CheckerContext &C, unsigned Count = C.getNodeBuilder().getCurrentBlockCount(); SValBuilder &svalBuilder = C.getSValBuilder(); QualType sizeTy = svalBuilder.getContext().getSizeType(); - SVal strLength = svalBuilder.getMetadataSymbolVal(getTag(), MR, Ex, sizeTy, Count); + SVal strLength = svalBuilder.getMetadataSymbolVal(CStringChecker::getTag(), + MR, Ex, sizeTy, Count); state = state->set(MR, strLength); return strLength; } SVal CStringChecker::getCStringLength(CheckerContext &C, const GRState *&state, - const Expr *Ex, SVal Buf) { + const Expr *Ex, SVal Buf) const { const MemRegion *MR = Buf.getAsRegion(); if (!MR) { // If we can't get a region, see if it's something we /know/ isn't a @@ -500,8 +500,8 @@ SVal CStringChecker::getCStringLength(CheckerContext &C, const GRState *&state, if (loc::GotoLabel *Label = dyn_cast(&Buf)) { if (ExplodedNode *N = C.generateNode(state)) { if (!BT_NotCString) - BT_NotCString = new BuiltinBug("API", - "Argument is not a null-terminated string."); + BT_NotCString.reset(new BuiltinBug("API", + "Argument is not a null-terminated string.")); llvm::SmallString<120> buf; llvm::raw_svector_ostream os(buf); @@ -556,8 +556,8 @@ SVal CStringChecker::getCStringLength(CheckerContext &C, const GRState *&state, // The caller should always be prepared to handle this case. if (ExplodedNode *N = C.generateNode(state)) { if (!BT_NotCString) - BT_NotCString = new BuiltinBug("API", - "Argument is not a null-terminated string."); + BT_NotCString.reset(new BuiltinBug("API", + "Argument is not a null-terminated string.")); llvm::SmallString<120> buf; llvm::raw_svector_ostream os(buf); @@ -657,7 +657,7 @@ bool CStringChecker::SummarizeRegion(llvm::raw_ostream& os, ASTContext& Ctx, void CStringChecker::evalCopyCommon(CheckerContext &C, const GRState *state, const Expr *Size, const Expr *Dest, - const Expr *Source, bool Restricted) { + const Expr *Source, bool Restricted) const { // See if the size argument is zero. SVal sizeVal = state->getSVal(Size); QualType sizeTy = Size->getType(); @@ -690,7 +690,7 @@ void CStringChecker::evalCopyCommon(CheckerContext &C, const GRState *state, } -void CStringChecker::evalMemcpy(CheckerContext &C, const CallExpr *CE) { +void CStringChecker::evalMemcpy(CheckerContext &C, const CallExpr *CE) const { // void *memcpy(void *restrict dst, const void *restrict src, size_t n); // The return value is the address of the destination buffer. const Expr *Dest = CE->getArg(0); @@ -699,7 +699,7 @@ void CStringChecker::evalMemcpy(CheckerContext &C, const CallExpr *CE) { evalCopyCommon(C, state, CE->getArg(2), Dest, CE->getArg(1), true); } -void CStringChecker::evalMemmove(CheckerContext &C, const CallExpr *CE) { +void CStringChecker::evalMemmove(CheckerContext &C, const CallExpr *CE) const { // void *memmove(void *dst, const void *src, size_t n); // The return value is the address of the destination buffer. const Expr *Dest = CE->getArg(0); @@ -708,12 +708,12 @@ void CStringChecker::evalMemmove(CheckerContext &C, const CallExpr *CE) { evalCopyCommon(C, state, CE->getArg(2), Dest, CE->getArg(1)); } -void CStringChecker::evalBcopy(CheckerContext &C, const CallExpr *CE) { +void CStringChecker::evalBcopy(CheckerContext &C, const CallExpr *CE) const { // void bcopy(const void *src, void *dst, size_t n); evalCopyCommon(C, C.getState(), CE->getArg(2), CE->getArg(1), CE->getArg(0)); } -void CStringChecker::evalMemcmp(CheckerContext &C, const CallExpr *CE) { +void CStringChecker::evalMemcmp(CheckerContext &C, const CallExpr *CE) const { // int memcmp(const void *s1, const void *s2, size_t n); const Expr *Left = CE->getArg(0); const Expr *Right = CE->getArg(1); @@ -779,18 +779,20 @@ void CStringChecker::evalMemcmp(CheckerContext &C, const CallExpr *CE) { } } -void CStringChecker::evalstrLength(CheckerContext &C, const CallExpr *CE) { +void CStringChecker::evalstrLength(CheckerContext &C, + const CallExpr *CE) const { // size_t strlen(const char *s); evalstrLengthCommon(C, CE, /* IsStrnlen = */ false); } -void CStringChecker::evalstrnLength(CheckerContext &C, const CallExpr *CE) { +void CStringChecker::evalstrnLength(CheckerContext &C, + const CallExpr *CE) const { // size_t strnlen(const char *s, size_t maxlen); evalstrLengthCommon(C, CE, /* IsStrnlen = */ true); } void CStringChecker::evalstrLengthCommon(CheckerContext &C, const CallExpr *CE, - bool IsStrnlen) { + bool IsStrnlen) const { const GRState *state = C.getState(); const Expr *Arg = CE->getArg(0); SVal ArgVal = state->getSVal(Arg); @@ -845,23 +847,23 @@ void CStringChecker::evalstrLengthCommon(CheckerContext &C, const CallExpr *CE, } } -void CStringChecker::evalStrcpy(CheckerContext &C, const CallExpr *CE) { +void CStringChecker::evalStrcpy(CheckerContext &C, const CallExpr *CE) const { // char *strcpy(char *restrict dst, const char *restrict src); evalStrcpyCommon(C, CE, /* returnEnd = */ false, /* isStrncpy = */ false); } -void CStringChecker::evalStrncpy(CheckerContext &C, const CallExpr *CE) { +void CStringChecker::evalStrncpy(CheckerContext &C, const CallExpr *CE) const { // char *strcpy(char *restrict dst, const char *restrict src); evalStrcpyCommon(C, CE, /* returnEnd = */ false, /* isStrncpy = */ true); } -void CStringChecker::evalStpcpy(CheckerContext &C, const CallExpr *CE) { +void CStringChecker::evalStpcpy(CheckerContext &C, const CallExpr *CE) const { // char *stpcpy(char *restrict dst, const char *restrict src); evalStrcpyCommon(C, CE, /* returnEnd = */ true, /* isStrncpy = */ false); } void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE, - bool returnEnd, bool isStrncpy) { + bool returnEnd, bool isStrncpy) const { const GRState *state = C.getState(); // Check that the destination is non-null @@ -960,7 +962,7 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE, // The driver method, and other Checker callbacks. //===----------------------------------------------------------------------===// -bool CStringChecker::evalCallExpr(CheckerContext &C, const CallExpr *CE) { +bool CStringChecker::evalCall(const CallExpr *CE, CheckerContext &C) const { // Get the callee. All the functions we care about are C functions // with simple identifiers. const GRState *state = C.getState(); @@ -999,7 +1001,7 @@ bool CStringChecker::evalCallExpr(CheckerContext &C, const CallExpr *CE) { return true; } -void CStringChecker::PreVisitDeclStmt(CheckerContext &C, const DeclStmt *DS) { +void CStringChecker::checkPreStmt(const DeclStmt *DS, CheckerContext &C) const { // Record string length for char a[] = "abc"; const GRState *state = C.getState(); @@ -1035,15 +1037,15 @@ void CStringChecker::PreVisitDeclStmt(CheckerContext &C, const DeclStmt *DS) { C.addTransition(state); } -bool CStringChecker::wantsRegionChangeUpdate(const GRState *state) { +bool CStringChecker::wantsRegionChangeUpdate(const GRState *state) const { CStringLength::EntryMap Entries = state->get(); return !Entries.isEmpty(); } -const GRState *CStringChecker::EvalRegionChanges(const GRState *state, - const MemRegion * const *Begin, - const MemRegion * const *End, - bool *) { +const GRState * +CStringChecker::checkRegionChanges(const GRState *state, + const MemRegion * const *Begin, + const MemRegion * const *End) const { CStringLength::EntryMap Entries = state->get(); if (Entries.isEmpty()) return state; @@ -1090,7 +1092,8 @@ const GRState *CStringChecker::EvalRegionChanges(const GRState *state, return state->set(Entries); } -void CStringChecker::MarkLiveSymbols(const GRState *state, SymbolReaper &SR) { +void CStringChecker::checkLiveSymbols(const GRState *state, + SymbolReaper &SR) const { // Mark all symbols in our string length map as valid. CStringLength::EntryMap Entries = state->get(); @@ -1102,7 +1105,8 @@ void CStringChecker::MarkLiveSymbols(const GRState *state, SymbolReaper &SR) { } } -void CStringChecker::evalDeadSymbols(CheckerContext &C, SymbolReaper &SR) { +void CStringChecker::checkDeadSymbols(SymbolReaper &SR, + CheckerContext &C) const { if (!SR.hasDeadSymbols()) return; @@ -1124,3 +1128,7 @@ void CStringChecker::evalDeadSymbols(CheckerContext &C, SymbolReaper &SR) { state = state->set(Entries); C.generateNode(state); } + +void ento::registerCStringChecker(CheckerManager &mgr) { + mgr.registerChecker(); +} diff --git a/lib/StaticAnalyzer/Checkers/ExprEngine.cpp b/lib/StaticAnalyzer/Checkers/ExprEngine.cpp index 15e6e25b80..bcc3cc5d82 100644 --- a/lib/StaticAnalyzer/Checkers/ExprEngine.cpp +++ b/lib/StaticAnalyzer/Checkers/ExprEngine.cpp @@ -515,7 +515,7 @@ bool ExprEngine::wantsRegionChangeUpdate(const GRState* state) { return true; } - return false; + return getCheckerManager().wantsRegionChangeUpdate(state); } const GRState * @@ -543,9 +543,9 @@ ExprEngine::processRegionChanges(const GRState *state, CO = CO_Ref; } - // If there are no checkers, just return the state as is. + // If there are no checkers, just delegate to the checker manager. if (CO->empty()) - return state; + return getCheckerManager().runCheckersForRegionChanges(state, Begin, End); for (CheckersOrdered::iterator I = CO->begin(), E = CO->end(); I != E; ++I) { // If any checker declares the state infeasible (or if it starts that way), @@ -568,7 +568,7 @@ ExprEngine::processRegionChanges(const GRState *state, if (NewCO.get()) CO_Ref = NewCO.take(); - return state; + return getCheckerManager().runCheckersForRegionChanges(state, Begin, End); } void ExprEngine::processEndWorklist(bool hasWorkRemaining) { @@ -624,6 +624,8 @@ void ExprEngine::ProcessStmt(const CFGStmt S, StmtNodeBuilder& builder) { checker->MarkLiveSymbols(St, SymReaper); } + getCheckerManager().runCheckersForLiveSymbols(St, SymReaper); + const StackFrameContext *SFC = LC->getCurrentStackFrame(); CleanedState = StateMgr.removeDeadBindings(St, SFC, SymReaper); } else { @@ -647,8 +649,9 @@ void ExprEngine::ProcessStmt(const CFGStmt S, StmtNodeBuilder& builder) { getTF().evalDeadSymbols(Tmp2, *this, *Builder, EntryNode, CleanedState, SymReaper); + ExplodedNodeSet checkersV1Tmp; if (Checkers.empty()) - Tmp.insert(Tmp2); + checkersV1Tmp.insert(Tmp2); else { ExplodedNodeSet Tmp3; ExplodedNodeSet *SrcSet = &Tmp2; @@ -656,7 +659,7 @@ void ExprEngine::ProcessStmt(const CFGStmt S, StmtNodeBuilder& builder) { I != E; ++I) { ExplodedNodeSet *DstSet = 0; if (I+1 == E) - DstSet = &Tmp; + DstSet = &checkersV1Tmp; else { DstSet = (SrcSet == &Tmp2) ? &Tmp3 : &Tmp2; DstSet->clear(); @@ -672,6 +675,9 @@ void ExprEngine::ProcessStmt(const CFGStmt S, StmtNodeBuilder& builder) { } } + getCheckerManager().runCheckersForDeadSymbols(Tmp, checkersV1Tmp, + SymReaper, currentStmt, *this); + if (!Builder->BuildSinks && !Builder->hasGeneratedNode) Tmp.Add(EntryNode); } diff --git a/lib/StaticAnalyzer/Core/CheckerManager.cpp b/lib/StaticAnalyzer/Core/CheckerManager.cpp index f909860f7d..d9990bc7bb 100644 --- a/lib/StaticAnalyzer/Core/CheckerManager.cpp +++ b/lib/StaticAnalyzer/Core/CheckerManager.cpp @@ -227,6 +227,71 @@ void CheckerManager::runCheckersForEndPath(EndOfFunctionNodeBuilder &B, } } +/// \brief Run checkers for live symbols. +void CheckerManager::runCheckersForLiveSymbols(const GRState *state, + SymbolReaper &SymReaper) { + for (unsigned i = 0, e = LiveSymbolsCheckers.size(); i != e; ++i) + LiveSymbolsCheckers[i](state, SymReaper); +} + +namespace { + struct CheckDeadSymbolsContext { + typedef std::vector CheckersTy; + const CheckersTy &Checkers; + SymbolReaper &SR; + const Stmt *S; + ExprEngine &Eng; + + CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); } + CheckersTy::const_iterator checkers_end() { return Checkers.end(); } + + CheckDeadSymbolsContext(const CheckersTy &checkers, SymbolReaper &sr, + const Stmt *s, ExprEngine &eng) + : Checkers(checkers), SR(sr), S(s), Eng(eng) { } + + void runChecker(CheckerManager::CheckDeadSymbolsFunc checkFn, + ExplodedNodeSet &Dst, ExplodedNode *Pred) { + CheckerContext C(Dst, Eng.getBuilder(), Eng, Pred, checkFn.Checker, + ProgramPoint::PostPurgeDeadSymbolsKind, 0, S); + checkFn(SR, C); + } + }; +} + +/// \brief Run checkers for dead symbols. +void CheckerManager::runCheckersForDeadSymbols(ExplodedNodeSet &Dst, + const ExplodedNodeSet &Src, + SymbolReaper &SymReaper, + const Stmt *S, + ExprEngine &Eng) { + CheckDeadSymbolsContext C(DeadSymbolsCheckers, SymReaper, S, Eng); + expandGraphWithCheckers(C, Dst, Src); +} + +/// \brief True if at least one checker wants to check region changes. +bool CheckerManager::wantsRegionChangeUpdate(const GRState *state) { + for (unsigned i = 0, e = RegionChangesCheckers.size(); i != e; ++i) + if (RegionChangesCheckers[i].WantUpdateFn(state)) + return true; + + return false; +} + +/// \brief Run checkers for region changes. +const GRState * +CheckerManager::runCheckersForRegionChanges(const GRState *state, + const MemRegion * const *Begin, + const MemRegion * const *End) { + for (unsigned i = 0, e = RegionChangesCheckers.size(); i != e; ++i) { + // If any checker declares the state infeasible (or if it starts that way), + // bail out. + if (!state) + return NULL; + state = RegionChangesCheckers[i].CheckFn(state, Begin, End); + } + return state; +} + /// \brief Run checkers for evaluating a call. /// Only one checker will evaluate the call. void CheckerManager::runCheckersForEvalCall(ExplodedNodeSet &Dst, @@ -324,6 +389,20 @@ void CheckerManager::_registerForEndPath(CheckEndPathFunc checkfn) { EndPathCheckers.push_back(checkfn); } +void CheckerManager::_registerForLiveSymbols(CheckLiveSymbolsFunc checkfn) { + LiveSymbolsCheckers.push_back(checkfn); +} + +void CheckerManager::_registerForDeadSymbols(CheckDeadSymbolsFunc checkfn) { + DeadSymbolsCheckers.push_back(checkfn); +} + +void CheckerManager::_registerForRegionChanges(CheckRegionChangesFunc checkfn, + WantsRegionChangeUpdateFunc wantUpdateFn) { + RegionChangesCheckerInfo info = {checkfn, wantUpdateFn}; + RegionChangesCheckers.push_back(info); +} + void CheckerManager::_registerForEvalCall(EvalCallFunc checkfn) { EvalCallCheckers.push_back(checkfn); }