diff options
Diffstat (limited to 'lib/StaticAnalyzer')
34 files changed, 1243 insertions, 876 deletions
diff --git a/lib/StaticAnalyzer/Checkers/ArrayBoundChecker.cpp b/lib/StaticAnalyzer/Checkers/ArrayBoundChecker.cpp index 9194791..25e224e 100644 --- a/lib/StaticAnalyzer/Checkers/ArrayBoundChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/ArrayBoundChecker.cpp @@ -12,9 +12,11 @@ // //===----------------------------------------------------------------------===// -#include "InternalChecks.h" +#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/ExprEngine.h" using namespace clang; @@ -22,21 +24,15 @@ using namespace ento; namespace { class ArrayBoundChecker : - public CheckerVisitor<ArrayBoundChecker> { - BuiltinBug *BT; + public CheckerV2<check::Location> { + mutable llvm::OwningPtr<BuiltinBug> BT; public: - ArrayBoundChecker() : BT(0) {} - static void *getTag() { static int x = 0; return &x; } - void visitLocation(CheckerContext &C, const Stmt *S, SVal l, bool isLoad); + void checkLocation(SVal l, bool isLoad, CheckerContext &C) const; }; } -void ento::RegisterArrayBoundChecker(ExprEngine &Eng) { - Eng.registerCheck(new ArrayBoundChecker()); -} - -void ArrayBoundChecker::visitLocation(CheckerContext &C, const Stmt *S, SVal l, - bool isLoad) { +void ArrayBoundChecker::checkLocation(SVal l, bool isLoad, + CheckerContext &C) const { // Check for out of bound array element access. const MemRegion *R = l.getAsRegion(); if (!R) @@ -69,8 +65,8 @@ void ArrayBoundChecker::visitLocation(CheckerContext &C, const Stmt *S, SVal l, return; if (!BT) - BT = new BuiltinBug("Out-of-bound array access", - "Access out-of-bound array element (buffer overflow)"); + BT.reset(new BuiltinBug("Out-of-bound array access", + "Access out-of-bound array element (buffer overflow)")); // FIXME: It would be nice to eventually make this diagnostic more clear, // e.g., by referencing the original declaration or by saying *why* this @@ -80,7 +76,7 @@ void ArrayBoundChecker::visitLocation(CheckerContext &C, const Stmt *S, SVal l, RangedBugReport *report = new RangedBugReport(*BT, BT->getDescription(), N); - report->addRange(S->getSourceRange()); + report->addRange(C.getStmt()->getSourceRange()); C.EmitReport(report); return; } @@ -90,3 +86,7 @@ void ArrayBoundChecker::visitLocation(CheckerContext &C, const Stmt *S, SVal l, assert(StInBound); C.addTransition(StInBound); } + +void ento::registerArrayBoundChecker(CheckerManager &mgr) { + mgr.registerChecker<ArrayBoundChecker>(); +} diff --git a/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp b/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp index 1b6c528..7aff201 100644 --- a/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp +++ b/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp @@ -16,14 +16,14 @@ #include "BasicObjCFoundationChecks.h" #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/PathSensitive/ExplodedGraph.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerVisitor.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" #include "clang/StaticAnalyzer/Core/PathSensitive/GRState.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerVisitor.h" #include "clang/StaticAnalyzer/Checkers/LocalCheckers.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/Expr.h" @@ -69,22 +69,23 @@ static inline bool isNil(SVal X) { //===----------------------------------------------------------------------===// namespace { - class NilArgChecker : public CheckerVisitor<NilArgChecker> { - APIMisuse *BT; - void WarnNilArg(CheckerContext &C, const ObjCMessage &msg, unsigned Arg); + class NilArgChecker : public CheckerV2<check::PreObjCMessage> { + mutable llvm::OwningPtr<APIMisuse> BT; + + void WarnNilArg(CheckerContext &C, + const ObjCMessage &msg, unsigned Arg) const; + public: - NilArgChecker() : BT(0) {} - static void *getTag() { static int x = 0; return &x; } - void preVisitObjCMessage(CheckerContext &C, ObjCMessage msg); + void checkPreObjCMessage(ObjCMessage msg, CheckerContext &C) const; }; } void NilArgChecker::WarnNilArg(CheckerContext &C, const ObjCMessage &msg, - unsigned int Arg) + unsigned int Arg) const { if (!BT) - BT = new APIMisuse("nil argument"); + BT.reset(new APIMisuse("nil argument")); if (ExplodedNode *N = C.generateSink()) { llvm::SmallString<128> sbuf; @@ -98,9 +99,8 @@ void NilArgChecker::WarnNilArg(CheckerContext &C, } } -void NilArgChecker::preVisitObjCMessage(CheckerContext &C, - ObjCMessage msg) -{ +void NilArgChecker::checkPreObjCMessage(ObjCMessage msg, + CheckerContext &C) const { const ObjCInterfaceType *ReceiverType = GetReceiverType(msg); if (!ReceiverType) return; @@ -140,14 +140,14 @@ void NilArgChecker::preVisitObjCMessage(CheckerContext &C, //===----------------------------------------------------------------------===// namespace { -class CFNumberCreateChecker : public CheckerVisitor<CFNumberCreateChecker> { - APIMisuse* BT; - IdentifierInfo* II; +class CFNumberCreateChecker : public CheckerV2< check::PreStmt<CallExpr> > { + mutable llvm::OwningPtr<APIMisuse> BT; + mutable IdentifierInfo* II; public: - CFNumberCreateChecker() : BT(0), II(0) {} - ~CFNumberCreateChecker() {} - static void *getTag() { static int x = 0; return &x; } - void PreVisitCallExpr(CheckerContext &C, const CallExpr *CE); + CFNumberCreateChecker() : II(0) {} + + void checkPreStmt(const CallExpr *CE, CheckerContext &C) const; + private: void EmitError(const TypedRegion* R, const Expr* Ex, uint64_t SourceSize, uint64_t TargetSize, uint64_t NumberKind); @@ -247,9 +247,8 @@ static const char* GetCFNumberTypeStr(uint64_t i) { } #endif -void CFNumberCreateChecker::PreVisitCallExpr(CheckerContext &C, - const CallExpr *CE) -{ +void CFNumberCreateChecker::checkPreStmt(const CallExpr *CE, + CheckerContext &C) const { const Expr* Callee = CE->getCallee(); const GRState *state = C.getState(); SVal CallV = state->getSVal(Callee); @@ -335,7 +334,7 @@ void CFNumberCreateChecker::PreVisitCallExpr(CheckerContext &C, << " bits of the input integer will be lost."; if (!BT) - BT = new APIMisuse("Bad use of CFNumberCreate"); + BT.reset(new APIMisuse("Bad use of CFNumberCreate")); RangedBugReport *report = new RangedBugReport(*BT, os.str(), N); report->addRange(CE->getArg(2)->getSourceRange()); @@ -348,19 +347,18 @@ void CFNumberCreateChecker::PreVisitCallExpr(CheckerContext &C, //===----------------------------------------------------------------------===// namespace { -class CFRetainReleaseChecker : public CheckerVisitor<CFRetainReleaseChecker> { - APIMisuse *BT; - IdentifierInfo *Retain, *Release; +class CFRetainReleaseChecker : public CheckerV2< check::PreStmt<CallExpr> > { + mutable llvm::OwningPtr<APIMisuse> BT; + mutable IdentifierInfo *Retain, *Release; public: - CFRetainReleaseChecker(): BT(0), Retain(0), Release(0) {} - static void *getTag() { static int x = 0; return &x; } - void PreVisitCallExpr(CheckerContext& C, const CallExpr* CE); + CFRetainReleaseChecker(): Retain(0), Release(0) {} + void checkPreStmt(const CallExpr* CE, CheckerContext& C) const; }; } // end anonymous namespace -void CFRetainReleaseChecker::PreVisitCallExpr(CheckerContext& C, - const CallExpr* CE) { +void CFRetainReleaseChecker::checkPreStmt(const CallExpr* CE, + CheckerContext& C) const { // If the CallExpr doesn't have exactly 1 argument just give up checking. if (CE->getNumArgs() != 1) return; @@ -377,7 +375,7 @@ void CFRetainReleaseChecker::PreVisitCallExpr(CheckerContext& C, ASTContext &Ctx = C.getASTContext(); Retain = &Ctx.Idents.get("CFRetain"); Release = &Ctx.Idents.get("CFRelease"); - BT = new APIMisuse("null passed to CFRetain/CFRelease"); + BT.reset(new APIMisuse("null passed to CFRetain/CFRelease")); } // Check if we called CFRetain/CFRelease. @@ -431,28 +429,24 @@ void CFRetainReleaseChecker::PreVisitCallExpr(CheckerContext& C, //===----------------------------------------------------------------------===// namespace { -class ClassReleaseChecker : public CheckerVisitor<ClassReleaseChecker> { - Selector releaseS; - Selector retainS; - Selector autoreleaseS; - Selector drainS; - BugType *BT; -public: - ClassReleaseChecker() - : BT(0) {} +class ClassReleaseChecker : public CheckerV2<check::PreObjCMessage> { + mutable Selector releaseS; + mutable Selector retainS; + mutable Selector autoreleaseS; + mutable Selector drainS; + mutable llvm::OwningPtr<BugType> BT; - static void *getTag() { static int x = 0; return &x; } - - void preVisitObjCMessage(CheckerContext &C, ObjCMessage msg); +public: + void checkPreObjCMessage(ObjCMessage msg, CheckerContext &C) const; }; } -void ClassReleaseChecker::preVisitObjCMessage(CheckerContext &C, - ObjCMessage msg) { +void ClassReleaseChecker::checkPreObjCMessage(ObjCMessage msg, + CheckerContext &C) const { if (!BT) { - BT = new APIMisuse("message incorrectly sent to class instead of class " - "instance"); + BT.reset(new APIMisuse("message incorrectly sent to class instead of class " + "instance")); ASTContext &Ctx = C.getASTContext(); releaseS = GetNullarySelector("release", Ctx); @@ -488,34 +482,18 @@ void ClassReleaseChecker::preVisitObjCMessage(CheckerContext &C, // Check registration. //===----------------------------------------------------------------------===// -static void RegisterNilArgChecker(ExprEngine& Eng) { - Eng.registerCheck(new NilArgChecker()); -} - void ento::registerNilArgChecker(CheckerManager &mgr) { - mgr.addCheckerRegisterFunction(RegisterNilArgChecker); -} - -static void RegisterCFNumberCreateChecker(ExprEngine& Eng) { - Eng.registerCheck(new CFNumberCreateChecker()); + mgr.registerChecker<NilArgChecker>(); } void ento::registerCFNumberCreateChecker(CheckerManager &mgr) { - mgr.addCheckerRegisterFunction(RegisterCFNumberCreateChecker); -} - -static void RegisterCFRetainReleaseChecker(ExprEngine& Eng) { - Eng.registerCheck(new CFRetainReleaseChecker()); + mgr.registerChecker<CFNumberCreateChecker>(); } void ento::registerCFRetainReleaseChecker(CheckerManager &mgr) { - mgr.addCheckerRegisterFunction(RegisterCFRetainReleaseChecker); -} - -static void RegisterClassReleaseChecker(ExprEngine& Eng) { - Eng.registerCheck(new ClassReleaseChecker()); + mgr.registerChecker<CFRetainReleaseChecker>(); } void ento::registerClassReleaseChecker(CheckerManager &mgr) { - mgr.addCheckerRegisterFunction(RegisterClassReleaseChecker); + mgr.registerChecker<ClassReleaseChecker>(); } diff --git a/lib/StaticAnalyzer/Checkers/CMakeLists.txt b/lib/StaticAnalyzer/Checkers/CMakeLists.txt index e172a52..e308396 100644 --- a/lib/StaticAnalyzer/Checkers/CMakeLists.txt +++ b/lib/StaticAnalyzer/Checkers/CMakeLists.txt @@ -1,9 +1,7 @@ -set(LLVM_TARGET_DEFINITIONS Checkers.td) -tablegen(Checkers.inc - -gen-clang-sa-checkers - -I ${CMAKE_CURRENT_SOURCE_DIR}/../../../include) -add_custom_target(ClangSACheckers - DEPENDS Checkers.inc) +clang_tablegen(Checkers.inc -gen-clang-sa-checkers + -I ${CMAKE_CURRENT_SOURCE_DIR}/../../../include + SOURCE Checkers.td + TARGET ClangSACheckers) set(LLVM_USED_LIBS clangBasic clangAST) @@ -29,8 +27,8 @@ add_clang_library(clangStaticAnalyzerCheckers DebugCheckers.cpp DereferenceChecker.cpp DivZeroChecker.cpp - ExprEngine.cpp ExperimentalChecks.cpp + ExprEngine.cpp FixedAddressChecker.cpp IdempotentOperationChecker.cpp LLVMConventionsChecker.cpp @@ -48,7 +46,7 @@ add_clang_library(clangStaticAnalyzerCheckers PthreadLockChecker.cpp ReturnPointerRangeChecker.cpp ReturnUndefChecker.cpp - StackAddrLeakChecker.cpp + StackAddrEscapeChecker.cpp StreamChecker.cpp UndefBranchChecker.cpp UndefCapturedBlockVarChecker.cpp diff --git a/lib/StaticAnalyzer/Checkers/CStringChecker.cpp b/lib/StaticAnalyzer/Checkers/CStringChecker.cpp index 03f9047..2566e3c 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,75 +24,86 @@ using namespace clang; using namespace ento; namespace { -class CStringChecker : public CheckerVisitor<CStringChecker> { - BugType *BT_Null, *BT_Bounds, *BT_BoundsWrite, *BT_Overlap, *BT_NotCString; +class CStringChecker : public CheckerV2< eval::Call, + check::PreStmt<DeclStmt>, + check::LiveSymbols, + check::DeadSymbols, + check::RegionChanges + > { + mutable llvm::OwningPtr<BugType> 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 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) const; - void evalStrcpy(CheckerContext &C, const CallExpr *CE); - void evalStpcpy(CheckerContext &C, const CallExpr *CE); - void evalStrcpyCommon(CheckerContext &C, const CallExpr *CE, bool returnEnd); + 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) const; // Utility methods std::pair<const GRState*, const GRState*> - 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 { @@ -110,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. //===----------------------------------------------------------------------===// @@ -136,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; @@ -150,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<BuiltinBug*>(BT_Null); + BuiltinBug *BT = static_cast<BuiltinBug*>(BT_Null.get()); EnhancedBugReport *report = new EnhancedBugReport(*BT, BT->getDescription(), N); @@ -173,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; @@ -209,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<BuiltinBug*>(BT_BoundsWrite); + BT = static_cast<BuiltinBug*>(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<BuiltinBug*>(BT_Bounds); + BT = static_cast<BuiltinBug*>(BT_Bounds.get()); } // FIXME: It would be nice to eventually make this diagnostic more clear, @@ -243,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; @@ -306,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. @@ -413,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 = @@ -480,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<CStringLength>(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 @@ -495,8 +500,8 @@ SVal CStringChecker::getCStringLength(CheckerContext &C, const GRState *&state, if (loc::GotoLabel *Label = dyn_cast<loc::GotoLabel>(&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); @@ -551,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); @@ -652,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(); @@ -685,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); @@ -694,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); @@ -703,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); @@ -774,8 +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) 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) const { const GRState *state = C.getState(); const Expr *Arg = CE->getArg(0); SVal ArgVal = state->getSVal(Arg); @@ -791,6 +808,32 @@ void CStringChecker::evalstrLength(CheckerContext &C, const CallExpr *CE) { if (strLength.isUndef()) return; + // If the check is for strnlen() then bind the return value to no more than + // the maxlen value. + if (IsStrnlen) { + const Expr *maxlenExpr = CE->getArg(1); + SVal maxlenVal = state->getSVal(maxlenExpr); + + NonLoc *strLengthNL = dyn_cast<NonLoc>(&strLength); + NonLoc *maxlenValNL = dyn_cast<NonLoc>(&maxlenVal); + + QualType cmpTy = C.getSValBuilder().getContext().IntTy; + const GRState *stateTrue, *stateFalse; + + // Check if the strLength is greater than or equal to the maxlen + llvm::tie(stateTrue, stateFalse) = + state->assume(cast<DefinedOrUnknownSVal> + (C.getSValBuilder().evalBinOpNN(state, BO_GE, + *strLengthNL, *maxlenValNL, + cmpTy))); + + // If the strLength is greater than or equal to the maxlen, set strLength + // to maxlen + if (stateTrue && !stateFalse) { + strLength = maxlenVal; + } + } + // If getCStringLength couldn't figure out the length, conjure a return // value, so it can be used in constraints, at least. if (strLength.isUnknown()) { @@ -804,18 +847,23 @@ void CStringChecker::evalstrLength(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) const { // char *strcpy(char *restrict dst, const char *restrict src); - evalStrcpyCommon(C, CE, /* returnEnd = */ false); + 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); + evalStrcpyCommon(C, CE, /* returnEnd = */ true, /* isStrncpy = */ false); } void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE, - bool returnEnd) { + bool returnEnd, bool isStrncpy) const { const GRState *state = C.getState(); // Check that the destination is non-null @@ -840,6 +888,31 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE, if (strLength.isUndef()) return; + if (isStrncpy) { + // Get the max number of characters to copy + const Expr *lenExpr = CE->getArg(2); + SVal lenVal = state->getSVal(lenExpr); + + NonLoc *strLengthNL = dyn_cast<NonLoc>(&strLength); + NonLoc *lenValNL = dyn_cast<NonLoc>(&lenVal); + + QualType cmpTy = C.getSValBuilder().getContext().IntTy; + const GRState *stateTrue, *stateFalse; + + // Check if the max number to copy is less than the length of the src + llvm::tie(stateTrue, stateFalse) = + state->assume(cast<DefinedOrUnknownSVal> + (C.getSValBuilder().evalBinOpNN(state, BO_GT, + *strLengthNL, *lenValNL, + cmpTy))); + + if (stateTrue) { + // Max number to copy is less than the length of the src, so the actual + // strLength copied is the max number arg. + strLength = lenVal; + } + } + SVal Result = (returnEnd ? UnknownVal() : DstVal); // If the destination is a MemRegion, try to check for a buffer overflow and @@ -889,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(); @@ -912,8 +985,10 @@ bool CStringChecker::evalCallExpr(CheckerContext &C, const CallExpr *CE) { .Cases("memcmp", "bcmp", &CStringChecker::evalMemcmp) .Cases("memmove", "__memmove_chk", &CStringChecker::evalMemmove) .Cases("strcpy", "__strcpy_chk", &CStringChecker::evalStrcpy) + .Cases("strncpy", "__strncpy_chk", &CStringChecker::evalStrncpy) .Cases("stpcpy", "__stpcpy_chk", &CStringChecker::evalStpcpy) .Case("strlen", &CStringChecker::evalstrLength) + .Case("strnlen", &CStringChecker::evalstrnLength) .Case("bcopy", &CStringChecker::evalBcopy) .Default(NULL); @@ -926,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(); @@ -962,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<CStringLength>(); 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<CStringLength>(); if (Entries.isEmpty()) return state; @@ -1017,7 +1092,8 @@ const GRState *CStringChecker::EvalRegionChanges(const GRState *state, return state->set<CStringLength>(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<CStringLength>(); @@ -1029,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; @@ -1051,3 +1128,7 @@ void CStringChecker::evalDeadSymbols(CheckerContext &C, SymbolReaper &SR) { state = state->set<CStringLength>(Entries); C.generateNode(state); } + +void ento::registerCStringChecker(CheckerManager &mgr) { + mgr.registerChecker<CStringChecker>(); +} diff --git a/lib/StaticAnalyzer/Checkers/CastSizeChecker.cpp b/lib/StaticAnalyzer/Checkers/CastSizeChecker.cpp index 518cf96..6a4506b 100644 --- a/lib/StaticAnalyzer/Checkers/CastSizeChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/CastSizeChecker.cpp @@ -11,30 +11,25 @@ // whether the size of the symbolic region is a multiple of the size of T. // //===----------------------------------------------------------------------===// -#include "clang/AST/CharUnits.h" +#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 "InternalChecks.h" +#include "clang/AST/CharUnits.h" using namespace clang; using namespace ento; namespace { -class CastSizeChecker : public CheckerVisitor<CastSizeChecker> { - BuiltinBug *BT; +class CastSizeChecker : public CheckerV2< check::PreStmt<CastExpr> > { + mutable llvm::OwningPtr<BuiltinBug> BT; public: - CastSizeChecker() : BT(0) {} - static void *getTag(); - void PreVisitCastExpr(CheckerContext &C, const CastExpr *B); + void checkPreStmt(const CastExpr *CE, CheckerContext &C) const; }; } -void *CastSizeChecker::getTag() { - static int x; - return &x; -} - -void CastSizeChecker::PreVisitCastExpr(CheckerContext &C, const CastExpr *CE) { +void CastSizeChecker::checkPreStmt(const CastExpr *CE,CheckerContext &C) const { const Expr *E = CE->getSubExpr(); ASTContext &Ctx = C.getASTContext(); QualType ToTy = Ctx.getCanonicalType(CE->getType()); @@ -74,9 +69,9 @@ void CastSizeChecker::PreVisitCastExpr(CheckerContext &C, const CastExpr *CE) { if (regionSize % typeSize != 0) { if (ExplodedNode *errorNode = C.generateSink()) { if (!BT) - BT = new BuiltinBug("Cast region with wrong size.", + BT.reset(new BuiltinBug("Cast region with wrong size.", "Cast a region whose size is not a multiple of the" - " destination type size."); + " destination type size.")); RangedBugReport *R = new RangedBugReport(*BT, BT->getDescription(), errorNode); R->addRange(CE->getSourceRange()); @@ -86,6 +81,6 @@ void CastSizeChecker::PreVisitCastExpr(CheckerContext &C, const CastExpr *CE) { } -void ento::RegisterCastSizeChecker(ExprEngine &Eng) { - Eng.registerCheck(new CastSizeChecker()); +void ento::registerCastSizeChecker(CheckerManager &mgr) { + mgr.registerChecker<CastSizeChecker>(); } diff --git a/lib/StaticAnalyzer/Checkers/CastToStructChecker.cpp b/lib/StaticAnalyzer/Checkers/CastToStructChecker.cpp index 8ec226a..04cc253 100644 --- a/lib/StaticAnalyzer/Checkers/CastToStructChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/CastToStructChecker.cpp @@ -14,31 +14,25 @@ //===----------------------------------------------------------------------===// #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" using namespace clang; using namespace ento; namespace { -class CastToStructChecker - : public CheckerVisitor<CastToStructChecker> { - BuiltinBug *BT; +class CastToStructChecker : public CheckerV2< check::PreStmt<CastExpr> > { + mutable llvm::OwningPtr<BuiltinBug> BT; + public: - CastToStructChecker() : BT(0) {} - static void *getTag(); - void PreVisitCastExpr(CheckerContext &C, const CastExpr *B); + void checkPreStmt(const CastExpr *CE, CheckerContext &C) const; }; } -void *CastToStructChecker::getTag() { - static int x; - return &x; -} - -void CastToStructChecker::PreVisitCastExpr(CheckerContext &C, - const CastExpr *CE) { +void CastToStructChecker::checkPreStmt(const CastExpr *CE, + CheckerContext &C) const { const Expr *E = CE->getSubExpr(); ASTContext &Ctx = C.getASTContext(); QualType OrigTy = Ctx.getCanonicalType(E->getType()); @@ -64,10 +58,10 @@ void CastToStructChecker::PreVisitCastExpr(CheckerContext &C, if (!OrigPointeeTy->isRecordType()) { if (ExplodedNode *N = C.generateNode()) { if (!BT) - BT = new BuiltinBug("Cast from non-struct type to struct type", + BT.reset(new BuiltinBug("Cast from non-struct type to struct type", "Casting a non-structure type to a structure type " "and accessing a field can lead to memory access " - "errors or data corruption."); + "errors or data corruption.")); RangedBugReport *R = new RangedBugReport(*BT,BT->getDescription(), N); R->addRange(CE->getSourceRange()); C.EmitReport(R); @@ -75,10 +69,6 @@ void CastToStructChecker::PreVisitCastExpr(CheckerContext &C, } } -static void RegisterCastToStructChecker(ExprEngine &Eng) { - Eng.registerCheck(new CastToStructChecker()); -} - void ento::registerCastToStructChecker(CheckerManager &mgr) { - mgr.addCheckerRegisterFunction(RegisterCastToStructChecker); + mgr.registerChecker<CastToStructChecker>(); } diff --git a/lib/StaticAnalyzer/Checkers/Checkers.td b/lib/StaticAnalyzer/Checkers/Checkers.td index 1dc7486..894b961 100644 --- a/lib/StaticAnalyzer/Checkers/Checkers.td +++ b/lib/StaticAnalyzer/Checkers/Checkers.td @@ -71,16 +71,16 @@ def ObjCUnusedIvarsChecker : Checker<"UnusedIvars">, HelpText<"Warn about private ivars that are never used">, DescFile<"ObjCUnusedIVarsChecker.cpp">; -} +} // end "cocoa" -def StackAddrLeakChecker : Checker<"StackAddrLeak">, +def StackAddrEscapeChecker : Checker<"StackAddrEscape">, InPackage<Core>, - HelpText<"Check that addresses to stack memory are not leaked outside the function">, - DescFile<"StackAddrLeakChecker.cpp">; + HelpText<"Check that addresses to stack memory do not escape the function">, + DescFile<"StackAddrEscapeChecker.cpp">; def DeadStoresChecker : Checker<"DeadStores">, InPackage<Core>, - HelpText<"Check for stores to dead variables">, + HelpText<"Check for values stored to a variables that are never read afterwards">, DescFile<"DeadStoresChecker.cpp">; def UnixAPIChecker : Checker<"API">, @@ -90,12 +90,12 @@ def UnixAPIChecker : Checker<"API">, def MacOSXAPIChecker : Checker<"API">, InPackage<MacOSX>, - HelpText<"Check calls to various MacOSXAPIChecker">, + HelpText<"Check for proper uses of various Mac OS X APIs">, DescFile<"MacOSXAPIChecker.cpp">; def CFNumberCreateChecker : Checker<"CFNumber">, InPackage<MacOSX>, - HelpText<"Check for CFNumberCreate">, + HelpText<"Check for proper uses of CFNumberCreate">, DescFile<"BasicObjCFoundationChecks.cpp">; def CFRetainReleaseChecker : Checker<"CFRetainRelease">, @@ -137,7 +137,8 @@ def CStringChecker : Checker<"CString">, def UnreachableCodeChecker : Checker<"UnreachableCode">, InPackage<CoreExperimental>, HelpText<"Check unreachable code">, - DescFile<"UnreachableCodeChecker.cpp">; + DescFile<"UnreachableCodeChecker.cpp">, + Hidden; // Must be specified explicitly in order to run. def IdempotentOperationChecker : Checker<"IdempotentOps">, InPackage<CoreExperimental>, @@ -174,6 +175,21 @@ def SecuritySyntaxChecker : Checker<"SecuritySyntactic">, HelpText<"Perform quick security checks that require no data flow">, DescFile<"CheckSecuritySyntaxOnly.cpp">; +def ReturnPointerRangeChecker : Checker<"ReturnPtrRange">, + InPackage<CoreExperimental>, + HelpText<"Check for an out-of-bound pointer being returned to callers">, + DescFile<"ReturnPointerRangeChecker.cpp">; + +def ArrayBoundChecker : Checker<"ArrayBound">, + InPackage<CoreExperimental>, + HelpText<"Check for an out-of-bound pointer being returned to callers">, + DescFile<"ArrayBoundChecker.cpp">; + +def CastSizeChecker : Checker<"CastSize">, + InPackage<CoreExperimental>, + HelpText<"Check when casting a malloc'ed type T, whether the size is a multiple of the size of T">, + DescFile<"CastSizeChecker.cpp">; + def ObjCDeallocChecker : Checker<"Dealloc">, InPackage<CocoaExperimental>, HelpText<"Warn about Objective-C classes that lack a correct implementation of -dealloc">, diff --git a/lib/StaticAnalyzer/Checkers/ChrootChecker.cpp b/lib/StaticAnalyzer/Checkers/ChrootChecker.cpp index 36e76d0..b6eef6d 100644 --- a/lib/StaticAnalyzer/Checkers/ChrootChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/ChrootChecker.cpp @@ -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>(); +} diff --git a/lib/StaticAnalyzer/Checkers/ClangSACheckerProvider.cpp b/lib/StaticAnalyzer/Checkers/ClangSACheckerProvider.cpp index 94f200f..5c0c950 100644 --- a/lib/StaticAnalyzer/Checkers/ClangSACheckerProvider.cpp +++ b/lib/StaticAnalyzer/Checkers/ClangSACheckerProvider.cpp @@ -16,7 +16,9 @@ #include "ClangSACheckers.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/CheckerProvider.h" +#include "llvm/Support/raw_ostream.h" #include "llvm/ADT/DenseSet.h" +#include "map" using namespace clang; using namespace ento; @@ -28,6 +30,7 @@ class ClangSACheckerProvider : public CheckerProvider { public: virtual void registerCheckers(CheckerManager &checkerMgr, CheckerOptInfo *checkOpts, unsigned numCheckOpts); + virtual void printHelp(llvm::raw_ostream &OS); }; } @@ -41,6 +44,7 @@ namespace { struct StaticCheckerInfoRec { const char *FullName; void (*RegFunc)(CheckerManager &mgr); + const char *HelpText; bool Hidden; }; @@ -49,13 +53,16 @@ struct StaticCheckerInfoRec { static const StaticCheckerInfoRec StaticCheckerInfo[] = { #define GET_CHECKERS #define CHECKER(FULLNAME,CLASS,DESCFILE,HELPTEXT,HIDDEN) \ - { FULLNAME, register##CLASS, HIDDEN }, + { FULLNAME, register##CLASS, HELPTEXT, HIDDEN }, #include "Checkers.inc" - { 0, 0, 0} + { 0, 0, 0, 0} #undef CHECKER #undef GET_CHECKERS }; +static const unsigned NumCheckers = sizeof(StaticCheckerInfo) + / sizeof(StaticCheckerInfoRec) - 1; + namespace { struct CheckNameOption { @@ -104,9 +111,10 @@ static void collectCheckers(const CheckNameOption *checkName, // Enable/disable all subgroups along with this one. if (const short *subGroups = checkName->SubGroups) { - for (; *subGroups != -1; ++subGroups) - collectCheckers(&CheckNameTable[*subGroups], enable, checkers, - collectHidden && checkName->Hidden); + for (; *subGroups != -1; ++subGroups) { + const CheckNameOption *sub = &CheckNameTable[*subGroups]; + collectCheckers(sub, enable, checkers, collectHidden && !sub->Hidden); + } } } @@ -135,3 +143,41 @@ void ClangSACheckerProvider::registerCheckers(CheckerManager &checkerMgr, (*I)->RegFunc(checkerMgr); } } + +typedef std::map<std::string, const StaticCheckerInfoRec *> SortedCheckers; + +static void printCheckerOption(llvm::raw_ostream &OS,SortedCheckers &checkers) { + // Find the maximum option length. + unsigned OptionFieldWidth = 0; + for (SortedCheckers::iterator + I = checkers.begin(), E = checkers.end(); I != E; ++I) { + // Limit the amount of padding we are willing to give up for alignment. + unsigned Length = strlen(I->second->FullName); + if (Length <= 30) + OptionFieldWidth = std::max(OptionFieldWidth, Length); + } + + const unsigned InitialPad = 2; + for (SortedCheckers::iterator + I = checkers.begin(), E = checkers.end(); I != E; ++I) { + const std::string &Option = I->first; + int Pad = OptionFieldWidth - int(Option.size()); + OS.indent(InitialPad) << Option; + + // Break on long option names. + if (Pad < 0) { + OS << "\n"; + Pad = OptionFieldWidth + InitialPad; + } + OS.indent(Pad + 1) << I->second->HelpText << '\n'; + } +} + +void ClangSACheckerProvider::printHelp(llvm::raw_ostream &OS) { + // Sort checkers according to their full name. + SortedCheckers checkers; + for (unsigned i = 0; i != NumCheckers; ++i) + checkers[StaticCheckerInfo[i].FullName] = &StaticCheckerInfo[i]; + + printCheckerOption(OS, checkers); +} diff --git a/lib/StaticAnalyzer/Checkers/ExperimentalChecks.cpp b/lib/StaticAnalyzer/Checkers/ExperimentalChecks.cpp index d9bb480..990ba1c0 100644 --- a/lib/StaticAnalyzer/Checkers/ExperimentalChecks.cpp +++ b/lib/StaticAnalyzer/Checkers/ExperimentalChecks.cpp @@ -24,14 +24,3 @@ void ento::RegisterExperimentalChecks(ExprEngine &Eng) { // within ExprEngine. RegisterMallocChecker(Eng); // ArrayBoundChecker depends on this. } - -void ento::RegisterExperimentalInternalChecks(ExprEngine &Eng) { - // These are internal checks that should eventually migrate to - // RegisterInternalChecks() once they have been further tested. - - // Note that this must be registered after ReturnStackAddresEngsChecker. - RegisterReturnPointerRangeChecker(Eng); - - RegisterArrayBoundChecker(Eng); - RegisterCastSizeChecker(Eng); -} diff --git a/lib/StaticAnalyzer/Checkers/ExprEngine.cpp b/lib/StaticAnalyzer/Checkers/ExprEngine.cpp index ab8d564..c1b1e65 100644 --- a/lib/StaticAnalyzer/Checkers/ExprEngine.cpp +++ b/lib/StaticAnalyzer/Checkers/ExprEngine.cpp @@ -92,12 +92,13 @@ void ExprEngine::CheckerVisit(const Stmt *S, ExplodedNodeSet &Dst, } if (CO->empty()) { - // If there are no checkers, return early without doing any - // more work. - Dst.insert(Src); + // If there are no checkers, just delegate to the checker manager. + getCheckerManager().runCheckersForStmt(Kind == PreVisitStmtCallback, + Dst, Src, S, *this); return; } + ExplodedNodeSet CheckersV1Dst; ExplodedNodeSet Tmp; ExplodedNodeSet *PrevSet = &Src; unsigned checkersEvaluated = 0; @@ -108,7 +109,7 @@ void ExprEngine::CheckerVisit(const Stmt *S, ExplodedNodeSet &Dst, break; ExplodedNodeSet *CurrSet = 0; if (I+1 == E) - CurrSet = &Dst; + CurrSet = &CheckersV1Dst; else { CurrSet = (PrevSet == &Tmp) ? &Src : &Tmp; CurrSet->clear(); @@ -144,6 +145,9 @@ void ExprEngine::CheckerVisit(const Stmt *S, ExplodedNodeSet &Dst, // Don't autotransition. The CheckerContext objects should do this // automatically. + + getCheckerManager().runCheckersForStmt(Kind == PreVisitStmtCallback, + Dst, CheckersV1Dst, S, *this); } void ExprEngine::CheckerVisitObjCMessage(const ObjCMessage &msg, @@ -152,10 +156,12 @@ void ExprEngine::CheckerVisitObjCMessage(const ObjCMessage &msg, bool isPrevisit) { if (Checkers.empty()) { - Dst.insert(Src); + getCheckerManager().runCheckersForObjCMessage(isPrevisit, Dst, Src, msg, + *this); return; } + ExplodedNodeSet CheckersV1Dst; ExplodedNodeSet Tmp; ExplodedNodeSet *PrevSet = &Src; @@ -163,7 +169,7 @@ void ExprEngine::CheckerVisitObjCMessage(const ObjCMessage &msg, { ExplodedNodeSet *CurrSet = 0; if (I+1 == E) - CurrSet = &Dst; + CurrSet = &CheckersV1Dst; else { CurrSet = (PrevSet == &Tmp) ? &Src : &Tmp; CurrSet->clear(); @@ -181,8 +187,8 @@ void ExprEngine::CheckerVisitObjCMessage(const ObjCMessage &msg, PrevSet = CurrSet; } - // Don't autotransition. The CheckerContext objects should do this - // automatically. + getCheckerManager().runCheckersForObjCMessage(isPrevisit, Dst, CheckersV1Dst, + msg, *this); } void ExprEngine::CheckerEvalNilReceiver(const ObjCMessage &msg, @@ -232,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; } @@ -335,8 +355,6 @@ ExprEngine::ExprEngine(AnalysisManager &mgr, TransferFuncs *tf) // FIXME: Eventually remove the TF object entirely. TF->RegisterChecks(*this); TF->RegisterPrinters(getStateManager().Printers); - - mgr.getCheckerManager()->registerCheckersToEngine(*this); if (mgr.shouldEagerlyTrimExplodedGraph()) { // Enable eager node reclaimation when constructing the ExplodedGraph. @@ -495,7 +513,7 @@ bool ExprEngine::wantsRegionChangeUpdate(const GRState* state) { return true; } - return false; + return getCheckerManager().wantsRegionChangeUpdate(state); } const GRState * @@ -523,9 +541,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), @@ -548,7 +566,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) { @@ -556,6 +574,7 @@ void ExprEngine::processEndWorklist(bool hasWorkRemaining) { I != E; ++I) { I->second->VisitEndAnalysis(G, BR, *this); } + getCheckerManager().runCheckersForEndAnalysis(G, BR, *this); } void ExprEngine::processCFGElement(const CFGElement E, @@ -603,6 +622,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 { @@ -626,8 +647,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; @@ -635,7 +657,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(); @@ -651,6 +673,9 @@ void ExprEngine::ProcessStmt(const CFGStmt S, StmtNodeBuilder& builder) { } } + getCheckerManager().runCheckersForDeadSymbols(Tmp, checkersV1Tmp, + SymReaper, currentStmt, *this); + if (!Builder->BuildSinks && !Builder->hasGeneratedNode) Tmp.Add(EntryNode); } @@ -1419,8 +1444,10 @@ void ExprEngine::processEndOfFunction(EndOfFunctionNodeBuilder& builder) { for (CheckersOrdered::iterator I=Checkers.begin(),E=Checkers.end(); I!=E;++I){ void *tag = I->first; Checker *checker = I->second; - checker->evalEndPath(builder, tag, *this); + EndOfFunctionNodeBuilder B = builder.withCheckerTag(tag); + checker->evalEndPath(B, tag, *this); } + getCheckerManager().runCheckersForEndPath(builder, *this); } /// ProcessSwitch - Called by CoreEngine. Used to generate successor @@ -1923,20 +1950,35 @@ void ExprEngine::evalLocation(ExplodedNodeSet &Dst, const Stmt *S, const GRState* state, SVal location, const void *tag, bool isLoad) { // Early checks for performance reason. - if (location.isUnknown() || Checkers.empty()) { + if (location.isUnknown()) { Dst.Add(Pred); return; } - ExplodedNodeSet Src, Tmp; + if (Checkers.empty()) { + ExplodedNodeSet Src; + if (Builder->GetState(Pred) == state) { + Src.Add(Pred); + } else { + // Associate this new state with an ExplodedNode. + Src.Add(Builder->generateNode(S, state, Pred)); + } + getCheckerManager().runCheckersForLocation(Dst, Src, location, isLoad, S, + *this); + return; + } + + ExplodedNodeSet Src; Src.Add(Pred); + ExplodedNodeSet CheckersV1Dst; + ExplodedNodeSet Tmp; ExplodedNodeSet *PrevSet = &Src; for (CheckersOrdered::iterator I=Checkers.begin(),E=Checkers.end(); I!=E; ++I) { ExplodedNodeSet *CurrSet = 0; if (I+1 == E) - CurrSet = &Dst; + CurrSet = &CheckersV1Dst; else { CurrSet = (PrevSet == &Tmp) ? &Src : &Tmp; CurrSet->clear(); @@ -1957,6 +1999,9 @@ void ExprEngine::evalLocation(ExplodedNodeSet &Dst, const Stmt *S, // Update which NodeSet is the current one. PrevSet = CurrSet; } + + getCheckerManager().runCheckersForLocation(Dst, CheckersV1Dst, location, + isLoad, S, *this); } bool ExprEngine::InlineCall(ExplodedNodeSet &Dst, const CallExpr *CE, @@ -3613,14 +3658,12 @@ void ExprEngine::ViewGraph(bool trim) { const_cast<BugType*>(*I)->FlushReports(BR); // Iterate through the reports and get their nodes. - for (BugReporter::iterator I=BR.begin(), E=BR.end(); I!=E; ++I) { - for (BugType::const_iterator I2=(*I)->begin(), E2=(*I)->end(); - I2!=E2; ++I2) { - const BugReportEquivClass& EQ = *I2; - const BugReport &R = **EQ.begin(); - ExplodedNode *N = const_cast<ExplodedNode*>(R.getErrorNode()); - if (N) Src.push_back(N); - } + for (BugReporter::EQClasses_iterator + EI = BR.EQClasses_begin(), EE = BR.EQClasses_end(); EI != EE; ++EI) { + BugReportEquivClass& EQ = *EI; + const BugReport &R = **EQ.begin(); + ExplodedNode *N = const_cast<ExplodedNode*>(R.getErrorNode()); + if (N) Src.push_back(N); } ViewGraph(&Src[0], &Src[0]+Src.size()); diff --git a/lib/StaticAnalyzer/Checkers/FixedAddressChecker.cpp b/lib/StaticAnalyzer/Checkers/FixedAddressChecker.cpp index fe628a2..d7b27b5 100644 --- a/lib/StaticAnalyzer/Checkers/FixedAddressChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/FixedAddressChecker.cpp @@ -14,31 +14,26 @@ //===----------------------------------------------------------------------===// #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" using namespace clang; using namespace ento; namespace { class FixedAddressChecker - : public CheckerVisitor<FixedAddressChecker> { - BuiltinBug *BT; + : public CheckerV2< check::PreStmt<BinaryOperator> > { + mutable llvm::OwningPtr<BuiltinBug> BT; + public: - FixedAddressChecker() : BT(0) {} - static void *getTag(); - void PreVisitBinaryOperator(CheckerContext &C, const BinaryOperator *B); + void checkPreStmt(const BinaryOperator *B, CheckerContext &C) const; }; } -void *FixedAddressChecker::getTag() { - static int x; - return &x; -} - -void FixedAddressChecker::PreVisitBinaryOperator(CheckerContext &C, - const BinaryOperator *B) { +void FixedAddressChecker::checkPreStmt(const BinaryOperator *B, + CheckerContext &C) const { // Using a fixed address is not portable because that address will probably // not be valid in all environments or platforms. @@ -58,20 +53,16 @@ void FixedAddressChecker::PreVisitBinaryOperator(CheckerContext &C, if (ExplodedNode *N = C.generateNode()) { if (!BT) - BT = new BuiltinBug("Use fixed address", + BT.reset(new BuiltinBug("Use fixed address", "Using a fixed address is not portable because that " "address will probably not be valid in all " - "environments or platforms."); + "environments or platforms.")); RangedBugReport *R = new RangedBugReport(*BT, BT->getDescription(), N); R->addRange(B->getRHS()->getSourceRange()); C.EmitReport(R); } } -static void RegisterFixedAddressChecker(ExprEngine &Eng) { - Eng.registerCheck(new FixedAddressChecker()); -} - void ento::registerFixedAddressChecker(CheckerManager &mgr) { - mgr.addCheckerRegisterFunction(RegisterFixedAddressChecker); + mgr.registerChecker<FixedAddressChecker>(); } diff --git a/lib/StaticAnalyzer/Checkers/IdempotentOperationChecker.cpp b/lib/StaticAnalyzer/Checkers/IdempotentOperationChecker.cpp index f49b125..83d9668 100644 --- a/lib/StaticAnalyzer/Checkers/IdempotentOperationChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/IdempotentOperationChecker.cpp @@ -45,11 +45,13 @@ #include "ClangSACheckers.h" #include "clang/Analysis/CFGStmtMap.h" #include "clang/Analysis/Analyses/PseudoConstantAnalysis.h" +#include "clang/Analysis/Analyses/CFGReachabilityAnalysis.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/BugReporter.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerVisitor.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" #include "clang/AST/Stmt.h" @@ -64,29 +66,29 @@ using namespace ento; namespace { class IdempotentOperationChecker - : public CheckerVisitor<IdempotentOperationChecker> { + : public CheckerV2<check::PreStmt<BinaryOperator>, + check::PostStmt<BinaryOperator>, + check::EndAnalysis> { public: - static void *getTag(); - void PreVisitBinaryOperator(CheckerContext &C, const BinaryOperator *B); - void PostVisitBinaryOperator(CheckerContext &C, const BinaryOperator *B); - void VisitEndAnalysis(ExplodedGraph &G, BugReporter &B, ExprEngine &Eng); + void checkPreStmt(const BinaryOperator *B, CheckerContext &C) const; + void checkPostStmt(const BinaryOperator *B, CheckerContext &C) const; + void checkEndAnalysis(ExplodedGraph &G, BugReporter &B,ExprEngine &Eng) const; private: // Our assumption about a particular operation. enum Assumption { Possible = 0, Impossible, Equal, LHSis1, RHSis1, LHSis0, RHSis0 }; - void UpdateAssumption(Assumption &A, const Assumption &New); + static void UpdateAssumption(Assumption &A, const Assumption &New); // False positive reduction methods static bool isSelfAssign(const Expr *LHS, const Expr *RHS); static bool isUnused(const Expr *E, AnalysisContext *AC); static bool isTruncationExtensionAssignment(const Expr *LHS, const Expr *RHS); - bool pathWasCompletelyAnalyzed(const CFG *cfg, - const CFGBlock *CB, - const CFGStmtMap *CBM, - const CoreEngine &CE); + static bool pathWasCompletelyAnalyzed(AnalysisContext *AC, + const CFGBlock *CB, + const CoreEngine &CE); static bool CanVary(const Expr *Ex, AnalysisContext *AC); static bool isConstantOrPseudoConstant(const DeclRefExpr *DR, @@ -104,46 +106,12 @@ private: }; typedef llvm::DenseMap<const BinaryOperator *, BinaryOperatorData> AssumptionMap; - AssumptionMap hash; - - // A class that performs reachability queries for CFGBlocks. Several internal - // checks in this checker require reachability information. The requests all - // tend to have a common destination, so we lazily do a predecessor search - // from the destination node and cache the results to prevent work - // duplication. - class CFGReachabilityAnalysis { - typedef llvm::BitVector ReachableSet; - typedef llvm::DenseMap<unsigned, ReachableSet> ReachableMap; - ReachableSet analyzed; - ReachableMap reachable; - public: - CFGReachabilityAnalysis(const CFG &cfg) - : analyzed(cfg.getNumBlockIDs(), false) {} - - inline bool isReachable(const CFGBlock *Src, const CFGBlock *Dst); - private: - void MapReachability(const CFGBlock *Dst); - }; - llvm::OwningPtr<CFGReachabilityAnalysis> CRA; + mutable AssumptionMap hash; }; } -void *IdempotentOperationChecker::getTag() { - static int x = 0; - return &x; -} - -static void RegisterIdempotentOperationChecker(ExprEngine &Eng) { - Eng.registerCheck(new IdempotentOperationChecker()); -} - -void ento::registerIdempotentOperationChecker(CheckerManager &mgr) { - mgr.addCheckerRegisterFunction(RegisterIdempotentOperationChecker); -} - -void IdempotentOperationChecker::PreVisitBinaryOperator( - CheckerContext &C, - const BinaryOperator *B) { +void IdempotentOperationChecker::checkPreStmt(const BinaryOperator *B, + CheckerContext &C) const { // Find or create an entry in the hash for this BinaryOperator instance. // If we haven't done a lookup before, it will get default initialized to // 'Possible'. At this stage we do not store the ExplodedNode, as it has not @@ -359,9 +327,8 @@ void IdempotentOperationChecker::PreVisitBinaryOperator( // At the post visit stage, the predecessor ExplodedNode will be the // BinaryOperator that was just created. We use this hook to collect the // ExplodedNode. -void IdempotentOperationChecker::PostVisitBinaryOperator( - CheckerContext &C, - const BinaryOperator *B) { +void IdempotentOperationChecker::checkPostStmt(const BinaryOperator *B, + CheckerContext &C) const { // Add the ExplodedNode we just visited BinaryOperatorData &Data = hash[B]; @@ -376,9 +343,9 @@ void IdempotentOperationChecker::PostVisitBinaryOperator( Data.explodedNodes.Add(C.getPredecessor()); } -void IdempotentOperationChecker::VisitEndAnalysis(ExplodedGraph &G, +void IdempotentOperationChecker::checkEndAnalysis(ExplodedGraph &G, BugReporter &BR, - ExprEngine &Eng) { + ExprEngine &Eng) const { BugType *BT = new BugType("Idempotent operation", "Dead code"); // Iterate over the hash to see if we have any paths with definite // idempotent operations. @@ -397,16 +364,11 @@ void IdempotentOperationChecker::VisitEndAnalysis(ExplodedGraph &G, // If the analyzer did not finish, check to see if we can still emit this // warning if (Eng.hasWorkRemaining()) { - const CFGStmtMap *CBM = CFGStmtMap::Build(AC->getCFG(), - &AC->getParentMap()); - // If we can trace back - if (!pathWasCompletelyAnalyzed(AC->getCFG(), - CBM->getBlock(B), CBM, + if (!pathWasCompletelyAnalyzed(AC, + AC->getCFGStmtMap()->getBlock(B), Eng.getCoreEngine())) continue; - - delete CBM; } // Select the error message and SourceRanges to report. @@ -464,6 +426,8 @@ void IdempotentOperationChecker::VisitEndAnalysis(ExplodedGraph &G, BR.EmitReport(report); } } + + hash.clear(); } // Updates the current assumption given the new assumption @@ -564,13 +528,11 @@ bool IdempotentOperationChecker::isTruncationExtensionAssignment( // Returns false if a path to this block was not completely analyzed, or true // otherwise. bool -IdempotentOperationChecker::pathWasCompletelyAnalyzed(const CFG *cfg, +IdempotentOperationChecker::pathWasCompletelyAnalyzed(AnalysisContext *AC, const CFGBlock *CB, - const CFGStmtMap *CBM, const CoreEngine &CE) { - if (!CRA.get()) - CRA.reset(new CFGReachabilityAnalysis(*cfg)); + CFGReachabilityAnalysis *CRA = AC->getCFGReachablityAnalysis(); // Test for reachability from any aborted blocks to this block typedef CoreEngine::BlocksAborted::const_iterator AbortedIterator; @@ -621,14 +583,14 @@ IdempotentOperationChecker::pathWasCompletelyAnalyzed(const CFG *cfg, return CRA.isReachable(B, TargetBlock); } }; - VisitWL visitWL(CBM, CB, *CRA.get()); + VisitWL visitWL(AC->getCFGStmtMap(), CB, *CRA); // Were there any items in the worklist that could potentially reach // this block? if (CE.getWorkList()->visitItemsInWorkList(visitWL)) return false; // Verify that this block is reachable from the entry block - if (!CRA->isReachable(&cfg->getEntry(), CB)) + if (!CRA->isReachable(&AC->getCFG()->getEntry(), CB)) return false; // If we get to this point, there is no connection to the entry block or an @@ -766,57 +728,7 @@ bool IdempotentOperationChecker::containsNonLocalVarDecl(const Stmt *S) { return false; } -bool IdempotentOperationChecker::CFGReachabilityAnalysis::isReachable( - const CFGBlock *Src, - const CFGBlock *Dst) { - const unsigned DstBlockID = Dst->getBlockID(); - // If we haven't analyzed the destination node, run the analysis now - if (!analyzed[DstBlockID]) { - MapReachability(Dst); - analyzed[DstBlockID] = true; - } - - // Return the cached result - return reachable[DstBlockID][Src->getBlockID()]; -} - -// Maps reachability to a common node by walking the predecessors of the -// destination node. -void IdempotentOperationChecker::CFGReachabilityAnalysis::MapReachability( - const CFGBlock *Dst) { - - llvm::SmallVector<const CFGBlock *, 11> worklist; - llvm::BitVector visited(analyzed.size()); - - ReachableSet &DstReachability = reachable[Dst->getBlockID()]; - DstReachability.resize(analyzed.size(), false); - - // Start searching from the destination node, since we commonly will perform - // multiple queries relating to a destination node. - worklist.push_back(Dst); - bool firstRun = true; - - while (!worklist.empty()) { - const CFGBlock *block = worklist.back(); - worklist.pop_back(); - - if (visited[block->getBlockID()]) - continue; - visited[block->getBlockID()] = true; - - // Update reachability information for this node -> Dst - if (!firstRun) { - // Don't insert Dst -> Dst unless it was a predecessor of itself - DstReachability[block->getBlockID()] = true; - } - else - firstRun = false; - - // Add the predecessors to the worklist. - for (CFGBlock::const_pred_iterator i = block->pred_begin(), - e = block->pred_end(); i != e; ++i) { - worklist.push_back(*i); - } - } +void ento::registerIdempotentOperationChecker(CheckerManager &mgr) { + mgr.registerChecker<IdempotentOperationChecker>(); } diff --git a/lib/StaticAnalyzer/Checkers/InternalChecks.h b/lib/StaticAnalyzer/Checkers/InternalChecks.h index e855386..e7c38ee 100644 --- a/lib/StaticAnalyzer/Checkers/InternalChecks.h +++ b/lib/StaticAnalyzer/Checkers/InternalChecks.h @@ -23,16 +23,13 @@ class ExprEngine; // Foundational checks that handle basic semantics. void RegisterAdjustedReturnValueChecker(ExprEngine &Eng); -void RegisterArrayBoundChecker(ExprEngine &Eng); void RegisterArrayBoundCheckerV2(ExprEngine &Eng); void RegisterAttrNonNullChecker(ExprEngine &Eng); void RegisterBuiltinFunctionChecker(ExprEngine &Eng); void RegisterCallAndMessageChecker(ExprEngine &Eng); -void RegisterCastSizeChecker(ExprEngine &Eng); void RegisterDereferenceChecker(ExprEngine &Eng); void RegisterDivZeroChecker(ExprEngine &Eng); void RegisterNoReturnFunctionChecker(ExprEngine &Eng); -void RegisterReturnPointerRangeChecker(ExprEngine &Eng); void RegisterReturnUndefChecker(ExprEngine &Eng); void RegisterUndefBranchChecker(ExprEngine &Eng); void RegisterUndefCapturedBlockVarChecker(ExprEngine &Eng); diff --git a/lib/StaticAnalyzer/Checkers/MacOSXAPIChecker.cpp b/lib/StaticAnalyzer/Checkers/MacOSXAPIChecker.cpp index 358be12..d70c65a 100644 --- a/lib/StaticAnalyzer/Checkers/MacOSXAPIChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/MacOSXAPIChecker.cpp @@ -16,11 +16,12 @@ //===----------------------------------------------------------------------===// #include "ClangSACheckers.h" -#include "clang/Basic/TargetInfo.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 "clang/Basic/TargetInfo.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/Support/raw_ostream.h" @@ -29,31 +30,26 @@ using namespace clang; using namespace ento; namespace { -class MacOSXAPIChecker : public CheckerVisitor<MacOSXAPIChecker> { +class MacOSXAPIChecker : public CheckerV2< check::PreStmt<CallExpr> > { enum SubChecks { DispatchOnce = 0, DispatchOnceF, NumChecks }; - BugType *BTypes[NumChecks]; + mutable BugType *BTypes[NumChecks]; public: MacOSXAPIChecker() { memset(BTypes, 0, sizeof(*BTypes) * NumChecks); } - static void *getTag() { static unsigned tag = 0; return &tag; } + ~MacOSXAPIChecker() { + for (unsigned i=0; i != NumChecks; ++i) + delete BTypes[i]; + } - void PreVisitCallExpr(CheckerContext &C, const CallExpr *CE); + void checkPreStmt(const CallExpr *CE, CheckerContext &C) const; }; } //end anonymous namespace -static void RegisterMacOSXAPIChecker(ExprEngine &Eng) { - Eng.registerCheck(new MacOSXAPIChecker()); -} - -void ento::registerMacOSXAPIChecker(CheckerManager &mgr) { - mgr.addCheckerRegisterFunction(RegisterMacOSXAPIChecker); -} - //===----------------------------------------------------------------------===// // dispatch_once and dispatch_once_f //===----------------------------------------------------------------------===// @@ -121,7 +117,8 @@ namespace { }; } // end anonymous namespace -void MacOSXAPIChecker::PreVisitCallExpr(CheckerContext &C, const CallExpr *CE) { +void MacOSXAPIChecker::checkPreStmt(const CallExpr *CE, + CheckerContext &C) const { // FIXME: Mostly copy and paste from UnixAPIChecker. Should refactor. const GRState *state = C.getState(); const Expr *Callee = CE->getCallee(); @@ -144,3 +141,11 @@ void MacOSXAPIChecker::PreVisitCallExpr(CheckerContext &C, const CallExpr *CE) { SC.run(C, CE, FI); } + +//===----------------------------------------------------------------------===// +// Registration. +//===----------------------------------------------------------------------===// + +void ento::registerMacOSXAPIChecker(CheckerManager &mgr) { + mgr.registerChecker<MacOSXAPIChecker>(); +} diff --git a/lib/StaticAnalyzer/Checkers/MallocChecker.cpp b/lib/StaticAnalyzer/Checkers/MallocChecker.cpp index 9d3a887..794740a 100644 --- a/lib/StaticAnalyzer/Checkers/MallocChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/MallocChecker.cpp @@ -599,7 +599,7 @@ void MallocChecker::evalEndPath(EndOfFunctionNodeBuilder &B, void *tag, for (RegionStateTy::iterator I = M.begin(), E = M.end(); I != E; ++I) { RefState RS = I->second; if (RS.isAllocated()) { - ExplodedNode *N = B.generateNode(state, tag, B.getPredecessor()); + ExplodedNode *N = B.generateNode(state); if (N) { if (!BT_Leak) BT_Leak = new BuiltinBug("Memory leak", diff --git a/lib/StaticAnalyzer/Checkers/NSAutoreleasePoolChecker.cpp b/lib/StaticAnalyzer/Checkers/NSAutoreleasePoolChecker.cpp index 9fb89d7..fed6a99 100644 --- a/lib/StaticAnalyzer/Checkers/NSAutoreleasePoolChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/NSAutoreleasePoolChecker.cpp @@ -16,10 +16,11 @@ //===----------------------------------------------------------------------===// #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/BugReporter.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerVisitor.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/Decl.h" @@ -28,39 +29,18 @@ using namespace ento; namespace { class NSAutoreleasePoolChecker - : public CheckerVisitor<NSAutoreleasePoolChecker> { + : public CheckerV2<check::PreObjCMessage> { - Selector releaseS; + mutable Selector releaseS; public: - NSAutoreleasePoolChecker(Selector release_s) : releaseS(release_s) {} - - static void *getTag() { - static int x = 0; - return &x; - } - - void preVisitObjCMessage(CheckerContext &C, ObjCMessage msg); + void checkPreObjCMessage(ObjCMessage msg, CheckerContext &C) const; }; } // end anonymous namespace - -static void RegisterNSAutoreleasePoolChecker(ExprEngine &Eng) { - ASTContext &Ctx = Eng.getContext(); - if (Ctx.getLangOptions().getGCMode() != LangOptions::NonGC) { - Eng.registerCheck(new NSAutoreleasePoolChecker(GetNullarySelector("release", - Ctx))); - } -} - -void ento::registerNSAutoreleasePoolChecker(CheckerManager &mgr) { - mgr.addCheckerRegisterFunction(RegisterNSAutoreleasePoolChecker); -} - -void -NSAutoreleasePoolChecker::preVisitObjCMessage(CheckerContext &C, - ObjCMessage msg) { +void NSAutoreleasePoolChecker::checkPreObjCMessage(ObjCMessage msg, + CheckerContext &C) const { const Expr *receiver = msg.getInstanceReceiver(); if (!receiver) @@ -78,7 +58,9 @@ NSAutoreleasePoolChecker::preVisitObjCMessage(CheckerContext &C, return; if (!OD->getIdentifier()->getName().equals("NSAutoreleasePool")) return; - + + if (releaseS.isNull()) + releaseS = GetNullarySelector("release", C.getASTContext()); // Sending 'release' message? if (msg.getSelector() != releaseS) return; @@ -90,3 +72,8 @@ NSAutoreleasePoolChecker::preVisitObjCMessage(CheckerContext &C, "Use -drain instead of -release when using NSAutoreleasePool " "and garbage collection", R.getBegin(), &R, 1); } + +void ento::registerNSAutoreleasePoolChecker(CheckerManager &mgr) { + if (mgr.getLangOptions().getGCMode() != LangOptions::NonGC) + mgr.registerChecker<NSAutoreleasePoolChecker>(); +} diff --git a/lib/StaticAnalyzer/Checkers/ObjCAtSyncChecker.cpp b/lib/StaticAnalyzer/Checkers/ObjCAtSyncChecker.cpp index 7d440ab..7746719 100644 --- a/lib/StaticAnalyzer/Checkers/ObjCAtSyncChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/ObjCAtSyncChecker.cpp @@ -13,39 +13,29 @@ //===----------------------------------------------------------------------===// #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/Checkers/DereferenceChecker.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerVisitor.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" using namespace clang; using namespace ento; namespace { -class ObjCAtSyncChecker : public CheckerVisitor<ObjCAtSyncChecker> { - BuiltinBug *BT_null; - BuiltinBug *BT_undef; +class ObjCAtSyncChecker + : public CheckerV2< check::PreStmt<ObjCAtSynchronizedStmt> > { + mutable llvm::OwningPtr<BuiltinBug> BT_null; + mutable llvm::OwningPtr<BuiltinBug> BT_undef; + public: - ObjCAtSyncChecker() : BT_null(0), BT_undef(0) {} - static void *getTag() { static int tag = 0; return &tag; } - void PreVisitObjCAtSynchronizedStmt(CheckerContext &C, - const ObjCAtSynchronizedStmt *S); + void checkPreStmt(const ObjCAtSynchronizedStmt *S, CheckerContext &C) const; }; } // end anonymous namespace -static void RegisterObjCAtSyncChecker(ExprEngine &Eng) { - // @synchronized is an Objective-C 2 feature. - if (Eng.getContext().getLangOptions().ObjC2) - Eng.registerCheck(new ObjCAtSyncChecker()); -} - -void ento::registerObjCAtSyncChecker(CheckerManager &mgr) { - mgr.addCheckerRegisterFunction(RegisterObjCAtSyncChecker); -} - -void ObjCAtSyncChecker::PreVisitObjCAtSynchronizedStmt(CheckerContext &C, - const ObjCAtSynchronizedStmt *S) { +void ObjCAtSyncChecker::checkPreStmt(const ObjCAtSynchronizedStmt *S, + CheckerContext &C) const { const Expr *Ex = S->getSynchExpr(); const GRState *state = C.getState(); @@ -55,8 +45,8 @@ void ObjCAtSyncChecker::PreVisitObjCAtSynchronizedStmt(CheckerContext &C, if (isa<UndefinedVal>(V)) { if (ExplodedNode *N = C.generateSink()) { if (!BT_undef) - BT_undef = new BuiltinBug("Uninitialized value used as mutex " - "for @synchronized"); + BT_undef.reset(new BuiltinBug("Uninitialized value used as mutex " + "for @synchronized")); EnhancedBugReport *report = new EnhancedBugReport(*BT_undef, BT_undef->getDescription(), N); report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, Ex); @@ -78,8 +68,8 @@ void ObjCAtSyncChecker::PreVisitObjCAtSynchronizedStmt(CheckerContext &C, // a null mutex just means no synchronization occurs. if (ExplodedNode *N = C.generateNode(nullState)) { if (!BT_null) - BT_null = new BuiltinBug("Nil value used as mutex for @synchronized() " - "(no synchronization will occur)"); + BT_null.reset(new BuiltinBug("Nil value used as mutex for @synchronized() " + "(no synchronization will occur)")); EnhancedBugReport *report = new EnhancedBugReport(*BT_null, BT_null->getDescription(), N); report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, @@ -98,3 +88,7 @@ void ObjCAtSyncChecker::PreVisitObjCAtSynchronizedStmt(CheckerContext &C, C.addTransition(notNullState); } +void ento::registerObjCAtSyncChecker(CheckerManager &mgr) { + if (mgr.getLangOptions().ObjC2) + mgr.registerChecker<ObjCAtSyncChecker>(); +} diff --git a/lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp b/lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp index 4f247ea..5f32bb8 100644 --- a/lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp @@ -47,8 +47,9 @@ // http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/ObjectiveC/Articles/ocAllocInit.html #include "ClangSACheckers.h" +#include "clang/StaticAnalyzer/Core/CheckerV2.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerVisitor.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" #include "clang/StaticAnalyzer/Core/PathSensitive/GRStateTrait.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/Analysis/DomainSpecific/CocoaConventions.h" @@ -63,45 +64,23 @@ static bool isInitMessage(const ObjCMessage &msg); static bool isSelfVar(SVal location, CheckerContext &C); namespace { -enum SelfFlagEnum { - /// \brief No flag set. - SelfFlag_None = 0x0, - /// \brief Value came from 'self'. - SelfFlag_Self = 0x1, - /// \brief Value came from the result of an initializer (e.g. [super init]). - SelfFlag_InitRes = 0x2 -}; -} - -namespace { -class ObjCSelfInitChecker : public CheckerVisitor<ObjCSelfInitChecker> { - /// \brief A call receiving a reference to 'self' invalidates the object that - /// 'self' contains. This field keeps the "self flags" assigned to the 'self' - /// object before the call and assign them to the new object that 'self' - /// points to after the call. - SelfFlagEnum preCallSelfFlags; - +class ObjCSelfInitChecker : public CheckerV2< + check::PostObjCMessage, + check::PostStmt<ObjCIvarRefExpr>, + check::PreStmt<ReturnStmt>, + check::PreStmt<CallExpr>, + check::PostStmt<CallExpr>, + check::Location > { public: - static void *getTag() { static int tag = 0; return &tag; } - void postVisitObjCMessage(CheckerContext &C, ObjCMessage msg); - void PostVisitObjCIvarRefExpr(CheckerContext &C, const ObjCIvarRefExpr *E); - void PreVisitReturnStmt(CheckerContext &C, const ReturnStmt *S); - void PreVisitGenericCall(CheckerContext &C, const CallExpr *CE); - void PostVisitGenericCall(CheckerContext &C, const CallExpr *CE); - virtual void visitLocation(CheckerContext &C, const Stmt *S, SVal location, - bool isLoad); + void checkPostObjCMessage(ObjCMessage msg, CheckerContext &C) const; + void checkPostStmt(const ObjCIvarRefExpr *E, CheckerContext &C) const; + void checkPreStmt(const ReturnStmt *S, CheckerContext &C) const; + void checkPreStmt(const CallExpr *CE, CheckerContext &C) const; + void checkPostStmt(const CallExpr *CE, CheckerContext &C) const; + void checkLocation(SVal location, bool isLoad, CheckerContext &C) const; }; } // end anonymous namespace -static void RegisterObjCSelfInitChecker(ExprEngine &Eng) { - if (Eng.getContext().getLangOptions().ObjC1) - Eng.registerCheck(new ObjCSelfInitChecker()); -} - -void ento::registerObjCSelfInitChecker(CheckerManager &mgr) { - mgr.addCheckerRegisterFunction(RegisterObjCSelfInitChecker); -} - namespace { class InitSelfBug : public BugType { @@ -113,22 +92,40 @@ public: } // end anonymous namespace +namespace { +enum SelfFlagEnum { + /// \brief No flag set. + SelfFlag_None = 0x0, + /// \brief Value came from 'self'. + SelfFlag_Self = 0x1, + /// \brief Value came from the result of an initializer (e.g. [super init]). + SelfFlag_InitRes = 0x2 +}; +} + typedef llvm::ImmutableMap<SymbolRef, unsigned> SelfFlag; namespace { struct CalledInit {}; } +namespace { struct PreCallSelfFlags {}; } namespace clang { namespace ento { template<> struct GRStateTrait<SelfFlag> : public GRStatePartialTrait<SelfFlag> { - static void* GDMIndex() { - static int index = 0; - return &index; - } + static void* GDMIndex() { static int index = 0; return &index; } }; template <> struct GRStateTrait<CalledInit> : public GRStatePartialTrait<bool> { static void *GDMIndex() { static int index = 0; return &index; } }; + + /// \brief A call receiving a reference to 'self' invalidates the object that + /// 'self' contains. This keeps the "self flags" assigned to the 'self' + /// object before the call so we can assign them to the new object that 'self' + /// points to after the call. + template <> + struct GRStateTrait<PreCallSelfFlags> : public GRStatePartialTrait<unsigned> { + static void *GDMIndex() { static int index = 0; return &index; } + }; } } @@ -188,8 +185,8 @@ static void checkForInvalidSelf(const Expr *E, CheckerContext &C, C.EmitReport(report); } -void ObjCSelfInitChecker::postVisitObjCMessage(CheckerContext &C, - ObjCMessage msg) { +void ObjCSelfInitChecker::checkPostObjCMessage(ObjCMessage msg, + CheckerContext &C) const { // When encountering a message that does initialization (init rule), // tag the return value so that we know later on that if self has this value // then it is properly initialized. @@ -219,8 +216,8 @@ void ObjCSelfInitChecker::postVisitObjCMessage(CheckerContext &C, // fails. } -void ObjCSelfInitChecker::PostVisitObjCIvarRefExpr(CheckerContext &C, - const ObjCIvarRefExpr *E) { +void ObjCSelfInitChecker::checkPostStmt(const ObjCIvarRefExpr *E, + CheckerContext &C) const { // FIXME: A callback should disable checkers at the start of functions. if (!shouldRunOnFunctionOrMethod(dyn_cast<NamedDecl>( C.getCurrentAnalysisContext()->getDecl()))) @@ -231,8 +228,8 @@ void ObjCSelfInitChecker::PostVisitObjCIvarRefExpr(CheckerContext &C, "'[(super or self) init...]'"); } -void ObjCSelfInitChecker::PreVisitReturnStmt(CheckerContext &C, - const ReturnStmt *S) { +void ObjCSelfInitChecker::checkPreStmt(const ReturnStmt *S, + CheckerContext &C) const { // FIXME: A callback should disable checkers at the start of functions. if (!shouldRunOnFunctionOrMethod(dyn_cast<NamedDecl>( C.getCurrentAnalysisContext()->getDecl()))) @@ -259,40 +256,46 @@ void ObjCSelfInitChecker::PreVisitReturnStmt(CheckerContext &C, // Until we can use inter-procedural analysis, in such a call, transfer the // SelfFlags to the result of the call. -void ObjCSelfInitChecker::PreVisitGenericCall(CheckerContext &C, - const CallExpr *CE) { +void ObjCSelfInitChecker::checkPreStmt(const CallExpr *CE, + CheckerContext &C) const { const GRState *state = C.getState(); for (CallExpr::const_arg_iterator I = CE->arg_begin(), E = CE->arg_end(); I != E; ++I) { SVal argV = state->getSVal(*I); if (isSelfVar(argV, C)) { - preCallSelfFlags = getSelfFlags(state->getSVal(cast<Loc>(argV)), C); + unsigned selfFlags = getSelfFlags(state->getSVal(cast<Loc>(argV)), C); + C.addTransition(state->set<PreCallSelfFlags>(selfFlags)); return; } else if (hasSelfFlag(argV, SelfFlag_Self, C)) { - preCallSelfFlags = getSelfFlags(argV, C); + unsigned selfFlags = getSelfFlags(argV, C); + C.addTransition(state->set<PreCallSelfFlags>(selfFlags)); return; } } } -void ObjCSelfInitChecker::PostVisitGenericCall(CheckerContext &C, - const CallExpr *CE) { +void ObjCSelfInitChecker::checkPostStmt(const CallExpr *CE, + CheckerContext &C) const { const GRState *state = C.getState(); for (CallExpr::const_arg_iterator I = CE->arg_begin(), E = CE->arg_end(); I != E; ++I) { SVal argV = state->getSVal(*I); if (isSelfVar(argV, C)) { - addSelfFlag(state, state->getSVal(cast<Loc>(argV)), preCallSelfFlags, C); + SelfFlagEnum prevFlags = (SelfFlagEnum)state->get<PreCallSelfFlags>(); + state = state->remove<PreCallSelfFlags>(); + addSelfFlag(state, state->getSVal(cast<Loc>(argV)), prevFlags, C); return; } else if (hasSelfFlag(argV, SelfFlag_Self, C)) { - addSelfFlag(state, state->getSVal(CE), preCallSelfFlags, C); + SelfFlagEnum prevFlags = (SelfFlagEnum)state->get<PreCallSelfFlags>(); + state = state->remove<PreCallSelfFlags>(); + addSelfFlag(state, state->getSVal(CE), prevFlags, C); return; } } } -void ObjCSelfInitChecker::visitLocation(CheckerContext &C, const Stmt *S, - SVal location, bool isLoad) { +void ObjCSelfInitChecker::checkLocation(SVal location, bool isLoad, + CheckerContext &C) const { // Tag the result of a load from 'self' so that we can easily know that the // value is the object that 'self' points to. const GRState *state = C.getState(); @@ -354,3 +357,11 @@ static bool isInitializationMethod(const ObjCMethodDecl *MD) { static bool isInitMessage(const ObjCMessage &msg) { return cocoa::deriveNamingConvention(msg.getSelector()) == cocoa::InitRule; } + +//===----------------------------------------------------------------------===// +// Registration. +//===----------------------------------------------------------------------===// + +void ento::registerObjCSelfInitChecker(CheckerManager &mgr) { + mgr.registerChecker<ObjCSelfInitChecker>(); +} diff --git a/lib/StaticAnalyzer/Checkers/PointerArithChecker.cpp b/lib/StaticAnalyzer/Checkers/PointerArithChecker.cpp index 741e48b..034a2aa 100644 --- a/lib/StaticAnalyzer/Checkers/PointerArithChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/PointerArithChecker.cpp @@ -13,31 +13,26 @@ //===----------------------------------------------------------------------===// #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" using namespace clang; using namespace ento; namespace { class PointerArithChecker - : public CheckerVisitor<PointerArithChecker> { - BuiltinBug *BT; + : public CheckerV2< check::PreStmt<BinaryOperator> > { + mutable llvm::OwningPtr<BuiltinBug> BT; + public: - PointerArithChecker() : BT(0) {} - static void *getTag(); - void PreVisitBinaryOperator(CheckerContext &C, const BinaryOperator *B); + void checkPreStmt(const BinaryOperator *B, CheckerContext &C) const; }; } -void *PointerArithChecker::getTag() { - static int x; - return &x; -} - -void PointerArithChecker::PreVisitBinaryOperator(CheckerContext &C, - const BinaryOperator *B) { +void PointerArithChecker::checkPreStmt(const BinaryOperator *B, + CheckerContext &C) const { if (B->getOpcode() != BO_Sub && B->getOpcode() != BO_Add) return; @@ -57,10 +52,10 @@ void PointerArithChecker::PreVisitBinaryOperator(CheckerContext &C, if (ExplodedNode *N = C.generateNode()) { if (!BT) - BT = new BuiltinBug("Dangerous pointer arithmetic", + BT.reset(new BuiltinBug("Dangerous pointer arithmetic", "Pointer arithmetic done on non-array variables " "means reliance on memory layout, which is " - "dangerous."); + "dangerous.")); RangedBugReport *R = new RangedBugReport(*BT, BT->getDescription(), N); R->addRange(B->getSourceRange()); C.EmitReport(R); @@ -68,10 +63,6 @@ void PointerArithChecker::PreVisitBinaryOperator(CheckerContext &C, } } -static void RegisterPointerArithChecker(ExprEngine &Eng) { - Eng.registerCheck(new PointerArithChecker()); -} - void ento::registerPointerArithChecker(CheckerManager &mgr) { - mgr.addCheckerRegisterFunction(RegisterPointerArithChecker); + mgr.registerChecker<PointerArithChecker>(); } diff --git a/lib/StaticAnalyzer/Checkers/PointerSubChecker.cpp b/lib/StaticAnalyzer/Checkers/PointerSubChecker.cpp index f28fe9a..bf85b95 100644 --- a/lib/StaticAnalyzer/Checkers/PointerSubChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/PointerSubChecker.cpp @@ -14,31 +14,26 @@ //===----------------------------------------------------------------------===// #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" using namespace clang; using namespace ento; namespace { class PointerSubChecker - : public CheckerVisitor<PointerSubChecker> { - BuiltinBug *BT; + : public CheckerV2< check::PreStmt<BinaryOperator> > { + mutable llvm::OwningPtr<BuiltinBug> BT; + public: - PointerSubChecker() : BT(0) {} - static void *getTag(); - void PreVisitBinaryOperator(CheckerContext &C, const BinaryOperator *B); + void checkPreStmt(const BinaryOperator *B, CheckerContext &C) const; }; } -void *PointerSubChecker::getTag() { - static int x; - return &x; -} - -void PointerSubChecker::PreVisitBinaryOperator(CheckerContext &C, - const BinaryOperator *B) { +void PointerSubChecker::checkPreStmt(const BinaryOperator *B, + CheckerContext &C) const { // When doing pointer subtraction, if the two pointers do not point to the // same memory chunk, emit a warning. if (B->getOpcode() != BO_Sub) @@ -66,19 +61,15 @@ void PointerSubChecker::PreVisitBinaryOperator(CheckerContext &C, if (ExplodedNode *N = C.generateNode()) { if (!BT) - BT = new BuiltinBug("Pointer subtraction", + BT.reset(new BuiltinBug("Pointer subtraction", "Subtraction of two pointers that do not point to " - "the same memory chunk may cause incorrect result."); + "the same memory chunk may cause incorrect result.")); RangedBugReport *R = new RangedBugReport(*BT, BT->getDescription(), N); R->addRange(B->getSourceRange()); C.EmitReport(R); } } -static void RegisterPointerSubChecker(ExprEngine &Eng) { - Eng.registerCheck(new PointerSubChecker()); -} - void ento::registerPointerSubChecker(CheckerManager &mgr) { - mgr.addCheckerRegisterFunction(RegisterPointerSubChecker); + mgr.registerChecker<PointerSubChecker>(); } diff --git a/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp b/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp index 34c095f..6c6901f 100644 --- a/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp @@ -13,8 +13,9 @@ //===----------------------------------------------------------------------===// #include "ClangSACheckers.h" +#include "clang/StaticAnalyzer/Core/CheckerV2.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerVisitor.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" #include "clang/StaticAnalyzer/Core/PathSensitive/GRStateTrait.h" #include "llvm/ADT/ImmutableSet.h" @@ -24,21 +25,15 @@ using namespace ento; namespace { class PthreadLockChecker - : public CheckerVisitor<PthreadLockChecker> { - BugType *BT; + : public CheckerV2< check::PostStmt<CallExpr> > { public: - PthreadLockChecker() : BT(0) {} - static void *getTag() { - static int x = 0; - return &x; - } - void PostVisitCallExpr(CheckerContext &C, const CallExpr *CE); + void checkPostStmt(const CallExpr *CE, CheckerContext &C) const; void AcquireLock(CheckerContext &C, const CallExpr *CE, - SVal lock, bool isTryLock); + SVal lock, bool isTryLock) const; void ReleaseLock(CheckerContext &C, const CallExpr *CE, - SVal lock); + SVal lock) const; }; } // end anonymous namespace @@ -49,22 +44,14 @@ namespace clang { namespace ento { template <> struct GRStateTrait<LockSet> : public GRStatePartialTrait<llvm::ImmutableSet<const MemRegion*> > { - static void* GDMIndex() { return PthreadLockChecker::getTag(); } + static void* GDMIndex() { static int x = 0; return &x; } }; } // end GR namespace } // end clang namespace -static void RegisterPthreadLockChecker(ExprEngine &Eng) { - Eng.registerCheck(new PthreadLockChecker()); -} - -void ento::registerPthreadLockChecker(CheckerManager &mgr) { - mgr.addCheckerRegisterFunction(RegisterPthreadLockChecker); -} - -void PthreadLockChecker::PostVisitCallExpr(CheckerContext &C, - const CallExpr *CE) { +void PthreadLockChecker::checkPostStmt(const CallExpr *CE, + CheckerContext &C) const { const GRState *state = C.getState(); const Expr *Callee = CE->getCallee(); const FunctionTextRegion *R = @@ -96,7 +83,7 @@ void PthreadLockChecker::PostVisitCallExpr(CheckerContext &C, } void PthreadLockChecker::AcquireLock(CheckerContext &C, const CallExpr *CE, - SVal lock, bool isTryLock) { + SVal lock, bool isTryLock) const { const MemRegion *lockR = lock.getAsRegion(); if (!lockR) @@ -132,7 +119,7 @@ void PthreadLockChecker::AcquireLock(CheckerContext &C, const CallExpr *CE, } void PthreadLockChecker::ReleaseLock(CheckerContext &C, const CallExpr *CE, - SVal lock) { + SVal lock) const { const MemRegion *lockR = lock.getAsRegion(); if (!lockR) @@ -150,3 +137,7 @@ void PthreadLockChecker::ReleaseLock(CheckerContext &C, const CallExpr *CE, C.addTransition(C.generateNode(CE, unlockState)); } + +void ento::registerPthreadLockChecker(CheckerManager &mgr) { + mgr.registerChecker<PthreadLockChecker>(); +} diff --git a/lib/StaticAnalyzer/Checkers/ReturnPointerRangeChecker.cpp b/lib/StaticAnalyzer/Checkers/ReturnPointerRangeChecker.cpp index 838a00f..2985156 100644 --- a/lib/StaticAnalyzer/Checkers/ReturnPointerRangeChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/ReturnPointerRangeChecker.cpp @@ -12,9 +12,11 @@ // //===----------------------------------------------------------------------===// -#include "InternalChecks.h" +#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/ExprEngine.h" using namespace clang; @@ -22,25 +24,15 @@ using namespace ento; namespace { class ReturnPointerRangeChecker : - public CheckerVisitor<ReturnPointerRangeChecker> { - BuiltinBug *BT; + public CheckerV2< check::PreStmt<ReturnStmt> > { + mutable llvm::OwningPtr<BuiltinBug> BT; public: - ReturnPointerRangeChecker() : BT(0) {} - static void *getTag(); - void PreVisitReturnStmt(CheckerContext &C, const ReturnStmt *RS); + void checkPreStmt(const ReturnStmt *RS, CheckerContext &C) const; }; } -void ento::RegisterReturnPointerRangeChecker(ExprEngine &Eng) { - Eng.registerCheck(new ReturnPointerRangeChecker()); -} - -void *ReturnPointerRangeChecker::getTag() { - static int x = 0; return &x; -} - -void ReturnPointerRangeChecker::PreVisitReturnStmt(CheckerContext &C, - const ReturnStmt *RS) { +void ReturnPointerRangeChecker::checkPreStmt(const ReturnStmt *RS, + CheckerContext &C) const { const GRState *state = C.getState(); const Expr *RetE = RS->getRetValue(); @@ -77,9 +69,9 @@ void ReturnPointerRangeChecker::PreVisitReturnStmt(CheckerContext &C, // FIXME: This bug correspond to CWE-466. Eventually we should have bug // types explicitly reference such exploit categories (when applicable). if (!BT) - BT = new BuiltinBug("Return of pointer value outside of expected range", + BT.reset(new BuiltinBug("Return of pointer value outside of expected range", "Returned pointer value points outside the original object " - "(potential buffer overflow)"); + "(potential buffer overflow)")); // FIXME: It would be nice to eventually make this diagnostic more clear, // e.g., by referencing the original declaration or by saying *why* this @@ -93,3 +85,7 @@ void ReturnPointerRangeChecker::PreVisitReturnStmt(CheckerContext &C, C.EmitReport(report); } } + +void ento::registerReturnPointerRangeChecker(CheckerManager &mgr) { + mgr.registerChecker<ReturnPointerRangeChecker>(); +} diff --git a/lib/StaticAnalyzer/Checkers/StackAddrLeakChecker.cpp b/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp index 363f404..6a9a37d 100644 --- a/lib/StaticAnalyzer/Checkers/StackAddrLeakChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp @@ -1,4 +1,4 @@ -//=== StackAddrLeakChecker.cpp ------------------------------------*- C++ -*--// +//=== StackAddrEscapeChecker.cpp ----------------------------------*- C++ -*--// // // The LLVM Compiler Infrastructure // @@ -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/GRState.h" #include "clang/Basic/SourceManager.h" #include "llvm/ADT/SmallString.h" @@ -23,34 +24,23 @@ using namespace clang; using namespace ento; namespace { -class StackAddrLeakChecker : public CheckerVisitor<StackAddrLeakChecker> { - BuiltinBug *BT_stackleak; - BuiltinBug *BT_returnstack; +class StackAddrEscapeChecker : public CheckerV2< check::PreStmt<ReturnStmt>, + check::EndPath > { + mutable llvm::OwningPtr<BuiltinBug> BT_stackleak; + mutable llvm::OwningPtr<BuiltinBug> BT_returnstack; public: - StackAddrLeakChecker() : BT_stackleak(0), BT_returnstack(0) {} - static void *getTag() { - static int x; - return &x; - } - void PreVisitReturnStmt(CheckerContext &C, const ReturnStmt *RS); - void evalEndPath(EndOfFunctionNodeBuilder &B, void *tag, ExprEngine &Eng); + void checkPreStmt(const ReturnStmt *RS, CheckerContext &C) const; + void checkEndPath(EndOfFunctionNodeBuilder &B, ExprEngine &Eng) const; private: - void EmitStackError(CheckerContext &C, const MemRegion *R, const Expr *RetE); - SourceRange GenName(llvm::raw_ostream &os, const MemRegion *R, - SourceManager &SM); + void EmitStackError(CheckerContext &C, const MemRegion *R, + const Expr *RetE) const; + static SourceRange GenName(llvm::raw_ostream &os, const MemRegion *R, + SourceManager &SM); }; } -static void RegisterStackAddrLeakChecker(ExprEngine &Eng) { - Eng.registerCheck(new StackAddrLeakChecker()); -} - -void ento::registerStackAddrLeakChecker(CheckerManager &mgr) { - mgr.addCheckerRegisterFunction(RegisterStackAddrLeakChecker); -} - -SourceRange StackAddrLeakChecker::GenName(llvm::raw_ostream &os, +SourceRange StackAddrEscapeChecker::GenName(llvm::raw_ostream &os, const MemRegion *R, SourceManager &SM) { // Get the base region, stripping away fields and elements. @@ -93,15 +83,16 @@ SourceRange StackAddrLeakChecker::GenName(llvm::raw_ostream &os, return range; } -void StackAddrLeakChecker::EmitStackError(CheckerContext &C, const MemRegion *R, - const Expr *RetE) { +void StackAddrEscapeChecker::EmitStackError(CheckerContext &C, const MemRegion *R, + const Expr *RetE) const { ExplodedNode *N = C.generateSink(); if (!N) return; if (!BT_returnstack) - BT_returnstack=new BuiltinBug("Return of address to stack-allocated memory"); + BT_returnstack.reset( + new BuiltinBug("Return of address to stack-allocated memory")); // Generate a report for this bug. llvm::SmallString<512> buf; @@ -116,8 +107,8 @@ void StackAddrLeakChecker::EmitStackError(CheckerContext &C, const MemRegion *R, C.EmitReport(report); } -void StackAddrLeakChecker::PreVisitReturnStmt(CheckerContext &C, - const ReturnStmt *RS) { +void StackAddrEscapeChecker::checkPreStmt(const ReturnStmt *RS, + CheckerContext &C) const { const Expr *RetE = RS->getRetValue(); if (!RetE) @@ -135,8 +126,8 @@ void StackAddrLeakChecker::PreVisitReturnStmt(CheckerContext &C, } } -void StackAddrLeakChecker::evalEndPath(EndOfFunctionNodeBuilder &B, void *tag, - ExprEngine &Eng) { +void StackAddrEscapeChecker::checkEndPath(EndOfFunctionNodeBuilder &B, + ExprEngine &Eng) const { const GRState *state = B.getState(); @@ -180,16 +171,16 @@ void StackAddrLeakChecker::evalEndPath(EndOfFunctionNodeBuilder &B, void *tag, return; // Generate an error node. - ExplodedNode *N = B.generateNode(state, tag, B.getPredecessor()); + ExplodedNode *N = B.generateNode(state); if (!N) return; if (!BT_stackleak) - BT_stackleak = + BT_stackleak.reset( new BuiltinBug("Stack address stored into global variable", "Stack address was saved into a global variable. " "This is dangerous because the address will become " - "invalid after returning from the function"); + "invalid after returning from the function")); for (unsigned i = 0, e = cb.V.size(); i != e; ++i) { // Generate a report for this bug. @@ -208,3 +199,7 @@ void StackAddrLeakChecker::evalEndPath(EndOfFunctionNodeBuilder &B, void *tag, Eng.getBugReporter().EmitReport(report); } } + +void ento::registerStackAddrEscapeChecker(CheckerManager &mgr) { + mgr.registerChecker<StackAddrEscapeChecker>(); +} diff --git a/lib/StaticAnalyzer/Checkers/StreamChecker.cpp b/lib/StaticAnalyzer/Checkers/StreamChecker.cpp index 2655be2..d0626b8 100644 --- a/lib/StaticAnalyzer/Checkers/StreamChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/StreamChecker.cpp @@ -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" @@ -55,52 +56,50 @@ struct StreamState { } }; -class StreamChecker : public CheckerVisitor<StreamChecker> { - IdentifierInfo *II_fopen, *II_tmpfile, *II_fclose, *II_fread, *II_fwrite, +class StreamChecker : public CheckerV2<eval::Call, + check::DeadSymbols, + check::EndPath, + check::PreStmt<ReturnStmt> > { + mutable IdentifierInfo *II_fopen, *II_tmpfile, *II_fclose, *II_fread, + *II_fwrite, *II_fseek, *II_ftell, *II_rewind, *II_fgetpos, *II_fsetpos, *II_clearerr, *II_feof, *II_ferror, *II_fileno; - BuiltinBug *BT_nullfp, *BT_illegalwhence, *BT_doubleclose, *BT_ResourceLeak; + mutable llvm::OwningPtr<BuiltinBug> BT_nullfp, BT_illegalwhence, + BT_doubleclose, BT_ResourceLeak; public: StreamChecker() : II_fopen(0), II_tmpfile(0) ,II_fclose(0), II_fread(0), II_fwrite(0), II_fseek(0), II_ftell(0), II_rewind(0), II_fgetpos(0), II_fsetpos(0), - II_clearerr(0), II_feof(0), II_ferror(0), II_fileno(0), - BT_nullfp(0), BT_illegalwhence(0), BT_doubleclose(0), - BT_ResourceLeak(0) {} + II_clearerr(0), II_feof(0), II_ferror(0), II_fileno(0) {} - static void *getTag() { - static int x; - return &x; - } - - virtual bool evalCallExpr(CheckerContext &C, const CallExpr *CE); - void evalDeadSymbols(CheckerContext &C, SymbolReaper &SymReaper); - void evalEndPath(EndOfFunctionNodeBuilder &B, void *tag, ExprEngine &Eng); - void PreVisitReturnStmt(CheckerContext &C, const ReturnStmt *S); + bool evalCall(const CallExpr *CE, CheckerContext &C) const; + void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const; + void checkEndPath(EndOfFunctionNodeBuilder &B, ExprEngine &Eng) const; + void checkPreStmt(const ReturnStmt *S, CheckerContext &C) const; private: - void Fopen(CheckerContext &C, const CallExpr *CE); - void Tmpfile(CheckerContext &C, const CallExpr *CE); - void Fclose(CheckerContext &C, const CallExpr *CE); - void Fread(CheckerContext &C, const CallExpr *CE); - void Fwrite(CheckerContext &C, const CallExpr *CE); - void Fseek(CheckerContext &C, const CallExpr *CE); - void Ftell(CheckerContext &C, const CallExpr *CE); - void Rewind(CheckerContext &C, const CallExpr *CE); - void Fgetpos(CheckerContext &C, const CallExpr *CE); - void Fsetpos(CheckerContext &C, const CallExpr *CE); - void Clearerr(CheckerContext &C, const CallExpr *CE); - void Feof(CheckerContext &C, const CallExpr *CE); - void Ferror(CheckerContext &C, const CallExpr *CE); - void Fileno(CheckerContext &C, const CallExpr *CE); - - void OpenFileAux(CheckerContext &C, const CallExpr *CE); + void Fopen(CheckerContext &C, const CallExpr *CE) const; + void Tmpfile(CheckerContext &C, const CallExpr *CE) const; + void Fclose(CheckerContext &C, const CallExpr *CE) const; + void Fread(CheckerContext &C, const CallExpr *CE) const; + void Fwrite(CheckerContext &C, const CallExpr *CE) const; + void Fseek(CheckerContext &C, const CallExpr *CE) const; + void Ftell(CheckerContext &C, const CallExpr *CE) const; + void Rewind(CheckerContext &C, const CallExpr *CE) const; + void Fgetpos(CheckerContext &C, const CallExpr *CE) const; + void Fsetpos(CheckerContext &C, const CallExpr *CE) const; + void Clearerr(CheckerContext &C, const CallExpr *CE) const; + void Feof(CheckerContext &C, const CallExpr *CE) const; + void Ferror(CheckerContext &C, const CallExpr *CE) const; + void Fileno(CheckerContext &C, const CallExpr *CE) const; + + void OpenFileAux(CheckerContext &C, const CallExpr *CE) const; const GRState *CheckNullStream(SVal SV, const GRState *state, - CheckerContext &C); + CheckerContext &C) const; const GRState *CheckDoubleClose(const CallExpr *CE, const GRState *state, - CheckerContext &C); + CheckerContext &C) const; }; } // end anonymous namespace @@ -110,20 +109,12 @@ namespace ento { template <> struct GRStateTrait<StreamState> : public GRStatePartialTrait<llvm::ImmutableMap<SymbolRef, StreamState> > { - static void *GDMIndex() { return StreamChecker::getTag(); } + static void *GDMIndex() { static int x; return &x; } }; } } -static void RegisterStreamChecker(ExprEngine &Eng) { - Eng.registerCheck(new StreamChecker()); -} - -void ento::registerStreamChecker(CheckerManager &mgr) { - mgr.addCheckerRegisterFunction(RegisterStreamChecker); -} - -bool StreamChecker::evalCallExpr(CheckerContext &C, const CallExpr *CE) { +bool StreamChecker::evalCall(const CallExpr *CE, CheckerContext &C) const { const GRState *state = C.getState(); const Expr *Callee = CE->getCallee(); SVal L = state->getSVal(Callee); @@ -221,15 +212,15 @@ bool StreamChecker::evalCallExpr(CheckerContext &C, const CallExpr *CE) { return false; } -void StreamChecker::Fopen(CheckerContext &C, const CallExpr *CE) { +void StreamChecker::Fopen(CheckerContext &C, const CallExpr *CE) const { OpenFileAux(C, CE); } -void StreamChecker::Tmpfile(CheckerContext &C, const CallExpr *CE) { +void StreamChecker::Tmpfile(CheckerContext &C, const CallExpr *CE) const { OpenFileAux(C, CE); } -void StreamChecker::OpenFileAux(CheckerContext &C, const CallExpr *CE) { +void StreamChecker::OpenFileAux(CheckerContext &C, const CallExpr *CE) const { const GRState *state = C.getState(); unsigned Count = C.getNodeBuilder().getCurrentBlockCount(); SValBuilder &svalBuilder = C.getSValBuilder(); @@ -255,25 +246,25 @@ void StreamChecker::OpenFileAux(CheckerContext &C, const CallExpr *CE) { } } -void StreamChecker::Fclose(CheckerContext &C, const CallExpr *CE) { +void StreamChecker::Fclose(CheckerContext &C, const CallExpr *CE) const { const GRState *state = CheckDoubleClose(CE, C.getState(), C); if (state) C.addTransition(state); } -void StreamChecker::Fread(CheckerContext &C, const CallExpr *CE) { +void StreamChecker::Fread(CheckerContext &C, const CallExpr *CE) const { const GRState *state = C.getState(); if (!CheckNullStream(state->getSVal(CE->getArg(3)), state, C)) return; } -void StreamChecker::Fwrite(CheckerContext &C, const CallExpr *CE) { +void StreamChecker::Fwrite(CheckerContext &C, const CallExpr *CE) const { const GRState *state = C.getState(); if (!CheckNullStream(state->getSVal(CE->getArg(3)), state, C)) return; } -void StreamChecker::Fseek(CheckerContext &C, const CallExpr *CE) { +void StreamChecker::Fseek(CheckerContext &C, const CallExpr *CE) const { const GRState *state = C.getState(); if (!(state = CheckNullStream(state->getSVal(CE->getArg(0)), state, C))) return; @@ -290,65 +281,65 @@ void StreamChecker::Fseek(CheckerContext &C, const CallExpr *CE) { if (ExplodedNode *N = C.generateNode(state)) { if (!BT_illegalwhence) - BT_illegalwhence = new BuiltinBug("Illegal whence argument", + BT_illegalwhence.reset(new BuiltinBug("Illegal whence argument", "The whence argument to fseek() should be " - "SEEK_SET, SEEK_END, or SEEK_CUR."); + "SEEK_SET, SEEK_END, or SEEK_CUR.")); BugReport *R = new BugReport(*BT_illegalwhence, BT_illegalwhence->getDescription(), N); C.EmitReport(R); } } -void StreamChecker::Ftell(CheckerContext &C, const CallExpr *CE) { +void StreamChecker::Ftell(CheckerContext &C, const CallExpr *CE) const { const GRState *state = C.getState(); if (!CheckNullStream(state->getSVal(CE->getArg(0)), state, C)) return; } -void StreamChecker::Rewind(CheckerContext &C, const CallExpr *CE) { +void StreamChecker::Rewind(CheckerContext &C, const CallExpr *CE) const { const GRState *state = C.getState(); if (!CheckNullStream(state->getSVal(CE->getArg(0)), state, C)) return; } -void StreamChecker::Fgetpos(CheckerContext &C, const CallExpr *CE) { +void StreamChecker::Fgetpos(CheckerContext &C, const CallExpr *CE) const { const GRState *state = C.getState(); if (!CheckNullStream(state->getSVal(CE->getArg(0)), state, C)) return; } -void StreamChecker::Fsetpos(CheckerContext &C, const CallExpr *CE) { +void StreamChecker::Fsetpos(CheckerContext &C, const CallExpr *CE) const { const GRState *state = C.getState(); if (!CheckNullStream(state->getSVal(CE->getArg(0)), state, C)) return; } -void StreamChecker::Clearerr(CheckerContext &C, const CallExpr *CE) { +void StreamChecker::Clearerr(CheckerContext &C, const CallExpr *CE) const { const GRState *state = C.getState(); if (!CheckNullStream(state->getSVal(CE->getArg(0)), state, C)) return; } -void StreamChecker::Feof(CheckerContext &C, const CallExpr *CE) { +void StreamChecker::Feof(CheckerContext &C, const CallExpr *CE) const { const GRState *state = C.getState(); if (!CheckNullStream(state->getSVal(CE->getArg(0)), state, C)) return; } -void StreamChecker::Ferror(CheckerContext &C, const CallExpr *CE) { +void StreamChecker::Ferror(CheckerContext &C, const CallExpr *CE) const { const GRState *state = C.getState(); if (!CheckNullStream(state->getSVal(CE->getArg(0)), state, C)) return; } -void StreamChecker::Fileno(CheckerContext &C, const CallExpr *CE) { +void StreamChecker::Fileno(CheckerContext &C, const CallExpr *CE) const { const GRState *state = C.getState(); if (!CheckNullStream(state->getSVal(CE->getArg(0)), state, C)) return; } const GRState *StreamChecker::CheckNullStream(SVal SV, const GRState *state, - CheckerContext &C) { + CheckerContext &C) const { const DefinedSVal *DV = dyn_cast<DefinedSVal>(&SV); if (!DV) return 0; @@ -360,8 +351,8 @@ const GRState *StreamChecker::CheckNullStream(SVal SV, const GRState *state, if (!stateNotNull && stateNull) { if (ExplodedNode *N = C.generateSink(stateNull)) { if (!BT_nullfp) - BT_nullfp = new BuiltinBug("NULL stream pointer", - "Stream pointer might be NULL."); + BT_nullfp.reset(new BuiltinBug("NULL stream pointer", + "Stream pointer might be NULL.")); BugReport *R =new BugReport(*BT_nullfp, BT_nullfp->getDescription(), N); C.EmitReport(R); } @@ -372,7 +363,7 @@ const GRState *StreamChecker::CheckNullStream(SVal SV, const GRState *state, const GRState *StreamChecker::CheckDoubleClose(const CallExpr *CE, const GRState *state, - CheckerContext &C) { + CheckerContext &C) const { SymbolRef Sym = state->getSVal(CE->getArg(0)).getAsSymbol(); if (!Sym) return state; @@ -389,9 +380,9 @@ const GRState *StreamChecker::CheckDoubleClose(const CallExpr *CE, ExplodedNode *N = C.generateSink(); if (N) { if (!BT_doubleclose) - BT_doubleclose = new BuiltinBug("Double fclose", + BT_doubleclose.reset(new BuiltinBug("Double fclose", "Try to close a file Descriptor already" - " closed. Cause undefined behaviour."); + " closed. Cause undefined behaviour.")); BugReport *R = new BugReport(*BT_doubleclose, BT_doubleclose->getDescription(), N); C.EmitReport(R); @@ -403,7 +394,8 @@ const GRState *StreamChecker::CheckDoubleClose(const CallExpr *CE, return state->set<StreamState>(Sym, StreamState::getClosed(CE)); } -void StreamChecker::evalDeadSymbols(CheckerContext &C,SymbolReaper &SymReaper) { +void StreamChecker::checkDeadSymbols(SymbolReaper &SymReaper, + CheckerContext &C) const { for (SymbolReaper::dead_iterator I = SymReaper.dead_begin(), E = SymReaper.dead_end(); I != E; ++I) { SymbolRef Sym = *I; @@ -416,8 +408,8 @@ void StreamChecker::evalDeadSymbols(CheckerContext &C,SymbolReaper &SymReaper) { ExplodedNode *N = C.generateSink(); if (N) { if (!BT_ResourceLeak) - BT_ResourceLeak = new BuiltinBug("Resource Leak", - "Opened File never closed. Potential Resource leak."); + BT_ResourceLeak.reset(new BuiltinBug("Resource Leak", + "Opened File never closed. Potential Resource leak.")); BugReport *R = new BugReport(*BT_ResourceLeak, BT_ResourceLeak->getDescription(), N); C.EmitReport(R); @@ -426,8 +418,8 @@ void StreamChecker::evalDeadSymbols(CheckerContext &C,SymbolReaper &SymReaper) { } } -void StreamChecker::evalEndPath(EndOfFunctionNodeBuilder &B, void *tag, - ExprEngine &Eng) { +void StreamChecker::checkEndPath(EndOfFunctionNodeBuilder &B, + ExprEngine &Eng) const { const GRState *state = B.getState(); typedef llvm::ImmutableMap<SymbolRef, StreamState> SymMap; SymMap M = state->get<StreamState>(); @@ -435,11 +427,11 @@ void StreamChecker::evalEndPath(EndOfFunctionNodeBuilder &B, void *tag, for (SymMap::iterator I = M.begin(), E = M.end(); I != E; ++I) { StreamState SS = I->second; if (SS.isOpened()) { - ExplodedNode *N = B.generateNode(state, tag, B.getPredecessor()); + ExplodedNode *N = B.generateNode(state); if (N) { if (!BT_ResourceLeak) - BT_ResourceLeak = new BuiltinBug("Resource Leak", - "Opened File never closed. Potential Resource leak."); + BT_ResourceLeak.reset(new BuiltinBug("Resource Leak", + "Opened File never closed. Potential Resource leak.")); BugReport *R = new BugReport(*BT_ResourceLeak, BT_ResourceLeak->getDescription(), N); Eng.getBugReporter().EmitReport(R); @@ -448,7 +440,7 @@ void StreamChecker::evalEndPath(EndOfFunctionNodeBuilder &B, void *tag, } } -void StreamChecker::PreVisitReturnStmt(CheckerContext &C, const ReturnStmt *S) { +void StreamChecker::checkPreStmt(const ReturnStmt *S, CheckerContext &C) const { const Expr *RetE = S->getRetValue(); if (!RetE) return; @@ -468,3 +460,7 @@ void StreamChecker::PreVisitReturnStmt(CheckerContext &C, const ReturnStmt *S) { C.addTransition(state); } + +void ento::registerStreamChecker(CheckerManager &mgr) { + mgr.registerChecker<StreamChecker>(); +} diff --git a/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp b/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp index a53ebb5..be4fbf6 100644 --- a/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp @@ -13,10 +13,11 @@ //===----------------------------------------------------------------------===// #include "ClangSACheckers.h" -#include "clang/Basic/TargetInfo.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/Basic/TargetInfo.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/StringSwitch.h" #include <fcntl.h> @@ -26,7 +27,7 @@ using namespace ento; using llvm::Optional; namespace { -class UnixAPIChecker : public CheckerVisitor<UnixAPIChecker> { +class UnixAPIChecker : public CheckerV2< check::PreStmt<CallExpr> > { enum SubChecks { OpenFn = 0, PthreadOnceFn = 1, @@ -34,27 +35,22 @@ class UnixAPIChecker : public CheckerVisitor<UnixAPIChecker> { NumChecks }; - BugType *BTypes[NumChecks]; + mutable BugType *BTypes[NumChecks]; public: - Optional<uint64_t> Val_O_CREAT; + mutable Optional<uint64_t> Val_O_CREAT; public: UnixAPIChecker() { memset(BTypes, 0, sizeof(*BTypes) * NumChecks); } - static void *getTag() { static unsigned tag = 0; return &tag; } + ~UnixAPIChecker() { + for (unsigned i=0; i != NumChecks; ++i) + delete BTypes[i]; + } - void PreVisitCallExpr(CheckerContext &C, const CallExpr *CE); + void checkPreStmt(const CallExpr *CE, CheckerContext &C) const; }; } //end anonymous namespace -static void RegisterUnixAPIChecker(ExprEngine &Eng) { - Eng.registerCheck(new UnixAPIChecker()); -} - -void ento::registerUnixAPIChecker(CheckerManager &mgr) { - mgr.addCheckerRegisterFunction(RegisterUnixAPIChecker); -} - //===----------------------------------------------------------------------===// // Utility functions. //===----------------------------------------------------------------------===// @@ -69,7 +65,7 @@ static inline void LazyInitialize(BugType *&BT, const char *name) { // "open" (man 2 open) //===----------------------------------------------------------------------===// -static void CheckOpen(CheckerContext &C, UnixAPIChecker &UC, +static void CheckOpen(CheckerContext &C, const UnixAPIChecker &UC, const CallExpr *CE, BugType *&BT) { // The definition of O_CREAT is platform specific. We need a better way // of querying this information from the checking environment. @@ -141,7 +137,7 @@ static void CheckOpen(CheckerContext &C, UnixAPIChecker &UC, // pthread_once //===----------------------------------------------------------------------===// -static void CheckPthreadOnce(CheckerContext &C, UnixAPIChecker &, +static void CheckPthreadOnce(CheckerContext &C, const UnixAPIChecker &, const CallExpr *CE, BugType *&BT) { // This is similar to 'CheckDispatchOnce' in the MacOSXAPIChecker. @@ -186,7 +182,7 @@ static void CheckPthreadOnce(CheckerContext &C, UnixAPIChecker &, // FIXME: Eventually this should be rolled into the MallocChecker, but this // check is more basic and is valuable for widespread use. -static void CheckMallocZero(CheckerContext &C, UnixAPIChecker &UC, +static void CheckMallocZero(CheckerContext &C, const UnixAPIChecker &UC, const CallExpr *CE, BugType *&BT) { // Sanity check that malloc takes one argument. @@ -234,16 +230,16 @@ static void CheckMallocZero(CheckerContext &C, UnixAPIChecker &UC, // Central dispatch function. //===----------------------------------------------------------------------===// -typedef void (*SubChecker)(CheckerContext &C, UnixAPIChecker &UC, +typedef void (*SubChecker)(CheckerContext &C, const UnixAPIChecker &UC, const CallExpr *CE, BugType *&BT); namespace { class SubCheck { SubChecker SC; - UnixAPIChecker *UC; + const UnixAPIChecker *UC; BugType **BT; public: - SubCheck(SubChecker sc, UnixAPIChecker *uc, BugType *& bt) : SC(sc), UC(uc), - BT(&bt) {} + SubCheck(SubChecker sc, const UnixAPIChecker *uc, BugType *& bt) + : SC(sc), UC(uc), BT(&bt) {} SubCheck() : SC(NULL), UC(NULL), BT(NULL) {} void run(CheckerContext &C, const CallExpr *CE) const { @@ -253,7 +249,7 @@ namespace { }; } // end anonymous namespace -void UnixAPIChecker::PreVisitCallExpr(CheckerContext &C, const CallExpr *CE) { +void UnixAPIChecker::checkPreStmt(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(); @@ -280,3 +276,11 @@ void UnixAPIChecker::PreVisitCallExpr(CheckerContext &C, const CallExpr *CE) { SC.run(C, CE); } + +//===----------------------------------------------------------------------===// +// Registration. +//===----------------------------------------------------------------------===// + +void ento::registerUnixAPIChecker(CheckerManager &mgr) { + mgr.registerChecker<UnixAPIChecker>(); +} diff --git a/lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp b/lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp index 3038e29..1bc487a 100644 --- a/lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp @@ -14,15 +14,16 @@ //===----------------------------------------------------------------------===// #include "ClangSACheckers.h" -#include "clang/AST/ParentMap.h" -#include "clang/Basic/Builtins.h" -#include "clang/Basic/SourceManager.h" +#include "clang/StaticAnalyzer/Core/CheckerV2.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerVisitor.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" +#include "clang/AST/ParentMap.h" +#include "clang/Basic/Builtins.h" +#include "clang/Basic/SourceManager.h" #include "llvm/ADT/SmallPtrSet.h" // The number of CFGBlock pointers we want to reserve memory for. This is used @@ -33,40 +34,27 @@ using namespace clang; using namespace ento; namespace { -class UnreachableCodeChecker : public Checker { +class UnreachableCodeChecker : public CheckerV2<check::EndAnalysis> { public: - static void *getTag(); - void VisitEndAnalysis(ExplodedGraph &G, - BugReporter &B, - ExprEngine &Eng); + void checkEndAnalysis(ExplodedGraph &G, BugReporter &B, + ExprEngine &Eng) const; private: + typedef llvm::SmallSet<unsigned, DEFAULT_CFGBLOCKS> CFGBlocksSet; + static inline const Stmt *getUnreachableStmt(const CFGBlock *CB); - void FindUnreachableEntryPoints(const CFGBlock *CB); + static void FindUnreachableEntryPoints(const CFGBlock *CB, + CFGBlocksSet &reachable, + CFGBlocksSet &visited); static bool isInvalidPath(const CFGBlock *CB, const ParentMap &PM); static inline bool isEmptyCFGBlock(const CFGBlock *CB); - - llvm::SmallSet<unsigned, DEFAULT_CFGBLOCKS> reachable; - llvm::SmallSet<unsigned, DEFAULT_CFGBLOCKS> visited; }; } -void *UnreachableCodeChecker::getTag() { - static int x = 0; - return &x; -} - -static void RegisterUnreachableCodeChecker(ExprEngine &Eng) { - Eng.registerCheck(new UnreachableCodeChecker()); -} - -void ento::registerUnreachableCodeChecker(CheckerManager &mgr) { - mgr.addCheckerRegisterFunction(RegisterUnreachableCodeChecker); -} - -void UnreachableCodeChecker::VisitEndAnalysis(ExplodedGraph &G, +void UnreachableCodeChecker::checkEndAnalysis(ExplodedGraph &G, BugReporter &B, - ExprEngine &Eng) { - // Bail out if we didn't cover all paths + ExprEngine &Eng) const { + CFGBlocksSet reachable, visited; + if (Eng.hasWorkRemaining()) return; @@ -109,7 +97,7 @@ void UnreachableCodeChecker::VisitEndAnalysis(ExplodedGraph &G, // Find the entry points for this block if (!visited.count(CB->getBlockID())) - FindUnreachableEntryPoints(CB); + FindUnreachableEntryPoints(CB, reachable, visited); // This block may have been pruned; check if we still want to report it if (reachable.count(CB->getBlockID())) @@ -155,7 +143,9 @@ void UnreachableCodeChecker::VisitEndAnalysis(ExplodedGraph &G, } // Recursively finds the entry point(s) for this dead CFGBlock. -void UnreachableCodeChecker::FindUnreachableEntryPoints(const CFGBlock *CB) { +void UnreachableCodeChecker::FindUnreachableEntryPoints(const CFGBlock *CB, + CFGBlocksSet &reachable, + CFGBlocksSet &visited) { visited.insert(CB->getBlockID()); for (CFGBlock::const_pred_iterator I = CB->pred_begin(), E = CB->pred_end(); @@ -166,7 +156,7 @@ void UnreachableCodeChecker::FindUnreachableEntryPoints(const CFGBlock *CB) { reachable.insert(CB->getBlockID()); if (!visited.count((*I)->getBlockID())) // If we haven't previously visited the unreachable predecessor, recurse - FindUnreachableEntryPoints(*I); + FindUnreachableEntryPoints(*I, reachable, visited); } } } @@ -226,3 +216,7 @@ bool UnreachableCodeChecker::isEmptyCFGBlock(const CFGBlock *CB) { && CB->size() == 0 // No statements && CB->getTerminator() == 0; // No terminator } + +void ento::registerUnreachableCodeChecker(CheckerManager &mgr) { + mgr.registerChecker<UnreachableCodeChecker>(); +} diff --git a/lib/StaticAnalyzer/Core/BugReporter.cpp b/lib/StaticAnalyzer/Core/BugReporter.cpp index 9a84045..672982a 100644 --- a/lib/StaticAnalyzer/Core/BugReporter.cpp +++ b/lib/StaticAnalyzer/Core/BugReporter.cpp @@ -1202,16 +1202,8 @@ static void GenerateExtensivePathDiagnostic(PathDiagnostic& PD, //===----------------------------------------------------------------------===// // Methods for BugType and subclasses. //===----------------------------------------------------------------------===// -BugType::~BugType() { - // Free up the equivalence class objects. Observe that we get a pointer to - // the object first before incrementing the iterator, as destroying the - // node before doing so means we will read from freed memory. - for (iterator I = begin(), E = end(); I !=E; ) { - BugReportEquivClass *EQ = &*I; - ++I; - delete EQ; - } -} +BugType::~BugType() { } + void BugType::FlushReports(BugReporter &BR) {} //===----------------------------------------------------------------------===// @@ -1315,28 +1307,30 @@ void BugReporter::FlushReports() { return; // First flush the warnings for each BugType. This may end up creating new - // warnings and new BugTypes. Because ImmutableSet is a functional data - // structure, we do not need to worry about the iterators being invalidated. + // warnings and new BugTypes. + // FIXME: Only NSErrorChecker needs BugType's FlushReports. + // Turn NSErrorChecker into a proper checker and remove this. + llvm::SmallVector<const BugType*, 16> bugTypes; for (BugTypesTy::iterator I=BugTypes.begin(), E=BugTypes.end(); I!=E; ++I) + bugTypes.push_back(*I); + for (llvm::SmallVector<const BugType*, 16>::iterator + I = bugTypes.begin(), E = bugTypes.end(); I != E; ++I) const_cast<BugType*>(*I)->FlushReports(*this); - // Iterate through BugTypes a second time. BugTypes may have been updated - // with new BugType objects and new warnings. - for (BugTypesTy::iterator I=BugTypes.begin(), E=BugTypes.end(); I!=E; ++I) { - BugType *BT = const_cast<BugType*>(*I); - - typedef llvm::FoldingSet<BugReportEquivClass> SetTy; - SetTy& EQClasses = BT->EQClasses; - - for (SetTy::iterator EI=EQClasses.begin(), EE=EQClasses.end(); EI!=EE;++EI){ - BugReportEquivClass& EQ = *EI; - FlushReport(EQ); - } - - // Delete the BugType object. - delete BT; + typedef llvm::FoldingSet<BugReportEquivClass> SetTy; + for (SetTy::iterator EI=EQClasses.begin(), EE=EQClasses.end(); EI!=EE;++EI){ + BugReportEquivClass& EQ = *EI; + FlushReport(EQ); } + // BugReporter owns and deletes only BugTypes created implicitly through + // EmitBasicReport. + // FIXME: There are leaks from checkers that assume that the BugTypes they + // create will be destroyed by the BugReporter. + for (llvm::StringMap<BugType*>::iterator + I = StrBugTypes.begin(), E = StrBugTypes.end(); I != E; ++I) + delete I->second; + // Remove all references to the BugType objects. BugTypes = F.getEmptySet(); } @@ -1632,11 +1626,11 @@ void BugReporter::EmitReport(BugReport* R) { BugType& BT = R->getBugType(); Register(&BT); void *InsertPos; - BugReportEquivClass* EQ = BT.EQClasses.FindNodeOrInsertPos(ID, InsertPos); + BugReportEquivClass* EQ = EQClasses.FindNodeOrInsertPos(ID, InsertPos); if (!EQ) { EQ = new BugReportEquivClass(R); - BT.EQClasses.InsertNode(EQ, InsertPos); + EQClasses.InsertNode(EQ, InsertPos); } else EQ->AddReport(R); @@ -1887,10 +1881,24 @@ void BugReporter::EmitBasicReport(llvm::StringRef name, llvm::StringRef str, SourceLocation Loc, SourceRange* RBeg, unsigned NumRanges) { - // 'BT' will be owned by BugReporter as soon as we call 'EmitReport'. - BugType *BT = new BugType(name, category); + // 'BT' is owned by BugReporter. + BugType *BT = getBugTypeForName(name, category); FullSourceLoc L = getContext().getFullLoc(Loc); RangedBugReport *R = new DiagBugReport(*BT, str, L); for ( ; NumRanges > 0 ; --NumRanges, ++RBeg) R->addRange(*RBeg); EmitReport(R); } + +BugType *BugReporter::getBugTypeForName(llvm::StringRef name, + llvm::StringRef category) { + llvm::SmallString<136> fullDesc; + llvm::raw_svector_ostream(fullDesc) << name << ":" << category; + llvm::StringMapEntry<BugType *> & + entry = StrBugTypes.GetOrCreateValue(fullDesc); + BugType *BT = entry.getValue(); + if (!BT) { + BT = new BugType(name, category); + entry.setValue(BT); + } + return BT; +} diff --git a/lib/StaticAnalyzer/Core/CheckerManager.cpp b/lib/StaticAnalyzer/Core/CheckerManager.cpp index 1989b82..75d331a 100644 --- a/lib/StaticAnalyzer/Core/CheckerManager.cpp +++ b/lib/StaticAnalyzer/Core/CheckerManager.cpp @@ -13,11 +13,17 @@ #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/CheckerProvider.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "clang/Analysis/ProgramPoint.h" #include "clang/AST/DeclBase.h" using namespace clang; using namespace ento; +//===----------------------------------------------------------------------===// +// Functions for running checkers for AST traversing.. +//===----------------------------------------------------------------------===// + void CheckerManager::runCheckersOnASTDecl(const Decl *D, AnalysisManager& mgr, BugReporter &BR) { assert(D); @@ -33,53 +39,398 @@ void CheckerManager::runCheckersOnASTDecl(const Decl *D, AnalysisManager& mgr, for (unsigned i = 0, e = DeclCheckers.size(); i != e; ++i) { DeclCheckerInfo &info = DeclCheckers[i]; if (info.IsForDeclFn(D)) - checkers->push_back(std::make_pair(info.Checker, info.CheckFn)); + checkers->push_back(info.CheckFn); } } assert(checkers); for (CachedDeclCheckers::iterator - I = checkers->begin(), E = checkers->end(); I != E; ++I) { - CheckerRef checker = I->first; - CheckDeclFunc fn = I->second; - fn(checker, D, mgr, BR); - } + I = checkers->begin(), E = checkers->end(); I != E; ++I) + (*I)(D, mgr, BR); } void CheckerManager::runCheckersOnASTBody(const Decl *D, AnalysisManager& mgr, BugReporter &BR) { assert(D && D->hasBody()); - for (unsigned i = 0, e = BodyCheckers.size(); i != e; ++i) { - CheckerRef checker = BodyCheckers[i].first; - CheckDeclFunc fn = BodyCheckers[i].second; - fn(checker, D, mgr, BR); + for (unsigned i = 0, e = BodyCheckers.size(); i != e; ++i) + BodyCheckers[i](D, mgr, BR); +} + +//===----------------------------------------------------------------------===// +// Functions for running checkers for path-sensitive checking. +//===----------------------------------------------------------------------===// + +template <typename CHECK_CTX> +static void expandGraphWithCheckers(CHECK_CTX checkCtx, + ExplodedNodeSet &Dst, + const ExplodedNodeSet &Src) { + + typename CHECK_CTX::CheckersTy::const_iterator + I = checkCtx.checkers_begin(), E = checkCtx.checkers_end(); + if (I == E) { + Dst.insert(Src); + return; + } + + ExplodedNodeSet Tmp1, Tmp2; + const ExplodedNodeSet *PrevSet = &Src; + + for (; I != E; ++I) { + ExplodedNodeSet *CurrSet = 0; + if (I+1 == E) + CurrSet = &Dst; + else { + CurrSet = (PrevSet == &Tmp1) ? &Tmp2 : &Tmp1; + CurrSet->clear(); + } + + for (ExplodedNodeSet::iterator NI = PrevSet->begin(), NE = PrevSet->end(); + NI != NE; ++NI) + checkCtx.runChecker(*I, *CurrSet, *NI); + + // Update which NodeSet is the current one. + PrevSet = CurrSet; + } +} + +namespace { + struct CheckStmtContext { + typedef llvm::SmallVectorImpl<CheckerManager::CheckStmtFunc> CheckersTy; + bool IsPreVisit; + const CheckersTy &Checkers; + 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) { } + + void runChecker(CheckerManager::CheckStmtFunc checkFn, + ExplodedNodeSet &Dst, ExplodedNode *Pred) { + // FIXME: Remove respondsToCallback from CheckerContext; + CheckerContext C(Dst, Eng.getBuilder(), Eng, Pred, checkFn.Checker, + IsPreVisit ? ProgramPoint::PreStmtKind : + ProgramPoint::PostStmtKind, 0, S); + checkFn(S, C); + } + }; +} + +/// \brief Run checkers for visiting Stmts. +void CheckerManager::runCheckersForStmt(bool isPreVisit, + ExplodedNodeSet &Dst, + const ExplodedNodeSet &Src, + const Stmt *S, + ExprEngine &Eng) { + CheckStmtContext C(isPreVisit, *getCachedStmtCheckersFor(S, isPreVisit), + S, Eng); + expandGraphWithCheckers(C, Dst, Src); +} + +namespace { + struct CheckObjCMessageContext { + typedef std::vector<CheckerManager::CheckObjCMessageFunc> CheckersTy; + bool IsPreVisit; + const CheckersTy &Checkers; + 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) { } + + void runChecker(CheckerManager::CheckObjCMessageFunc checkFn, + ExplodedNodeSet &Dst, ExplodedNode *Pred) { + CheckerContext C(Dst, Eng.getBuilder(), Eng, Pred, checkFn.Checker, + IsPreVisit ? ProgramPoint::PreStmtKind : + ProgramPoint::PostStmtKind, 0, + Msg.getOriginExpr()); + checkFn(Msg, C); + } + }; +} + +/// \brief Run checkers for visiting obj-c messages. +void CheckerManager::runCheckersForObjCMessage(bool isPreVisit, + ExplodedNodeSet &Dst, + const ExplodedNodeSet &Src, + const ObjCMessage &msg, + ExprEngine &Eng) { + CheckObjCMessageContext C(isPreVisit, + isPreVisit ? PreObjCMessageCheckers + : PostObjCMessageCheckers, + msg, Eng); + expandGraphWithCheckers(C, Dst, Src); +} + +namespace { + struct CheckLocationContext { + typedef std::vector<CheckerManager::CheckLocationFunc> CheckersTy; + const CheckersTy &Checkers; + SVal Loc; + bool IsLoad; + const Stmt *S; + 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, ExprEngine &eng) + : Checkers(checkers), Loc(loc), IsLoad(isLoad), S(s), Eng(eng) { } + + void runChecker(CheckerManager::CheckLocationFunc checkFn, + ExplodedNodeSet &Dst, ExplodedNode *Pred) { + CheckerContext C(Dst, Eng.getBuilder(), Eng, Pred, checkFn.Checker, + IsLoad ? ProgramPoint::PreLoadKind : + ProgramPoint::PreStoreKind, 0, S); + checkFn(Loc, IsLoad, C); + } + }; +} + +/// \brief Run checkers for load/store of a location. +void CheckerManager::runCheckersForLocation(ExplodedNodeSet &Dst, + const ExplodedNodeSet &Src, + SVal location, bool isLoad, + const Stmt *S, ExprEngine &Eng) { + CheckLocationContext C(LocationCheckers, location, isLoad, S, Eng); + expandGraphWithCheckers(C, Dst, Src); +} + +void CheckerManager::runCheckersForEndAnalysis(ExplodedGraph &G, + BugReporter &BR, + ExprEngine &Eng) { + for (unsigned i = 0, e = EndAnalysisCheckers.size(); i != e; ++i) + EndAnalysisCheckers[i](G, BR, Eng); +} + +/// \brief Run checkers for end of path. +void CheckerManager::runCheckersForEndPath(EndOfFunctionNodeBuilder &B, + ExprEngine &Eng) { + for (unsigned i = 0, e = EndPathCheckers.size(); i != e; ++i) { + CheckEndPathFunc fn = EndPathCheckers[i]; + EndOfFunctionNodeBuilder specialB = B.withCheckerTag(fn.Checker); + fn(specialB, Eng); + } +} + +/// \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<CheckerManager::CheckDeadSymbolsFunc> 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; } -void CheckerManager::_registerForDecl(CheckerRef checker, CheckDeclFunc checkfn, +/// \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); + } + } +} + +//===----------------------------------------------------------------------===// +// Internal registration functions for AST traversing. +//===----------------------------------------------------------------------===// + +void CheckerManager::_registerForDecl(CheckDeclFunc checkfn, HandlesDeclFunc isForDeclFn) { - DeclCheckerInfo info = { checker, checkfn, isForDeclFn }; + DeclCheckerInfo info = { checkfn, isForDeclFn }; DeclCheckers.push_back(info); } -void CheckerManager::_registerForBody(CheckerRef checker, - CheckDeclFunc checkfn) { - BodyCheckers.push_back(std::make_pair(checker, checkfn)); +void CheckerManager::_registerForBody(CheckDeclFunc checkfn) { + BodyCheckers.push_back(checkfn); } -void CheckerManager::registerCheckersToEngine(ExprEngine &eng) { - for (unsigned i = 0, e = Funcs.size(); i != e; ++i) - Funcs[i](eng); +//===----------------------------------------------------------------------===// +// Internal registration functions for path-sensitive checking. +//===----------------------------------------------------------------------===// + +void CheckerManager::_registerForPreStmt(CheckStmtFunc checkfn, + HandlesStmtFunc isForStmtFn) { + StmtCheckerInfo info = { checkfn, isForStmtFn, /*IsPreVisit*/true }; + StmtCheckers.push_back(info); +} +void CheckerManager::_registerForPostStmt(CheckStmtFunc checkfn, + HandlesStmtFunc isForStmtFn) { + StmtCheckerInfo info = { checkfn, isForStmtFn, /*IsPreVisit*/false }; + StmtCheckers.push_back(info); } -CheckerManager::~CheckerManager() { - for (unsigned i = 0, e = Checkers.size(); i != e; ++i) { - CheckerRef checker = Checkers[i].first; - Dtor dtor = Checkers[i].second; - dtor(checker); +void CheckerManager::_registerForPreObjCMessage(CheckObjCMessageFunc checkfn) { + PreObjCMessageCheckers.push_back(checkfn); +} +void CheckerManager::_registerForPostObjCMessage(CheckObjCMessageFunc checkfn) { + PostObjCMessageCheckers.push_back(checkfn); +} + +void CheckerManager::_registerForLocation(CheckLocationFunc checkfn) { + LocationCheckers.push_back(checkfn); +} + +void CheckerManager::_registerForEndAnalysis(CheckEndAnalysisFunc checkfn) { + EndAnalysisCheckers.push_back(checkfn); +} + +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); +} + +//===----------------------------------------------------------------------===// +// Implementation details. +//===----------------------------------------------------------------------===// + +CheckerManager::CachedStmtCheckers * +CheckerManager::getCachedStmtCheckersFor(const Stmt *S, bool isPreVisit) { + assert(S); + + CachedStmtCheckersKey key(S->getStmtClass(), isPreVisit); + CachedStmtCheckers *checkers = 0; + CachedStmtCheckersMapTy::iterator CCI = CachedStmtCheckersMap.find(key); + if (CCI != CachedStmtCheckersMap.end()) { + checkers = &(CCI->second); + } else { + // Find the checkers that should run for this Stmt and cache them. + checkers = &CachedStmtCheckersMap[key]; + for (unsigned i = 0, e = StmtCheckers.size(); i != e; ++i) { + StmtCheckerInfo &info = StmtCheckers[i]; + if (info.IsPreVisit == isPreVisit && info.IsForStmtFn(S)) + checkers->push_back(info.CheckFn); + } } + + assert(checkers); + return checkers; +} + +CheckerManager::~CheckerManager() { + for (unsigned i = 0, e = CheckerDtors.size(); i != e; ++i) + CheckerDtors[i](); } // Anchor for the vtable. CheckerProvider::~CheckerProvider() { } + +// Anchor for the vtable. +GraphExpander::~GraphExpander() { } diff --git a/lib/StaticAnalyzer/Core/CoreEngine.cpp b/lib/StaticAnalyzer/Core/CoreEngine.cpp index 070042a..08a2068 100644 --- a/lib/StaticAnalyzer/Core/CoreEngine.cpp +++ b/lib/StaticAnalyzer/Core/CoreEngine.cpp @@ -718,13 +718,14 @@ EndOfFunctionNodeBuilder::~EndOfFunctionNodeBuilder() { } ExplodedNode* -EndOfFunctionNodeBuilder::generateNode(const GRState* State, const void *tag, - ExplodedNode* P) { +EndOfFunctionNodeBuilder::generateNode(const GRState* State, + ExplodedNode* P, const void *tag) { hasGeneratedNode = true; bool IsNew; ExplodedNode* Node = Eng.G->getNode(BlockEntrance(&B, - Pred->getLocationContext(), tag), State, &IsNew); + Pred->getLocationContext(), tag ? tag : Tag), + State, &IsNew); Node->addPredecessor(P ? P : Pred, *Eng.G); diff --git a/lib/StaticAnalyzer/Core/Environment.cpp b/lib/StaticAnalyzer/Core/Environment.cpp index ecaff29..1bffa30 100644 --- a/lib/StaticAnalyzer/Core/Environment.cpp +++ b/lib/StaticAnalyzer/Core/Environment.cpp @@ -32,6 +32,11 @@ SVal Environment::getSVal(const Stmt *E, SValBuilder& svalBuilder) const { switch (E->getStmtClass()) { case Stmt::AddrLabelExprClass: return svalBuilder.makeLoc(cast<AddrLabelExpr>(E)); + case Stmt::OpaqueValueExprClass: { + const OpaqueValueExpr *ope = cast<OpaqueValueExpr>(E); + E = ope->getSourceExpr(); + continue; + } case Stmt::ParenExprClass: // ParenExprs are no-ops. E = cast<ParenExpr>(E)->getSubExpr(); diff --git a/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp b/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp index dbfebcc..e3e7ee9 100644 --- a/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp +++ b/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp @@ -175,7 +175,8 @@ public: virtual void Initialize(ASTContext &Context) { Ctx = &Context; - checkerMgr.reset(registerCheckers(Opts, PP.getDiagnostics())); + checkerMgr.reset(registerCheckers(Opts, PP.getLangOptions(), + PP.getDiagnostics())); Mgr.reset(new AnalysisManager(*Ctx, PP.getDiagnostics(), PP.getLangOptions(), PD, CreateStoreMgr, CreateConstraintMgr, @@ -339,9 +340,6 @@ static void ActionExprEngine(AnalysisConsumer &C, AnalysisManager& mgr, return; ExprEngine Eng(mgr, TF.take()); - if (C.Opts.EnableExperimentalInternalChecks) - RegisterExperimentalInternalChecks(Eng); - RegisterNSErrorChecks(Eng.getBugReporter(), Eng, *D); if (C.Opts.EnableExperimentalChecks) diff --git a/lib/StaticAnalyzer/Frontend/CMakeLists.txt b/lib/StaticAnalyzer/Frontend/CMakeLists.txt index cd9ac1b..16014ba 100644 --- a/lib/StaticAnalyzer/Frontend/CMakeLists.txt +++ b/lib/StaticAnalyzer/Frontend/CMakeLists.txt @@ -16,5 +16,5 @@ add_dependencies(clangStaticAnalyzerFrontend ClangAttrClasses ClangAttrList ClangDeclNodes - ClangStmtNode + ClangStmtNodes ) diff --git a/lib/StaticAnalyzer/Frontend/CheckerRegistration.cpp b/lib/StaticAnalyzer/Frontend/CheckerRegistration.cpp index 6625729..677e20c 100644 --- a/lib/StaticAnalyzer/Frontend/CheckerRegistration.cpp +++ b/lib/StaticAnalyzer/Frontend/CheckerRegistration.cpp @@ -12,12 +12,14 @@ //===----------------------------------------------------------------------===// #include "clang/StaticAnalyzer/Frontend/CheckerRegistration.h" +#include "clang/StaticAnalyzer/Frontend/FrontendActions.h" #include "../Checkers/ClangSACheckerProvider.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/CheckerProvider.h" #include "clang/Frontend/AnalyzerOptions.h" #include "clang/Frontend/FrontendDiagnostic.h" #include "clang/Basic/Diagnostic.h" +#include "llvm/Support/raw_ostream.h" #include "llvm/ADT/OwningPtr.h" #include "llvm/ADT/SmallVector.h" @@ -25,8 +27,9 @@ using namespace clang; using namespace ento; CheckerManager *ento::registerCheckers(const AnalyzerOptions &opts, + const LangOptions &langOpts, Diagnostic &diags) { - llvm::OwningPtr<CheckerManager> checkerMgr(new CheckerManager()); + llvm::OwningPtr<CheckerManager> checkerMgr(new CheckerManager(langOpts)); llvm::SmallVector<CheckerOptInfo, 8> checkerOpts; for (unsigned i = 0, e = opts.CheckersControlList.size(); i != e; ++i) { @@ -48,3 +51,16 @@ CheckerManager *ento::registerCheckers(const AnalyzerOptions &opts, return checkerMgr.take(); } + +void ento::printCheckerHelp(llvm::raw_ostream &OS) { + OS << "OVERVIEW: Clang Static Analyzer Checkers List\n"; + OS << '\n'; + OS << "USAGE: -analyzer-checker <check1,check2,...>\n"; + OS << '\n'; + OS << "CHECKERS:\n"; + + llvm::OwningPtr<CheckerProvider> provider(createClangSACheckerProvider()); + provider->printHelp(OS); + + // FIXME: Load CheckerProviders from plugins. +} |