diff options
author | Dimitry Andric <dim@FreeBSD.org> | 2011-10-20 21:14:49 +0000 |
---|---|---|
committer | Dimitry Andric <dim@FreeBSD.org> | 2011-10-20 21:14:49 +0000 |
commit | 36981b17ed939300f6f8fc2355a255f711fcef71 (patch) | |
tree | ee2483e98b09cac943dc93a6969d83ca737ff139 /lib/StaticAnalyzer | |
parent | 180abc3db9ae3b4fc63cd65b15697e6ffcc8a657 (diff) | |
download | src-36981b17ed939300f6f8fc2355a255f711fcef71.tar.gz src-36981b17ed939300f6f8fc2355a255f711fcef71.zip |
Vendor import of clang release_30 branch r142614:vendor/clang/clang-r142614
Notes
Notes:
svn path=/vendor/clang/dist/; revision=226586
svn path=/vendor/clang/clang-r142614/; revision=226587; tag=vendor/clang/clang-r142614
Diffstat (limited to 'lib/StaticAnalyzer')
101 files changed, 7413 insertions, 6774 deletions
diff --git a/lib/StaticAnalyzer/Checkers/AdjustedReturnValueChecker.cpp b/lib/StaticAnalyzer/Checkers/AdjustedReturnValueChecker.cpp index 8fc6d2a2933e..dc524ba24e06 100644 --- a/lib/StaticAnalyzer/Checkers/AdjustedReturnValueChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/AdjustedReturnValueChecker.cpp @@ -37,7 +37,7 @@ void AdjustedReturnValueChecker::checkPostStmt(const CallExpr *CE, QualType expectedResultTy = CE->getType(); // Fetch the signature of the called function. - const GRState *state = C.getState(); + const ProgramState *state = C.getState(); SVal V = state->getSVal(CE); diff --git a/lib/StaticAnalyzer/Checkers/AnalyzerStatsChecker.cpp b/lib/StaticAnalyzer/Checkers/AnalyzerStatsChecker.cpp index 983427afb62d..cd977bf54c67 100644 --- a/lib/StaticAnalyzer/Checkers/AnalyzerStatsChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/AnalyzerStatsChecker.cpp @@ -80,7 +80,7 @@ void AnalyzerStatsChecker::checkEndAnalysis(ExplodedGraph &G, if (isa<FunctionDecl>(D) || isa<ObjCMethodDecl>(D)) { const NamedDecl *ND = cast<NamedDecl>(D); - output << ND; + output << *ND; } else if (isa<BlockDecl>(D)) { output << "block(line:" << Loc.getLine() << ":col:" << Loc.getColumn(); @@ -94,7 +94,7 @@ void AnalyzerStatsChecker::checkEndAnalysis(ExplodedGraph &G, << (Eng.hasEmptyWorkList() ? "yes" : "no"); B.EmitBasicReport("Analyzer Statistics", "Internal Statistics", output.str(), - D->getLocation()); + PathDiagnosticLocation(D, SM)); // Emit warning for each block we bailed out on typedef CoreEngine::BlocksExhausted::const_iterator ExhaustedIterator; @@ -106,7 +106,8 @@ void AnalyzerStatsChecker::checkEndAnalysis(ExplodedGraph &G, const CFGElement &CE = Exit->front(); if (const CFGStmt *CS = dyn_cast<CFGStmt>(&CE)) B.EmitBasicReport("Bailout Point", "Internal Statistics", "The analyzer " - "stopped analyzing at this point", CS->getStmt()->getLocStart()); + "stopped analyzing at this point", + PathDiagnosticLocation::createBegin(CS->getStmt(), SM, LC)); } } diff --git a/lib/StaticAnalyzer/Checkers/ArrayBoundChecker.cpp b/lib/StaticAnalyzer/Checkers/ArrayBoundChecker.cpp index eb9665a4a343..6935c5f1c192 100644 --- a/lib/StaticAnalyzer/Checkers/ArrayBoundChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/ArrayBoundChecker.cpp @@ -27,11 +27,12 @@ class ArrayBoundChecker : public Checker<check::Location> { mutable llvm::OwningPtr<BuiltinBug> BT; public: - void checkLocation(SVal l, bool isLoad, CheckerContext &C) const; + void checkLocation(SVal l, bool isLoad, const Stmt* S, + CheckerContext &C) const; }; } -void ArrayBoundChecker::checkLocation(SVal l, bool isLoad, +void ArrayBoundChecker::checkLocation(SVal l, bool isLoad, const Stmt* LoadS, CheckerContext &C) const { // Check for out of bound array element access. const MemRegion *R = l.getAsRegion(); @@ -50,15 +51,15 @@ void ArrayBoundChecker::checkLocation(SVal l, bool isLoad, if (Idx.isZeroConstant()) return; - const GRState *state = C.getState(); + const ProgramState *state = C.getState(); // Get the size of the array. DefinedOrUnknownSVal NumElements = C.getStoreManager().getSizeInElements(state, ER->getSuperRegion(), ER->getValueType()); - const GRState *StInBound = state->assumeInBound(Idx, NumElements, true); - const GRState *StOutBound = state->assumeInBound(Idx, NumElements, false); + const ProgramState *StInBound = state->assumeInBound(Idx, NumElements, true); + const ProgramState *StOutBound = state->assumeInBound(Idx, NumElements, false); if (StOutBound && !StInBound) { ExplodedNode *N = C.generateSink(StOutBound); if (!N) @@ -73,10 +74,10 @@ void ArrayBoundChecker::checkLocation(SVal l, bool isLoad, // reference is outside the range. // Generate a report for this bug. - RangedBugReport *report = - new RangedBugReport(*BT, BT->getDescription(), N); + BugReport *report = + new BugReport(*BT, BT->getDescription(), N); - report->addRange(C.getStmt()->getSourceRange()); + report->addRange(LoadS->getSourceRange()); C.EmitReport(report); return; } diff --git a/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp b/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp index 65a6e633dc8e..6175028a9b20 100644 --- a/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp +++ b/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp @@ -30,11 +30,12 @@ class ArrayBoundCheckerV2 : enum OOB_Kind { OOB_Precedes, OOB_Excedes }; - void reportOOB(CheckerContext &C, const GRState *errorState, + void reportOOB(CheckerContext &C, const ProgramState *errorState, OOB_Kind kind) const; public: - void checkLocation(SVal l, bool isLoad, CheckerContext &C) const; + void checkLocation(SVal l, bool isLoad, const Stmt*S, + CheckerContext &C) const; }; // FIXME: Eventually replace RegionRawOffset with this class. @@ -53,12 +54,12 @@ public: NonLoc getByteOffset() const { return cast<NonLoc>(byteOffset); } const SubRegion *getRegion() const { return baseRegion; } - static RegionRawOffsetV2 computeOffset(const GRState *state, + static RegionRawOffsetV2 computeOffset(const ProgramState *state, SValBuilder &svalBuilder, SVal location); void dump() const; - void dumpToStream(llvm::raw_ostream& os) const; + void dumpToStream(raw_ostream &os) const; }; } @@ -79,9 +80,10 @@ static SVal computeExtentBegin(SValBuilder &svalBuilder, } void ArrayBoundCheckerV2::checkLocation(SVal location, bool isLoad, + const Stmt* LoadS, CheckerContext &checkerContext) const { - // NOTE: Instead of using GRState::assumeInBound(), we are prototyping + // NOTE: Instead of using ProgramState::assumeInBound(), we are prototyping // some new logic here that reasons directly about memory region extents. // Once that logic is more mature, we can bring it back to assumeInBound() // for all clients to use. @@ -90,8 +92,8 @@ void ArrayBoundCheckerV2::checkLocation(SVal location, bool isLoad, // memory access is within the extent of the base region. Since we // have some flexibility in defining the base region, we can achieve // various levels of conservatism in our buffer overflow checking. - const GRState *state = checkerContext.getState(); - const GRState *originalState = state; + const ProgramState *state = checkerContext.getState(); + const ProgramState *originalState = state; SValBuilder &svalBuilder = checkerContext.getSValBuilder(); const RegionRawOffsetV2 &rawOffset = @@ -116,7 +118,7 @@ void ArrayBoundCheckerV2::checkLocation(SVal location, bool isLoad, if (!lowerBoundToCheck) return; - const GRState *state_precedesLowerBound, *state_withinLowerBound; + const ProgramState *state_precedesLowerBound, *state_withinLowerBound; llvm::tie(state_precedesLowerBound, state_withinLowerBound) = state->assume(*lowerBoundToCheck); @@ -148,7 +150,7 @@ void ArrayBoundCheckerV2::checkLocation(SVal location, bool isLoad, if (!upperboundToCheck) break; - const GRState *state_exceedsUpperBound, *state_withinUpperBound; + const ProgramState *state_exceedsUpperBound, *state_withinUpperBound; llvm::tie(state_exceedsUpperBound, state_withinUpperBound) = state->assume(*upperboundToCheck); @@ -168,7 +170,7 @@ void ArrayBoundCheckerV2::checkLocation(SVal location, bool isLoad, } void ArrayBoundCheckerV2::reportOOB(CheckerContext &checkerContext, - const GRState *errorState, + const ProgramState *errorState, OOB_Kind kind) const { ExplodedNode *errorNode = checkerContext.generateSink(errorState); @@ -187,14 +189,14 @@ void ArrayBoundCheckerV2::reportOOB(CheckerContext &checkerContext, << (kind == OOB_Precedes ? "(accessed memory precedes memory block)" : "(access exceeds upper limit of memory block)"); - checkerContext.EmitReport(new RangedBugReport(*BT, os.str(), errorNode)); + checkerContext.EmitReport(new BugReport(*BT, os.str(), errorNode)); } void RegionRawOffsetV2::dump() const { dumpToStream(llvm::errs()); } -void RegionRawOffsetV2::dumpToStream(llvm::raw_ostream& os) const { +void RegionRawOffsetV2::dumpToStream(raw_ostream &os) const { os << "raw_offset_v2{" << getRegion() << ',' << getByteOffset() << '}'; } @@ -219,7 +221,7 @@ static inline SVal getValue(SVal val, SValBuilder &svalBuilder) { // Scale a base value by a scaling factor, and return the scaled // value as an SVal. Used by 'computeOffset'. -static inline SVal scaleValue(const GRState *state, +static inline SVal scaleValue(const ProgramState *state, NonLoc baseVal, CharUnits scaling, SValBuilder &sb) { return sb.evalBinOpNN(state, BO_Mul, baseVal, @@ -229,7 +231,7 @@ static inline SVal scaleValue(const GRState *state, // Add an SVal to another, treating unknown and undefined values as // summing to UnknownVal. Used by 'computeOffset'. -static SVal addValue(const GRState *state, SVal x, SVal y, +static SVal addValue(const ProgramState *state, SVal x, SVal y, SValBuilder &svalBuilder) { // We treat UnknownVals and UndefinedVals the same here because we // only care about computing offsets. @@ -243,7 +245,7 @@ static SVal addValue(const GRState *state, SVal x, SVal y, /// Compute a raw byte offset from a base region. Used for array bounds /// checking. -RegionRawOffsetV2 RegionRawOffsetV2::computeOffset(const GRState *state, +RegionRawOffsetV2 RegionRawOffsetV2::computeOffset(const ProgramState *state, SValBuilder &svalBuilder, SVal location) { diff --git a/lib/StaticAnalyzer/Checkers/AttrNonNullChecker.cpp b/lib/StaticAnalyzer/Checkers/AttrNonNullChecker.cpp index d88a111e9a56..8296eb93c5ae 100644 --- a/lib/StaticAnalyzer/Checkers/AttrNonNullChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/AttrNonNullChecker.cpp @@ -33,12 +33,12 @@ public: void AttrNonNullChecker::checkPreStmt(const CallExpr *CE, CheckerContext &C) const { - const GRState *state = C.getState(); + const ProgramState *state = C.getState(); // Check if the callee has a 'nonnull' attribute. SVal X = state->getSVal(CE->getCallee()); - const FunctionDecl* FD = X.getAsFunctionDecl(); + const FunctionDecl *FD = X.getAsFunctionDecl(); if (!FD) return; @@ -85,7 +85,7 @@ void AttrNonNullChecker::checkPreStmt(const CallExpr *CE, } ConstraintManager &CM = C.getConstraintManager(); - const GRState *stateNotNull, *stateNull; + const ProgramState *stateNotNull, *stateNull; llvm::tie(stateNotNull, stateNull) = CM.assumeDual(state, *DV); if (stateNull && !stateNotNull) { @@ -100,16 +100,15 @@ void AttrNonNullChecker::checkPreStmt(const CallExpr *CE, BT.reset(new BugType("Argument with 'nonnull' attribute passed null", "API")); - EnhancedBugReport *R = - new EnhancedBugReport(*BT, - "Null pointer passed as an argument to a " - "'nonnull' parameter", errorNode); + BugReport *R = + new BugReport(*BT, "Null pointer passed as an argument to a " + "'nonnull' parameter", errorNode); // Highlight the range of the argument that was null. const Expr *arg = *I; R->addRange(arg->getSourceRange()); - R->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, arg); - + R->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(errorNode, + arg)); // Emit the bug report. C.EmitReport(R); } diff --git a/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp b/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp index 9fc8163ab8b9..08cff0fd4292 100644 --- a/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp +++ b/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp @@ -20,7 +20,8 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/GRState.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h" #include "clang/AST/DeclObjC.h" @@ -49,7 +50,7 @@ static const char* GetReceiverNameType(const ObjCMessage &msg) { } static bool isReceiverClassOrSuperclass(const ObjCInterfaceDecl *ID, - llvm::StringRef ClassName) { + StringRef ClassName) { if (ID->getIdentifier()->getName() == ClassName) return true; @@ -92,7 +93,7 @@ void NilArgChecker::WarnNilArg(CheckerContext &C, os << "Argument to '" << GetReceiverNameType(msg) << "' method '" << msg.getSelector().getAsString() << "' cannot be nil"; - RangedBugReport *R = new RangedBugReport(*BT, os.str(), N); + BugReport *R = new BugReport(*BT, os.str(), N); R->addRange(msg.getArgSourceRange(Arg)); C.EmitReport(R); } @@ -114,7 +115,7 @@ void NilArgChecker::checkPreObjCMessage(ObjCMessage msg, // lexical comparisons. std::string NameStr = S.getAsString(); - llvm::StringRef Name(NameStr); + StringRef Name(NameStr); assert(!Name.empty()); // FIXME: Checking for initWithFormat: will not work in most cases @@ -148,7 +149,7 @@ public: void checkPreStmt(const CallExpr *CE, CheckerContext &C) const; private: - void EmitError(const TypedRegion* R, const Expr* Ex, + void EmitError(const TypedRegion* R, const Expr *Ex, uint64_t SourceSize, uint64_t TargetSize, uint64_t NumberKind); }; } // end anonymous namespace @@ -194,7 +195,7 @@ namespace { }; } -static Optional<uint64_t> GetCFNumberSize(ASTContext& Ctx, uint64_t i) { +static Optional<uint64_t> GetCFNumberSize(ASTContext &Ctx, uint64_t i) { static const unsigned char FixedSize[] = { 8, 16, 32, 64, 32, 64 }; if (i < kCFNumberCharType) @@ -248,10 +249,10 @@ static const char* GetCFNumberTypeStr(uint64_t i) { void CFNumberCreateChecker::checkPreStmt(const CallExpr *CE, CheckerContext &C) const { - const Expr* Callee = CE->getCallee(); - const GRState *state = C.getState(); + const Expr *Callee = CE->getCallee(); + const ProgramState *state = C.getState(); SVal CallV = state->getSVal(Callee); - const FunctionDecl* FD = CallV.getAsFunctionDecl(); + const FunctionDecl *FD = CallV.getAsFunctionDecl(); if (!FD) return; @@ -290,7 +291,7 @@ void CFNumberCreateChecker::checkPreStmt(const CallExpr *CE, if (!LV) return; - const TypedRegion* R = dyn_cast<TypedRegion>(LV->stripCasts()); + const TypedValueRegion* R = dyn_cast<TypedValueRegion>(LV->stripCasts()); if (!R) return; @@ -335,7 +336,7 @@ void CFNumberCreateChecker::checkPreStmt(const CallExpr *CE, if (!BT) BT.reset(new APIMisuse("Bad use of CFNumberCreate")); - RangedBugReport *report = new RangedBugReport(*BT, os.str(), N); + BugReport *report = new BugReport(*BT, os.str(), N); report->addRange(CE->getArg(2)->getSourceRange()); C.EmitReport(report); } @@ -351,21 +352,21 @@ class CFRetainReleaseChecker : public Checker< check::PreStmt<CallExpr> > { mutable IdentifierInfo *Retain, *Release; public: CFRetainReleaseChecker(): Retain(0), Release(0) {} - void checkPreStmt(const CallExpr* CE, CheckerContext& C) const; + void checkPreStmt(const CallExpr *CE, CheckerContext &C) const; }; } // end anonymous namespace -void CFRetainReleaseChecker::checkPreStmt(const CallExpr* CE, - CheckerContext& C) const { +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; // Get the function declaration of the callee. - const GRState* state = C.getState(); + const ProgramState *state = C.getState(); SVal X = state->getSVal(CE->getCallee()); - const FunctionDecl* FD = X.getAsFunctionDecl(); + const FunctionDecl *FD = X.getAsFunctionDecl(); if (!FD) return; @@ -400,7 +401,7 @@ void CFRetainReleaseChecker::checkPreStmt(const CallExpr* CE, DefinedOrUnknownSVal ArgIsNull = svalBuilder.evalEQ(state, zero, *DefArgVal); // Are they equal? - const GRState *stateTrue, *stateFalse; + const ProgramState *stateTrue, *stateFalse; llvm::tie(stateTrue, stateFalse) = state->assume(ArgIsNull); if (stateTrue && !stateFalse) { @@ -412,9 +413,9 @@ void CFRetainReleaseChecker::checkPreStmt(const CallExpr* CE, ? "Null pointer argument in call to CFRetain" : "Null pointer argument in call to CFRelease"; - EnhancedBugReport *report = new EnhancedBugReport(*BT, description, N); + BugReport *report = new BugReport(*BT, description, N); report->addRange(Arg->getSourceRange()); - report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, Arg); + report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, Arg)); C.EmitReport(report); return; } @@ -471,7 +472,7 @@ void ClassReleaseChecker::checkPreObjCMessage(ObjCMessage msg, "of class '" << Class->getName() << "' and not the class directly"; - RangedBugReport *report = new RangedBugReport(*BT, os.str(), N); + BugReport *report = new BugReport(*BT, os.str(), N); report->addRange(msg.getSourceRange()); C.EmitReport(report); } @@ -586,7 +587,7 @@ void VariadicMethodTypeChecker::checkPreObjCMessage(ObjCMessage msg, // Verify that all arguments have Objective-C types. llvm::Optional<ExplodedNode*> errorNode; - const GRState *state = C.getState(); + const ProgramState *state = C.getState(); for (unsigned I = variadicArgsBegin; I != variadicArgsEnd; ++I) { QualType ArgTy = msg.getArgType(I); @@ -629,7 +630,7 @@ void VariadicMethodTypeChecker::checkPreObjCMessage(ObjCMessage msg, << "' should be an Objective-C pointer type, not '" << ArgTy.getAsString() << "'"; - RangedBugReport *R = new RangedBugReport(*BT, os.str(), + BugReport *R = new BugReport(*BT, os.str(), errorNode.getValue()); R->addRange(msg.getArgSourceRange(I)); C.EmitReport(R); diff --git a/lib/StaticAnalyzer/Checkers/BuiltinFunctionChecker.cpp b/lib/StaticAnalyzer/Checkers/BuiltinFunctionChecker.cpp index 12ac652ba060..a57d03175c5f 100644 --- a/lib/StaticAnalyzer/Checkers/BuiltinFunctionChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/BuiltinFunctionChecker.cpp @@ -31,7 +31,7 @@ public: bool BuiltinFunctionChecker::evalCall(const CallExpr *CE, CheckerContext &C) const{ - const GRState *state = C.getState(); + const ProgramState *state = C.getState(); const Expr *Callee = CE->getCallee(); SVal L = state->getSVal(Callee); const FunctionDecl *FD = L.getAsFunctionDecl(); @@ -57,7 +57,7 @@ bool BuiltinFunctionChecker::evalCall(const CallExpr *CE, // FIXME: Refactor into StoreManager itself? MemRegionManager& RM = C.getStoreManager().getRegionManager(); const AllocaRegion* R = - RM.getAllocaRegion(CE, C.getNodeBuilder().getCurrentBlockCount(), + RM.getAllocaRegion(CE, C.getCurrentBlockCount(), C.getPredecessor()->getLocationContext()); // Set the extent of the region in bytes. This enables us to use the diff --git a/lib/StaticAnalyzer/Checkers/CMakeLists.txt b/lib/StaticAnalyzer/Checkers/CMakeLists.txt index 8dc7f385a58d..3e0d0946daaf 100644 --- a/lib/StaticAnalyzer/Checkers/CMakeLists.txt +++ b/lib/StaticAnalyzer/Checkers/CMakeLists.txt @@ -22,7 +22,7 @@ add_clang_library(clangStaticAnalyzerCheckers CheckSecuritySyntaxOnly.cpp CheckSizeofPointer.cpp ChrootChecker.cpp - ClangSACheckerProvider.cpp + ClangCheckers.cpp DeadStoresChecker.cpp DebugCheckers.cpp DereferenceChecker.cpp @@ -31,8 +31,10 @@ add_clang_library(clangStaticAnalyzerCheckers IdempotentOperationChecker.cpp IteratorsChecker.cpp LLVMConventionsChecker.cpp + MacOSKeychainAPIChecker.cpp MacOSXAPIChecker.cpp MallocChecker.cpp + MallocOverflowSecurityChecker.cpp NSAutoreleasePoolChecker.cpp NSErrorChecker.cpp NoReturnFunctionChecker.cpp @@ -43,6 +45,7 @@ add_clang_library(clangStaticAnalyzerCheckers PointerArithChecker.cpp PointerSubChecker.cpp PthreadLockChecker.cpp + RetainCountChecker.cpp ReturnPointerRangeChecker.cpp ReturnUndefChecker.cpp StackAddrEscapeChecker.cpp diff --git a/lib/StaticAnalyzer/Checkers/CStringChecker.cpp b/lib/StaticAnalyzer/Checkers/CStringChecker.cpp index c5dac5d21626..1625219fc877 100644 --- a/lib/StaticAnalyzer/Checkers/CStringChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/CStringChecker.cpp @@ -1,4 +1,4 @@ -//= CStringChecker.h - Checks calls to C string functions ----------*- C++ -*-// +//= CStringChecker.cpp - Checks calls to C string functions --------*- C++ -*-// // // The LLVM Compiler Infrastructure // @@ -17,7 +17,7 @@ #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/GRStateTrait.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" #include "llvm/ADT/StringSwitch.h" using namespace clang; @@ -40,14 +40,15 @@ public: 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 checkLiveSymbols(const ProgramState *state, SymbolReaper &SR) const; void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const; - bool wantsRegionChangeUpdate(const GRState *state) const; + bool wantsRegionChangeUpdate(const ProgramState *state) const; - const GRState *checkRegionChanges(const GRState *state, - const StoreManager::InvalidatedSymbols *, - const MemRegion * const *Begin, - const MemRegion * const *End) const; + const ProgramState * + checkRegionChanges(const ProgramState *state, + const StoreManager::InvalidatedSymbols *, + ArrayRef<const MemRegion *> ExplicitRegions, + ArrayRef<const MemRegion *> Regions) const; typedef void (CStringChecker::*FnCheck)(CheckerContext &, const CallExpr *) const; @@ -57,8 +58,10 @@ public: void evalMemmove(CheckerContext &C, const CallExpr *CE) const; void evalBcopy(CheckerContext &C, const CallExpr *CE) const; void evalCopyCommon(CheckerContext &C, const CallExpr *CE, - const GRState *state, - const Expr *Size, const Expr *Source, const Expr *Dest, + const ProgramState *state, + const Expr *Size, + const Expr *Source, + const Expr *Dest, bool Restricted = false, bool IsMempcpy = false) const; @@ -66,14 +69,18 @@ public: void evalstrLength(CheckerContext &C, const CallExpr *CE) const; void evalstrnLength(CheckerContext &C, const CallExpr *CE) const; - void evalstrLengthCommon(CheckerContext &C, const CallExpr *CE, + void evalstrLengthCommon(CheckerContext &C, + const CallExpr *CE, bool IsStrnlen = false) const; 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 isBounded, bool isAppending) const; + void evalStrcpyCommon(CheckerContext &C, + const CallExpr *CE, + bool returnEnd, + bool isBounded, + bool isAppending) const; void evalStrcat(CheckerContext &C, const CallExpr *CE) const; void evalStrncat(CheckerContext &C, const CallExpr *CE) const; @@ -82,64 +89,85 @@ public: void evalStrncmp(CheckerContext &C, const CallExpr *CE) const; void evalStrcasecmp(CheckerContext &C, const CallExpr *CE) const; void evalStrncasecmp(CheckerContext &C, const CallExpr *CE) const; - void evalStrcmpCommon(CheckerContext &C, const CallExpr *CE, - bool isBounded = false, bool ignoreCase = false) const; + void evalStrcmpCommon(CheckerContext &C, + const CallExpr *CE, + bool isBounded = false, + bool ignoreCase = false) const; // Utility methods - std::pair<const GRState*, const GRState*> + std::pair<const ProgramState*, const ProgramState*> static assumeZero(CheckerContext &C, - const GRState *state, SVal V, QualType Ty); + const ProgramState *state, SVal V, QualType Ty); - static const GRState *setCStringLength(const GRState *state, - const MemRegion *MR, SVal strLength); + static const ProgramState *setCStringLength(const ProgramState *state, + const MemRegion *MR, + SVal strLength); static SVal getCStringLengthForRegion(CheckerContext &C, - const GRState *&state, - const Expr *Ex, const MemRegion *MR, + const ProgramState *&state, + const Expr *Ex, + const MemRegion *MR, bool hypothetical); - SVal getCStringLength(CheckerContext &C, const GRState *&state, - const Expr *Ex, SVal Buf, + SVal getCStringLength(CheckerContext &C, + const ProgramState *&state, + const Expr *Ex, + SVal Buf, bool hypothetical = false) const; const StringLiteral *getCStringLiteral(CheckerContext &C, - const GRState *&state, + const ProgramState *&state, const Expr *expr, SVal val) const; - static const GRState *InvalidateBuffer(CheckerContext &C, - const GRState *state, - const Expr *Ex, SVal V); + static const ProgramState *InvalidateBuffer(CheckerContext &C, + const ProgramState *state, + const Expr *Ex, SVal V); - static bool SummarizeRegion(llvm::raw_ostream& os, ASTContext& Ctx, + static bool SummarizeRegion(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; - const GRState *CheckLocation(CheckerContext &C, const GRState *state, - const Expr *S, SVal l, - const char *message = NULL) const; - const GRState *CheckBufferAccess(CheckerContext &C, const GRState *state, - const Expr *Size, - const Expr *FirstBuf, - const Expr *SecondBuf, - const char *firstMessage = NULL, - const char *secondMessage = NULL, - bool WarnAboutSize = false) const; - const GRState *CheckBufferAccess(CheckerContext &C, const GRState *state, - const Expr *Size, const Expr *Buf, - const char *message = NULL, - bool WarnAboutSize = false) const { + const ProgramState *checkNonNull(CheckerContext &C, + const ProgramState *state, + const Expr *S, + SVal l) const; + const ProgramState *CheckLocation(CheckerContext &C, + const ProgramState *state, + const Expr *S, + SVal l, + const char *message = NULL) const; + const ProgramState *CheckBufferAccess(CheckerContext &C, + const ProgramState *state, + const Expr *Size, + const Expr *FirstBuf, + const Expr *SecondBuf, + const char *firstMessage = NULL, + const char *secondMessage = NULL, + bool WarnAboutSize = false) const; + + const ProgramState *CheckBufferAccess(CheckerContext &C, + const ProgramState *state, + const Expr *Size, + const Expr *Buf, + const char *message = NULL, + bool WarnAboutSize = false) const { // This is a convenience override. return CheckBufferAccess(C, state, Size, Buf, NULL, message, NULL, WarnAboutSize); } - const GRState *CheckOverlap(CheckerContext &C, const GRState *state, - const Expr *Size, const Expr *First, - const Expr *Second) const; - void emitOverlapBug(CheckerContext &C, const GRState *state, - const Stmt *First, const Stmt *Second) const; - const GRState *checkAdditionOverflow(CheckerContext &C, const GRState *state, - NonLoc left, NonLoc right) const; + const ProgramState *CheckOverlap(CheckerContext &C, + const ProgramState *state, + const Expr *Size, + const Expr *First, + const Expr *Second) const; + void emitOverlapBug(CheckerContext &C, + const ProgramState *state, + const Stmt *First, + const Stmt *Second) const; + + const ProgramState *checkAdditionOverflow(CheckerContext &C, + const ProgramState *state, + NonLoc left, + NonLoc right) const; }; class CStringLength { @@ -151,8 +179,8 @@ public: namespace clang { namespace ento { template <> - struct GRStateTrait<CStringLength> - : public GRStatePartialTrait<CStringLength::EntryMap> { + struct ProgramStateTrait<CStringLength> + : public ProgramStatePartialTrait<CStringLength::EntryMap> { static void *GDMIndex() { return CStringChecker::getTag(); } }; } @@ -162,26 +190,26 @@ namespace ento { // Individual checks and utility methods. //===----------------------------------------------------------------------===// -std::pair<const GRState*, const GRState*> -CStringChecker::assumeZero(CheckerContext &C, const GRState *state, SVal V, +std::pair<const ProgramState*, const ProgramState*> +CStringChecker::assumeZero(CheckerContext &C, const ProgramState *state, SVal V, QualType Ty) { DefinedSVal *val = dyn_cast<DefinedSVal>(&V); if (!val) - return std::pair<const GRState*, const GRState *>(state, state); + return std::pair<const ProgramState*, const ProgramState *>(state, state); SValBuilder &svalBuilder = C.getSValBuilder(); DefinedOrUnknownSVal zero = svalBuilder.makeZeroVal(Ty); return state->assume(svalBuilder.evalEQ(state, *val, zero)); } -const GRState *CStringChecker::checkNonNull(CheckerContext &C, - const GRState *state, +const ProgramState *CStringChecker::checkNonNull(CheckerContext &C, + const ProgramState *state, const Expr *S, SVal l) const { // If a previous check has failed, propagate the failure. if (!state) return NULL; - const GRState *stateNull, *stateNonNull; + const ProgramState *stateNull, *stateNonNull; llvm::tie(stateNull, stateNonNull) = assumeZero(C, state, l, S->getType()); if (stateNull && !stateNonNull) { @@ -200,10 +228,10 @@ const GRState *CStringChecker::checkNonNull(CheckerContext &C, // Generate a report for this bug. BuiltinBug *BT = static_cast<BuiltinBug*>(BT_Null.get()); - EnhancedBugReport *report = new EnhancedBugReport(*BT, os.str(), N); + BugReport *report = new BugReport(*BT, os.str(), N); report->addRange(S->getSourceRange()); - report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, S); + report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, S)); C.EmitReport(report); return NULL; } @@ -214,8 +242,8 @@ const GRState *CStringChecker::checkNonNull(CheckerContext &C, } // FIXME: This was originally copied from ArrayBoundChecker.cpp. Refactor? -const GRState *CStringChecker::CheckLocation(CheckerContext &C, - const GRState *state, +const ProgramState *CStringChecker::CheckLocation(CheckerContext &C, + const ProgramState *state, const Expr *S, SVal l, const char *warningMsg) const { // If a previous check has failed, propagate the failure. @@ -244,8 +272,8 @@ const GRState *CStringChecker::CheckLocation(CheckerContext &C, // Get the index of the accessed element. DefinedOrUnknownSVal Idx = cast<DefinedOrUnknownSVal>(ER->getIndex()); - const GRState *StInBound = state->assumeInBound(Idx, Size, true); - const GRState *StOutBound = state->assumeInBound(Idx, Size, false); + const ProgramState *StInBound = state->assumeInBound(Idx, Size, true); + const ProgramState *StOutBound = state->assumeInBound(Idx, Size, false); if (StOutBound && !StInBound) { ExplodedNode *N = C.generateSink(StOutBound); if (!N) @@ -258,9 +286,9 @@ const GRState *CStringChecker::CheckLocation(CheckerContext &C, BuiltinBug *BT = static_cast<BuiltinBug*>(BT_Bounds.get()); // Generate a report for this bug. - RangedBugReport *report; + BugReport *report; if (warningMsg) { - report = new RangedBugReport(*BT, warningMsg, N); + report = new BugReport(*BT, warningMsg, N); } else { assert(CurrentFunctionDescription); assert(CurrentFunctionDescription[0] != '\0'); @@ -270,7 +298,7 @@ const GRState *CStringChecker::CheckLocation(CheckerContext &C, os << (char)toupper(CurrentFunctionDescription[0]) << &CurrentFunctionDescription[1] << " accesses out-of-bound array element"; - report = new RangedBugReport(*BT, os.str(), N); + report = new BugReport(*BT, os.str(), N); } // FIXME: It would be nice to eventually make this diagnostic more clear, @@ -287,8 +315,8 @@ const GRState *CStringChecker::CheckLocation(CheckerContext &C, return StInBound; } -const GRState *CStringChecker::CheckBufferAccess(CheckerContext &C, - const GRState *state, +const ProgramState *CStringChecker::CheckBufferAccess(CheckerContext &C, + const ProgramState *state, const Expr *Size, const Expr *FirstBuf, const Expr *SecondBuf, @@ -359,8 +387,8 @@ const GRState *CStringChecker::CheckBufferAccess(CheckerContext &C, return state; } -const GRState *CStringChecker::CheckOverlap(CheckerContext &C, - const GRState *state, +const ProgramState *CStringChecker::CheckOverlap(CheckerContext &C, + const ProgramState *state, const Expr *Size, const Expr *First, const Expr *Second) const { @@ -372,7 +400,7 @@ const GRState *CStringChecker::CheckOverlap(CheckerContext &C, if (!state) return NULL; - const GRState *stateTrue, *stateFalse; + const ProgramState *stateTrue, *stateFalse; // Get the buffer values and make sure they're known locations. SVal firstVal = state->getSVal(First); @@ -470,7 +498,7 @@ const GRState *CStringChecker::CheckOverlap(CheckerContext &C, return stateFalse; } -void CStringChecker::emitOverlapBug(CheckerContext &C, const GRState *state, +void CStringChecker::emitOverlapBug(CheckerContext &C, const ProgramState *state, const Stmt *First, const Stmt *Second) const { ExplodedNode *N = C.generateSink(state); if (!N) @@ -480,8 +508,8 @@ void CStringChecker::emitOverlapBug(CheckerContext &C, const GRState *state, BT_Overlap.reset(new BugType("Unix API", "Improper arguments")); // Generate a report for this bug. - RangedBugReport *report = - new RangedBugReport(*BT_Overlap, + BugReport *report = + new BugReport(*BT_Overlap, "Arguments must not be overlapping buffers", N); report->addRange(First->getSourceRange()); report->addRange(Second->getSourceRange()); @@ -489,8 +517,8 @@ void CStringChecker::emitOverlapBug(CheckerContext &C, const GRState *state, C.EmitReport(report); } -const GRState *CStringChecker::checkAdditionOverflow(CheckerContext &C, - const GRState *state, +const ProgramState *CStringChecker::checkAdditionOverflow(CheckerContext &C, + const ProgramState *state, NonLoc left, NonLoc right) const { // If a previous check has failed, propagate the failure. @@ -521,7 +549,7 @@ const GRState *CStringChecker::checkAdditionOverflow(CheckerContext &C, SVal willOverflow = svalBuilder.evalBinOpNN(state, BO_GT, left, *maxMinusRightNL, cmpTy); - const GRState *stateOverflow, *stateOkay; + const ProgramState *stateOverflow, *stateOkay; llvm::tie(stateOverflow, stateOkay) = state->assume(cast<DefinedOrUnknownSVal>(willOverflow)); @@ -557,7 +585,7 @@ const GRState *CStringChecker::checkAdditionOverflow(CheckerContext &C, return state; } -const GRState *CStringChecker::setCStringLength(const GRState *state, +const ProgramState *CStringChecker::setCStringLength(const ProgramState *state, const MemRegion *MR, SVal strLength) { assert(!strLength.isUndef() && "Attempt to set an undefined string length"); @@ -598,7 +626,7 @@ const GRState *CStringChecker::setCStringLength(const GRState *state, } SVal CStringChecker::getCStringLengthForRegion(CheckerContext &C, - const GRState *&state, + const ProgramState *&state, const Expr *Ex, const MemRegion *MR, bool hypothetical) { @@ -610,7 +638,7 @@ SVal CStringChecker::getCStringLengthForRegion(CheckerContext &C, } // Otherwise, get a new symbol and update the state. - unsigned Count = C.getNodeBuilder().getCurrentBlockCount(); + unsigned Count = C.getCurrentBlockCount(); SValBuilder &svalBuilder = C.getSValBuilder(); QualType sizeTy = svalBuilder.getContext().getSizeType(); SVal strLength = svalBuilder.getMetadataSymbolVal(CStringChecker::getTag(), @@ -622,7 +650,7 @@ SVal CStringChecker::getCStringLengthForRegion(CheckerContext &C, return strLength; } -SVal CStringChecker::getCStringLength(CheckerContext &C, const GRState *&state, +SVal CStringChecker::getCStringLength(CheckerContext &C, const ProgramState *&state, const Expr *Ex, SVal Buf, bool hypothetical) const { const MemRegion *MR = Buf.getAsRegion(); @@ -644,7 +672,7 @@ SVal CStringChecker::getCStringLength(CheckerContext &C, const GRState *&state, << "', which is not a null-terminated string"; // Generate a report for this bug. - EnhancedBugReport *report = new EnhancedBugReport(*BT_NotCString, + BugReport *report = new BugReport(*BT_NotCString, os.str(), N); report->addRange(Ex->getSourceRange()); @@ -705,7 +733,7 @@ SVal CStringChecker::getCStringLength(CheckerContext &C, const GRState *&state, os << "not a null-terminated string"; // Generate a report for this bug. - EnhancedBugReport *report = new EnhancedBugReport(*BT_NotCString, + BugReport *report = new BugReport(*BT_NotCString, os.str(), N); report->addRange(Ex->getSourceRange()); @@ -717,7 +745,7 @@ SVal CStringChecker::getCStringLength(CheckerContext &C, const GRState *&state, } const StringLiteral *CStringChecker::getCStringLiteral(CheckerContext &C, - const GRState *&state, const Expr *expr, SVal val) const { + const ProgramState *&state, const Expr *expr, SVal val) const { // Get the memory region pointed to by the val. const MemRegion *bufRegion = val.getAsRegion(); @@ -736,8 +764,8 @@ const StringLiteral *CStringChecker::getCStringLiteral(CheckerContext &C, return strRegion->getStringLiteral(); } -const GRState *CStringChecker::InvalidateBuffer(CheckerContext &C, - const GRState *state, +const ProgramState *CStringChecker::InvalidateBuffer(CheckerContext &C, + const ProgramState *state, const Expr *E, SVal V) { Loc *L = dyn_cast<Loc>(&V); if (!L) @@ -757,8 +785,8 @@ const GRState *CStringChecker::InvalidateBuffer(CheckerContext &C, } // Invalidate this region. - unsigned Count = C.getNodeBuilder().getCurrentBlockCount(); - return state->invalidateRegion(R, E, Count, NULL); + unsigned Count = C.getCurrentBlockCount(); + return state->invalidateRegions(R, E, Count); } // If we have a non-region value by chance, just remove the binding. @@ -767,17 +795,15 @@ const GRState *CStringChecker::InvalidateBuffer(CheckerContext &C, return state->unbindLoc(*L); } -bool CStringChecker::SummarizeRegion(llvm::raw_ostream& os, ASTContext& Ctx, +bool CStringChecker::SummarizeRegion(raw_ostream &os, ASTContext &Ctx, const MemRegion *MR) { - const TypedRegion *TR = dyn_cast<TypedRegion>(MR); - if (!TR) - return false; + const TypedValueRegion *TVR = dyn_cast<TypedValueRegion>(MR); - switch (TR->getKind()) { + switch (MR->getKind()) { case MemRegion::FunctionTextRegionKind: { - const FunctionDecl *FD = cast<FunctionTextRegion>(TR)->getDecl(); + const FunctionDecl *FD = cast<FunctionTextRegion>(MR)->getDecl(); if (FD) - os << "the address of the function '" << FD << "'"; + os << "the address of the function '" << *FD << '\''; else os << "the address of a function"; return true; @@ -790,16 +816,16 @@ bool CStringChecker::SummarizeRegion(llvm::raw_ostream& os, ASTContext& Ctx, return true; case MemRegion::CXXThisRegionKind: case MemRegion::CXXTempObjectRegionKind: - os << "a C++ temp object of type " << TR->getValueType().getAsString(); + os << "a C++ temp object of type " << TVR->getValueType().getAsString(); return true; case MemRegion::VarRegionKind: - os << "a variable of type" << TR->getValueType().getAsString(); + os << "a variable of type" << TVR->getValueType().getAsString(); return true; case MemRegion::FieldRegionKind: - os << "a field of type " << TR->getValueType().getAsString(); + os << "a field of type " << TVR->getValueType().getAsString(); return true; case MemRegion::ObjCIvarRegionKind: - os << "an instance variable of type " << TR->getValueType().getAsString(); + os << "an instance variable of type " << TVR->getValueType().getAsString(); return true; default: return false; @@ -812,7 +838,7 @@ bool CStringChecker::SummarizeRegion(llvm::raw_ostream& os, ASTContext& Ctx, void CStringChecker::evalCopyCommon(CheckerContext &C, const CallExpr *CE, - const GRState *state, + const ProgramState *state, const Expr *Size, const Expr *Dest, const Expr *Source, bool Restricted, bool IsMempcpy) const { @@ -822,7 +848,7 @@ void CStringChecker::evalCopyCommon(CheckerContext &C, SVal sizeVal = state->getSVal(Size); QualType sizeTy = Size->getType(); - const GRState *stateZeroSize, *stateNonZeroSize; + const ProgramState *stateZeroSize, *stateNonZeroSize; llvm::tie(stateZeroSize, stateNonZeroSize) = assumeZero(C, state, sizeVal, sizeTy); @@ -887,7 +913,7 @@ void CStringChecker::evalCopyCommon(CheckerContext &C, } else { // If we don't know how much we copied, we can at least // conjure a return value for later. - unsigned Count = C.getNodeBuilder().getCurrentBlockCount(); + unsigned Count = C.getCurrentBlockCount(); SVal result = C.getSValBuilder().getConjuredSymbolVal(NULL, CE, Count); state = state->BindExpr(CE, result); @@ -914,7 +940,7 @@ 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); - const GRState *state = C.getState(); + const ProgramState *state = C.getState(); evalCopyCommon(C, CE, state, CE->getArg(2), Dest, CE->getArg(1), true); } @@ -923,7 +949,7 @@ void CStringChecker::evalMempcpy(CheckerContext &C, const CallExpr *CE) const { // void *mempcpy(void *restrict dst, const void *restrict src, size_t n); // The return value is a pointer to the byte following the last written byte. const Expr *Dest = CE->getArg(0); - const GRState *state = C.getState(); + const ProgramState *state = C.getState(); evalCopyCommon(C, CE, state, CE->getArg(2), Dest, CE->getArg(1), true, true); } @@ -932,7 +958,7 @@ 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); - const GRState *state = C.getState(); + const ProgramState *state = C.getState(); evalCopyCommon(C, CE, state, CE->getArg(2), Dest, CE->getArg(1)); } @@ -951,14 +977,14 @@ void CStringChecker::evalMemcmp(CheckerContext &C, const CallExpr *CE) const { const Expr *Right = CE->getArg(1); const Expr *Size = CE->getArg(2); - const GRState *state = C.getState(); + const ProgramState *state = C.getState(); SValBuilder &svalBuilder = C.getSValBuilder(); // See if the size argument is zero. SVal sizeVal = state->getSVal(Size); QualType sizeTy = Size->getType(); - const GRState *stateZeroSize, *stateNonZeroSize; + const ProgramState *stateZeroSize, *stateNonZeroSize; llvm::tie(stateZeroSize, stateNonZeroSize) = assumeZero(C, state, sizeVal, sizeTy); @@ -981,7 +1007,7 @@ void CStringChecker::evalMemcmp(CheckerContext &C, const CallExpr *CE) const { // See if they are the same. DefinedOrUnknownSVal SameBuf = svalBuilder.evalEQ(state, LV, RV); - const GRState *StSameBuf, *StNotSameBuf; + const ProgramState *StSameBuf, *StNotSameBuf; llvm::tie(StSameBuf, StNotSameBuf) = state->assume(SameBuf); // If the two arguments might be the same buffer, we know the result is 0, @@ -1002,7 +1028,7 @@ void CStringChecker::evalMemcmp(CheckerContext &C, const CallExpr *CE) const { state = CheckBufferAccess(C, state, Size, Left, Right); if (state) { // The return value is the comparison result, which we don't know. - unsigned Count = C.getNodeBuilder().getCurrentBlockCount(); + unsigned Count = C.getCurrentBlockCount(); SVal CmpV = svalBuilder.getConjuredSymbolVal(NULL, CE, Count); state = state->BindExpr(CE, CmpV); C.addTransition(state); @@ -1026,13 +1052,13 @@ void CStringChecker::evalstrnLength(CheckerContext &C, void CStringChecker::evalstrLengthCommon(CheckerContext &C, const CallExpr *CE, bool IsStrnlen) const { CurrentFunctionDescription = "string length function"; - const GRState *state = C.getState(); + const ProgramState *state = C.getState(); if (IsStrnlen) { const Expr *maxlenExpr = CE->getArg(1); SVal maxlenVal = state->getSVal(maxlenExpr); - const GRState *stateZeroSize, *stateNonZeroSize; + const ProgramState *stateZeroSize, *stateNonZeroSize; llvm::tie(stateZeroSize, stateNonZeroSize) = assumeZero(C, state, maxlenVal, maxlenExpr->getType()); @@ -1084,7 +1110,7 @@ void CStringChecker::evalstrLengthCommon(CheckerContext &C, const CallExpr *CE, NonLoc *maxlenValNL = dyn_cast<NonLoc>(&maxlenVal); if (strLengthNL && maxlenValNL) { - const GRState *stateStringTooLong, *stateStringNotTooLong; + const ProgramState *stateStringTooLong, *stateStringNotTooLong; // Check if the strLength is greater than the maxlen. llvm::tie(stateStringTooLong, stateStringNotTooLong) = @@ -1108,7 +1134,7 @@ void CStringChecker::evalstrLengthCommon(CheckerContext &C, const CallExpr *CE, // no guarantee the full string length will actually be returned. // All we know is the return value is the min of the string length // and the limit. This is better than nothing. - unsigned Count = C.getNodeBuilder().getCurrentBlockCount(); + unsigned Count = C.getCurrentBlockCount(); result = C.getSValBuilder().getConjuredSymbolVal(NULL, CE, Count); NonLoc *resultNL = cast<NonLoc>(&result); @@ -1136,7 +1162,7 @@ void CStringChecker::evalstrLengthCommon(CheckerContext &C, const CallExpr *CE, // If we don't know the length of the string, conjure a return // value, so it can be used in constraints, at least. if (result.isUnknown()) { - unsigned Count = C.getNodeBuilder().getCurrentBlockCount(); + unsigned Count = C.getCurrentBlockCount(); result = C.getSValBuilder().getConjuredSymbolVal(NULL, CE, Count); } } @@ -1191,7 +1217,7 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE, bool returnEnd, bool isBounded, bool isAppending) const { CurrentFunctionDescription = "string copy function"; - const GRState *state = C.getState(); + const ProgramState *state = C.getState(); // Check that the destination is non-null. const Expr *Dst = CE->getArg(0); @@ -1241,7 +1267,7 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE, // If we know both values, we might be able to figure out how much // we're copying. if (strLengthNL && lenValNL) { - const GRState *stateSourceTooLong, *stateSourceNotTooLong; + const ProgramState *stateSourceTooLong, *stateSourceNotTooLong; // Check if the max number to copy is less than the length of the src. // If the bound is equal to the source length, strncpy won't null- @@ -1480,7 +1506,7 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE, // If this is a stpcpy-style copy, but we were unable to check for a buffer // overflow, we still need a result. Conjure a return value. if (returnEnd && Result.isUnknown()) { - unsigned Count = C.getNodeBuilder().getCurrentBlockCount(); + unsigned Count = C.getCurrentBlockCount(); Result = svalBuilder.getConjuredSymbolVal(NULL, CE, Count); } @@ -1514,7 +1540,7 @@ void CStringChecker::evalStrncasecmp(CheckerContext &C, void CStringChecker::evalStrcmpCommon(CheckerContext &C, const CallExpr *CE, bool isBounded, bool ignoreCase) const { CurrentFunctionDescription = "string comparison function"; - const GRState *state = C.getState(); + const ProgramState *state = C.getState(); // Check that the first string is non-null const Expr *s1 = CE->getArg(0); @@ -1549,7 +1575,7 @@ void CStringChecker::evalStrcmpCommon(CheckerContext &C, const CallExpr *CE, // See if they are the same. SValBuilder &svalBuilder = C.getSValBuilder(); DefinedOrUnknownSVal SameBuf = svalBuilder.evalEQ(state, LV, RV); - const GRState *StSameBuf, *StNotSameBuf; + const ProgramState *StSameBuf, *StNotSameBuf; llvm::tie(StSameBuf, StNotSameBuf) = state->assume(SameBuf); // If the two arguments might be the same buffer, we know the result is 0, @@ -1575,8 +1601,8 @@ void CStringChecker::evalStrcmpCommon(CheckerContext &C, const CallExpr *CE, bool canComputeResult = false; if (s1StrLiteral && s2StrLiteral) { - llvm::StringRef s1StrRef = s1StrLiteral->getString(); - llvm::StringRef s2StrRef = s2StrLiteral->getString(); + StringRef s1StrRef = s1StrLiteral->getString(); + StringRef s2StrRef = s2StrLiteral->getString(); if (isBounded) { // Get the max number of characters to compare. @@ -1598,11 +1624,11 @@ void CStringChecker::evalStrcmpCommon(CheckerContext &C, const CallExpr *CE, if (canComputeResult) { // Real strcmp stops at null characters. size_t s1Term = s1StrRef.find('\0'); - if (s1Term != llvm::StringRef::npos) + if (s1Term != StringRef::npos) s1StrRef = s1StrRef.substr(0, s1Term); size_t s2Term = s2StrRef.find('\0'); - if (s2Term != llvm::StringRef::npos) + if (s2Term != StringRef::npos) s2StrRef = s2StrRef.substr(0, s2Term); // Use StringRef's comparison methods to compute the actual result. @@ -1624,7 +1650,7 @@ void CStringChecker::evalStrcmpCommon(CheckerContext &C, const CallExpr *CE, if (!canComputeResult) { // Conjure a symbolic value. It's the best we can do. - unsigned Count = C.getNodeBuilder().getCurrentBlockCount(); + unsigned Count = C.getCurrentBlockCount(); SVal resultVal = svalBuilder.getConjuredSymbolVal(NULL, CE, Count); state = state->BindExpr(CE, resultVal); } @@ -1640,7 +1666,7 @@ void CStringChecker::evalStrcmpCommon(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(); + const ProgramState *state = C.getState(); const Expr *Callee = CE->getCallee(); const FunctionDecl *FD = state->getSVal(Callee).getAsFunctionDecl(); @@ -1651,7 +1677,7 @@ bool CStringChecker::evalCall(const CallExpr *CE, CheckerContext &C) const { IdentifierInfo *II = FD->getIdentifier(); if (!II) // if no identifier, not a simple C function return false; - llvm::StringRef Name = II->getName(); + StringRef Name = II->getName(); if (Name.startswith("__builtin_")) Name = Name.substr(10); @@ -1689,7 +1715,7 @@ bool CStringChecker::evalCall(const CallExpr *CE, CheckerContext &C) const { void CStringChecker::checkPreStmt(const DeclStmt *DS, CheckerContext &C) const { // Record string length for char a[] = "abc"; - const GRState *state = C.getState(); + const ProgramState *state = C.getState(); for (DeclStmt::const_decl_iterator I = DS->decl_begin(), E = DS->decl_end(); I != E; ++I) { @@ -1723,16 +1749,16 @@ void CStringChecker::checkPreStmt(const DeclStmt *DS, CheckerContext &C) const { C.addTransition(state); } -bool CStringChecker::wantsRegionChangeUpdate(const GRState *state) const { +bool CStringChecker::wantsRegionChangeUpdate(const ProgramState *state) const { CStringLength::EntryMap Entries = state->get<CStringLength>(); return !Entries.isEmpty(); } -const GRState * -CStringChecker::checkRegionChanges(const GRState *state, +const ProgramState * +CStringChecker::checkRegionChanges(const ProgramState *state, const StoreManager::InvalidatedSymbols *, - const MemRegion * const *Begin, - const MemRegion * const *End) const { + ArrayRef<const MemRegion *> ExplicitRegions, + ArrayRef<const MemRegion *> Regions) const { CStringLength::EntryMap Entries = state->get<CStringLength>(); if (Entries.isEmpty()) return state; @@ -1741,8 +1767,9 @@ CStringChecker::checkRegionChanges(const GRState *state, llvm::SmallPtrSet<const MemRegion *, 32> SuperRegions; // First build sets for the changed regions and their super-regions. - for ( ; Begin != End; ++Begin) { - const MemRegion *MR = *Begin; + for (ArrayRef<const MemRegion *>::iterator + I = Regions.begin(), E = Regions.end(); I != E; ++I) { + const MemRegion *MR = *I; Invalidated.insert(MR); SuperRegions.insert(MR); @@ -1779,7 +1806,7 @@ CStringChecker::checkRegionChanges(const GRState *state, return state->set<CStringLength>(Entries); } -void CStringChecker::checkLiveSymbols(const GRState *state, +void CStringChecker::checkLiveSymbols(const ProgramState *state, SymbolReaper &SR) const { // Mark all symbols in our string length map as valid. CStringLength::EntryMap Entries = state->get<CStringLength>(); @@ -1799,7 +1826,7 @@ void CStringChecker::checkDeadSymbols(SymbolReaper &SR, if (!SR.hasDeadSymbols()) return; - const GRState *state = C.getState(); + const ProgramState *state = C.getState(); CStringLength::EntryMap Entries = state->get<CStringLength>(); if (Entries.isEmpty()) return; diff --git a/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp b/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp index 6c3dfacec331..4db6ac0181a0 100644 --- a/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp @@ -16,6 +16,7 @@ #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/AST/ParentMap.h" #include "clang/Basic/TargetInfo.h" @@ -47,7 +48,8 @@ private: void emitNilReceiverBug(CheckerContext &C, const ObjCMessage &msg, ExplodedNode *N) const; - void HandleNilReceiver(CheckerContext &C, const GRState *state, + void HandleNilReceiver(CheckerContext &C, + const ProgramState *state, ObjCMessage msg) const; static void LazyInit_BT(const char *desc, llvm::OwningPtr<BugType> &BT) { @@ -63,9 +65,9 @@ void CallAndMessageChecker::EmitBadCall(BugType *BT, CheckerContext &C, if (!N) return; - EnhancedBugReport *R = new EnhancedBugReport(*BT, BT->getName(), N); - R->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, - bugreporter::GetCalleeExpr(N)); + BugReport *R = new BugReport(*BT, BT->getName(), N); + R->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, + bugreporter::GetCalleeExpr(N))); C.EmitReport(R); } @@ -91,10 +93,10 @@ bool CallAndMessageChecker::PreVisitProcessArg(CheckerContext &C, LazyInit_BT(BT_desc, BT); // Generate a report for this bug. - EnhancedBugReport *R = new EnhancedBugReport(*BT, BT->getName(), N); + BugReport *R = new BugReport(*BT, BT->getName(), N); R->addRange(argRange); if (argEx) - R->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, argEx); + R->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, argEx)); C.EmitReport(R); } return true; @@ -105,7 +107,7 @@ bool CallAndMessageChecker::PreVisitProcessArg(CheckerContext &C, class FindUninitializedField { public: - llvm::SmallVector<const FieldDecl *, 10> FieldChain; + SmallVector<const FieldDecl *, 10> FieldChain; private: ASTContext &C; StoreManager &StoreMgr; @@ -116,7 +118,7 @@ bool CallAndMessageChecker::PreVisitProcessArg(CheckerContext &C, MemRegionManager &mrMgr, Store s) : C(c), StoreMgr(storeMgr), MrMgr(mrMgr), store(s) {} - bool Find(const TypedRegion *R) { + bool Find(const TypedValueRegion *R) { QualType T = R->getValueType(); if (const RecordType *RT = T->getAsStructureType()) { const RecordDecl *RD = RT->getDecl()->getDefinition(); @@ -157,23 +159,23 @@ bool CallAndMessageChecker::PreVisitProcessArg(CheckerContext &C, os << "Passed-by-value struct argument contains uninitialized data"; if (F.FieldChain.size() == 1) - os << " (e.g., field: '" << F.FieldChain[0] << "')"; + os << " (e.g., field: '" << *F.FieldChain[0] << "')"; else { os << " (e.g., via the field chain: '"; bool first = true; - for (llvm::SmallVectorImpl<const FieldDecl *>::iterator + for (SmallVectorImpl<const FieldDecl *>::iterator DI = F.FieldChain.begin(), DE = F.FieldChain.end(); DI!=DE;++DI){ if (first) first = false; else os << '.'; - os << *DI; + os << **DI; } os << "')"; } // Generate a report for this bug. - EnhancedBugReport *R = new EnhancedBugReport(*BT, os.str(), N); + BugReport *R = new BugReport(*BT, os.str(), N); R->addRange(argRange); // FIXME: enhance track back for uninitialized value for arbitrary @@ -216,7 +218,7 @@ void CallAndMessageChecker::checkPreStmt(const CallExpr *CE, void CallAndMessageChecker::checkPreObjCMessage(ObjCMessage msg, CheckerContext &C) const { - const GRState *state = C.getState(); + const ProgramState *state = C.getState(); // FIXME: Handle 'super'? if (const Expr *receiver = msg.getInstanceReceiver()) { @@ -226,11 +228,11 @@ void CallAndMessageChecker::checkPreObjCMessage(ObjCMessage msg, if (!BT_msg_undef) BT_msg_undef.reset(new BuiltinBug("Receiver in message expression is " "an uninitialized value")); - EnhancedBugReport *R = - new EnhancedBugReport(*BT_msg_undef, BT_msg_undef->getName(), N); + BugReport *R = + new BugReport(*BT_msg_undef, BT_msg_undef->getName(), N); R->addRange(receiver->getSourceRange()); - R->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, - receiver); + R->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, + receiver)); C.EmitReport(R); } return; @@ -238,7 +240,7 @@ void CallAndMessageChecker::checkPreObjCMessage(ObjCMessage msg, // Bifurcate the state into nil and non-nil ones. DefinedOrUnknownSVal receiverVal = cast<DefinedOrUnknownSVal>(recVal); - const GRState *notNilState, *nilState; + const ProgramState *notNilState, *nilState; llvm::tie(notNilState, nilState) = state->assume(receiverVal); // Handle receiver must be nil. @@ -271,11 +273,11 @@ void CallAndMessageChecker::emitNilReceiverBug(CheckerContext &C, << "' is nil and returns a value of type '" << msg.getType(C.getASTContext()).getAsString() << "' that will be garbage"; - EnhancedBugReport *report = new EnhancedBugReport(*BT_msg_ret, os.str(), N); + BugReport *report = new BugReport(*BT_msg_ret, os.str(), N); if (const Expr *receiver = msg.getInstanceReceiver()) { report->addRange(receiver->getSourceRange()); - report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, - receiver); + report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, + receiver)); } C.EmitReport(report); } @@ -288,7 +290,7 @@ static bool supportsNilWithFloatRet(const llvm::Triple &triple) { } void CallAndMessageChecker::HandleNilReceiver(CheckerContext &C, - const GRState *state, + const ProgramState *state, ObjCMessage msg) const { ASTContext &Ctx = C.getASTContext(); @@ -303,7 +305,7 @@ void CallAndMessageChecker::HandleNilReceiver(CheckerContext &C, // have the "use of undefined value" be smarter about where the // undefined value came from. if (C.getPredecessor()->getParentMap().isConsumedExpr(msg.getOriginExpr())){ - if (ExplodedNode* N = C.generateSink(state)) + if (ExplodedNode *N = C.generateSink(state)) emitNilReceiverBug(C, msg, N); return; } @@ -322,13 +324,13 @@ void CallAndMessageChecker::HandleNilReceiver(CheckerContext &C, const uint64_t returnTypeSize = Ctx.getTypeSize(CanRetTy); if (voidPtrSize < returnTypeSize && - !(supportsNilWithFloatRet(Ctx.Target.getTriple()) && + !(supportsNilWithFloatRet(Ctx.getTargetInfo().getTriple()) && (Ctx.FloatTy == CanRetTy || Ctx.DoubleTy == CanRetTy || Ctx.LongDoubleTy == CanRetTy || Ctx.LongLongTy == CanRetTy || Ctx.UnsignedLongLongTy == CanRetTy))) { - if (ExplodedNode* N = C.generateSink(state)) + if (ExplodedNode *N = C.generateSink(state)) emitNilReceiverBug(C, msg, N); return; } diff --git a/lib/StaticAnalyzer/Checkers/CastSizeChecker.cpp b/lib/StaticAnalyzer/Checkers/CastSizeChecker.cpp index 585a87d498d3..84a9e6b3c528 100644 --- a/lib/StaticAnalyzer/Checkers/CastSizeChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/CastSizeChecker.cpp @@ -44,7 +44,7 @@ void CastSizeChecker::checkPreStmt(const CastExpr *CE,CheckerContext &C) const { if (ToPointeeTy->isIncompleteType()) return; - const GRState *state = C.getState(); + const ProgramState *state = C.getState(); const MemRegion *R = state->getSVal(E).getAsRegion(); if (R == 0) return; @@ -72,7 +72,7 @@ void CastSizeChecker::checkPreStmt(const CastExpr *CE,CheckerContext &C) const { BT.reset(new BuiltinBug("Cast region with wrong size.", "Cast a region whose size is not a multiple of the" " destination type size.")); - RangedBugReport *R = new RangedBugReport(*BT, BT->getDescription(), + BugReport *R = new BugReport(*BT, BT->getDescription(), errorNode); R->addRange(CE->getSourceRange()); C.EmitReport(R); diff --git a/lib/StaticAnalyzer/Checkers/CastToStructChecker.cpp b/lib/StaticAnalyzer/Checkers/CastToStructChecker.cpp index 3210b0a4bb61..c85521066711 100644 --- a/lib/StaticAnalyzer/Checkers/CastToStructChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/CastToStructChecker.cpp @@ -62,7 +62,7 @@ void CastToStructChecker::checkPreStmt(const CastExpr *CE, "Casting a non-structure type to a structure type " "and accessing a field can lead to memory access " "errors or data corruption.")); - RangedBugReport *R = new RangedBugReport(*BT,BT->getDescription(), N); + BugReport *R = new BugReport(*BT,BT->getDescription(), N); R->addRange(CE->getSourceRange()); C.EmitReport(R); } diff --git a/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp b/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp index 0c693a0bd1b6..c325bb1517f3 100644 --- a/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp +++ b/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp @@ -27,9 +27,9 @@ using namespace clang; using namespace ento; -static bool scan_dealloc(Stmt* S, Selector Dealloc) { +static bool scan_dealloc(Stmt *S, Selector Dealloc) { - if (ObjCMessageExpr* ME = dyn_cast<ObjCMessageExpr>(S)) + if (ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(S)) if (ME->getSelector() == Dealloc) { switch (ME->getReceiverKind()) { case ObjCMessageExpr::Instance: return false; @@ -48,26 +48,26 @@ static bool scan_dealloc(Stmt* S, Selector Dealloc) { return false; } -static bool scan_ivar_release(Stmt* S, ObjCIvarDecl* ID, - const ObjCPropertyDecl* PD, +static bool scan_ivar_release(Stmt *S, ObjCIvarDecl *ID, + const ObjCPropertyDecl *PD, Selector Release, IdentifierInfo* SelfII, - ASTContext& Ctx) { + ASTContext &Ctx) { // [mMyIvar release] - if (ObjCMessageExpr* ME = dyn_cast<ObjCMessageExpr>(S)) + if (ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(S)) if (ME->getSelector() == Release) if (ME->getInstanceReceiver()) - if (Expr* Receiver = ME->getInstanceReceiver()->IgnoreParenCasts()) - if (ObjCIvarRefExpr* E = dyn_cast<ObjCIvarRefExpr>(Receiver)) + if (Expr *Receiver = ME->getInstanceReceiver()->IgnoreParenCasts()) + if (ObjCIvarRefExpr *E = dyn_cast<ObjCIvarRefExpr>(Receiver)) if (E->getDecl() == ID) return true; // [self setMyIvar:nil]; - if (ObjCMessageExpr* ME = dyn_cast<ObjCMessageExpr>(S)) + if (ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(S)) if (ME->getInstanceReceiver()) - if (Expr* Receiver = ME->getInstanceReceiver()->IgnoreParenCasts()) - if (DeclRefExpr* E = dyn_cast<DeclRefExpr>(Receiver)) + if (Expr *Receiver = ME->getInstanceReceiver()->IgnoreParenCasts()) + if (DeclRefExpr *E = dyn_cast<DeclRefExpr>(Receiver)) if (E->getDecl()->getIdentifier() == SelfII) if (ME->getMethodDecl() == PD->getSetterMethodDecl() && ME->getNumArgs() == 1 && @@ -78,7 +78,7 @@ static bool scan_ivar_release(Stmt* S, ObjCIvarDecl* ID, // self.myIvar = nil; if (BinaryOperator* BO = dyn_cast<BinaryOperator>(S)) if (BO->isAssignmentOp()) - if (ObjCPropertyRefExpr* PRE = + if (ObjCPropertyRefExpr *PRE = dyn_cast<ObjCPropertyRefExpr>(BO->getLHS()->IgnoreParenCasts())) if (PRE->isExplicitProperty() && PRE->getExplicitProperty() == PD) if (BO->getRHS()->isNullPointerConstant(Ctx, @@ -96,13 +96,13 @@ static bool scan_ivar_release(Stmt* S, ObjCIvarDecl* ID, return false; } -static void checkObjCDealloc(const ObjCImplementationDecl* D, +static void checkObjCDealloc(const ObjCImplementationDecl *D, const LangOptions& LOpts, BugReporter& BR) { - assert (LOpts.getGCMode() != LangOptions::GCOnly); + assert (LOpts.getGC() != LangOptions::GCOnly); - ASTContext& Ctx = BR.getContext(); - const ObjCInterfaceDecl* ID = D->getClassInterface(); + ASTContext &Ctx = BR.getContext(); + const ObjCInterfaceDecl *ID = D->getClassInterface(); // Does the class contain any ivars that are pointers (or id<...>)? // If not, skip the check entirely. @@ -114,7 +114,7 @@ static void checkObjCDealloc(const ObjCImplementationDecl* D, for (ObjCInterfaceDecl::ivar_iterator I=ID->ivar_begin(), E=ID->ivar_end(); I!=E; ++I) { - ObjCIvarDecl* ID = *I; + ObjCIvarDecl *ID = *I; QualType T = ID->getType(); if (!T->isObjCObjectPointerType() || @@ -154,7 +154,7 @@ static void checkObjCDealloc(const ObjCImplementationDecl* D, // Get the "dealloc" selector. IdentifierInfo* II = &Ctx.Idents.get("dealloc"); Selector S = Ctx.Selectors.getSelector(0, &II); - ObjCMethodDecl* MD = 0; + ObjCMethodDecl *MD = 0; // Scan the instance methods for "dealloc". for (ObjCImplementationDecl::instmeth_iterator I = D->instmeth_begin(), @@ -166,9 +166,12 @@ static void checkObjCDealloc(const ObjCImplementationDecl* D, } } + PathDiagnosticLocation DLoc = + PathDiagnosticLocation::createBegin(D, BR.getSourceManager()); + if (!MD) { // No dealloc found. - const char* name = LOpts.getGCMode() == LangOptions::NonGC + const char* name = LOpts.getGC() == LangOptions::NonGC ? "missing -dealloc" : "missing -dealloc (Hybrid MM, non-GC)"; @@ -176,14 +179,14 @@ static void checkObjCDealloc(const ObjCImplementationDecl* D, llvm::raw_string_ostream os(buf); os << "Objective-C class '" << D << "' lacks a 'dealloc' instance method"; - BR.EmitBasicReport(name, os.str(), D->getLocStart()); + BR.EmitBasicReport(name, os.str(), DLoc); return; } // dealloc found. Scan for missing [super dealloc]. if (MD->getBody() && !scan_dealloc(MD->getBody(), S)) { - const char* name = LOpts.getGCMode() == LangOptions::NonGC + const char* name = LOpts.getGC() == LangOptions::NonGC ? "missing [super dealloc]" : "missing [super dealloc] (Hybrid MM, non-GC)"; @@ -193,7 +196,7 @@ static void checkObjCDealloc(const ObjCImplementationDecl* D, << "' does not send a 'dealloc' message to its super class" " (missing [super dealloc])"; - BR.EmitBasicReport(name, os.str(), D->getLocStart()); + BR.EmitBasicReport(name, os.str(), DLoc); return; } @@ -213,7 +216,7 @@ static void checkObjCDealloc(const ObjCImplementationDecl* D, if ((*I)->getPropertyImplementation() != ObjCPropertyImplDecl::Synthesize) continue; - ObjCIvarDecl* ID = (*I)->getPropertyIvarDecl(); + ObjCIvarDecl *ID = (*I)->getPropertyIvarDecl(); if (!ID) continue; @@ -221,13 +224,13 @@ static void checkObjCDealloc(const ObjCImplementationDecl* D, if (!T->isObjCObjectPointerType()) // Skip non-pointer ivars continue; - const ObjCPropertyDecl* PD = (*I)->getPropertyDecl(); + const ObjCPropertyDecl *PD = (*I)->getPropertyDecl(); if (!PD) continue; // ivars cannot be set via read-only properties, so we'll skip them if (PD->isReadOnly()) - continue; + continue; // ivar must be released if and only if the kind of setter was not 'assign' bool requiresRelease = PD->getSetterKind() != ObjCPropertyDecl::Assign; @@ -240,24 +243,27 @@ static void checkObjCDealloc(const ObjCImplementationDecl* D, llvm::raw_string_ostream os(buf); if (requiresRelease) { - name = LOpts.getGCMode() == LangOptions::NonGC + name = LOpts.getGC() == LangOptions::NonGC ? "missing ivar release (leak)" : "missing ivar release (Hybrid MM, non-GC)"; - os << "The '" << ID + os << "The '" << *ID << "' instance variable was retained by a synthesized property but " "wasn't released in 'dealloc'"; } else { - name = LOpts.getGCMode() == LangOptions::NonGC + name = LOpts.getGC() == LangOptions::NonGC ? "extra ivar release (use-after-release)" : "extra ivar release (Hybrid MM, non-GC)"; - os << "The '" << ID + os << "The '" << *ID << "' instance variable was not retained by a synthesized property " "but was released in 'dealloc'"; } - BR.EmitBasicReport(name, category, os.str(), (*I)->getLocation()); + PathDiagnosticLocation SDLoc = + PathDiagnosticLocation::createBegin((*I), BR.getSourceManager()); + + BR.EmitBasicReport(name, category, os.str(), SDLoc); } } } @@ -272,7 +278,7 @@ class ObjCDeallocChecker : public Checker< public: void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager& mgr, BugReporter &BR) const { - if (mgr.getLangOptions().getGCMode() == LangOptions::GCOnly) + if (mgr.getLangOptions().getGC() == LangOptions::GCOnly) return; checkObjCDealloc(cast<ObjCImplementationDecl>(D), mgr.getLangOptions(), BR); } diff --git a/lib/StaticAnalyzer/Checkers/CheckObjCInstMethSignature.cpp b/lib/StaticAnalyzer/Checkers/CheckObjCInstMethSignature.cpp index fec06a95350e..c076c1e39b08 100644 --- a/lib/StaticAnalyzer/Checkers/CheckObjCInstMethSignature.cpp +++ b/lib/StaticAnalyzer/Checkers/CheckObjCInstMethSignature.cpp @@ -28,7 +28,7 @@ using namespace clang; using namespace ento; static bool AreTypesCompatible(QualType Derived, QualType Ancestor, - ASTContext& C) { + ASTContext &C) { // Right now don't compare the compatibility of pointers. That involves // looking at subtyping relationships. FIXME: Future patch. @@ -51,36 +51,40 @@ static void CompareReturnTypes(const ObjCMethodDecl *MethDerived, llvm::raw_string_ostream os(sbuf); os << "The Objective-C class '" - << MethDerived->getClassInterface() + << *MethDerived->getClassInterface() << "', which is derived from class '" - << MethAncestor->getClassInterface() + << *MethAncestor->getClassInterface() << "', defines the instance method '" << MethDerived->getSelector().getAsString() << "' whose return type is '" << ResDerived.getAsString() << "'. A method with the same name (same selector) is also defined in " "class '" - << MethAncestor->getClassInterface() + << *MethAncestor->getClassInterface() << "' and has a return type of '" << ResAncestor.getAsString() << "'. These two types are incompatible, and may result in undefined " "behavior for clients of these classes."; + PathDiagnosticLocation MethDLoc = + PathDiagnosticLocation::createBegin(MethDerived, + BR.getSourceManager()); + BR.EmitBasicReport("Incompatible instance method return type", - os.str(), MethDerived->getLocStart()); + os.str(), MethDLoc); } } -static void CheckObjCInstMethSignature(const ObjCImplementationDecl* ID, +static void CheckObjCInstMethSignature(const ObjCImplementationDecl *ID, BugReporter& BR) { - const ObjCInterfaceDecl* D = ID->getClassInterface(); - const ObjCInterfaceDecl* C = D->getSuperClass(); + const ObjCInterfaceDecl *D = ID->getClassInterface(); + const ObjCInterfaceDecl *C = D->getSuperClass(); if (!C) return; - ASTContext& Ctx = BR.getContext(); + ASTContext &Ctx = BR.getContext(); // Build a DenseMap of the methods for quick querying. typedef llvm::DenseMap<Selector,ObjCMethodDecl*> MapTy; @@ -90,7 +94,7 @@ static void CheckObjCInstMethSignature(const ObjCImplementationDecl* ID, for (ObjCImplementationDecl::instmeth_iterator I=ID->instmeth_begin(), E=ID->instmeth_end(); I!=E; ++I) { - ObjCMethodDecl* M = *I; + ObjCMethodDecl *M = *I; IMeths[M->getSelector()] = M; ++NumMethods; } @@ -101,7 +105,7 @@ static void CheckObjCInstMethSignature(const ObjCImplementationDecl* ID, for (ObjCInterfaceDecl::instmeth_iterator I=C->instmeth_begin(), E=C->instmeth_end(); I!=E; ++I) { - ObjCMethodDecl* M = *I; + ObjCMethodDecl *M = *I; Selector S = M->getSelector(); MapTy::iterator MI = IMeths.find(S); @@ -110,7 +114,7 @@ static void CheckObjCInstMethSignature(const ObjCImplementationDecl* ID, continue; --NumMethods; - ObjCMethodDecl* MethDerived = MI->second; + ObjCMethodDecl *MethDerived = MI->second; MI->second = 0; CompareReturnTypes(MethDerived, M, BR, Ctx, ID); diff --git a/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp b/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp index 53810eed1255..bf7ba185b5aa 100644 --- a/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp +++ b/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp @@ -12,18 +12,20 @@ //===----------------------------------------------------------------------===// #include "ClangSACheckers.h" +#include "clang/Analysis/AnalysisContext.h" +#include "clang/AST/StmtVisitor.h" +#include "clang/Basic/TargetInfo.h" #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" -#include "clang/Basic/TargetInfo.h" -#include "clang/AST/StmtVisitor.h" -#include "llvm/Support/raw_ostream.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" #include "llvm/ADT/StringSwitch.h" +#include "llvm/Support/raw_ostream.h" using namespace clang; using namespace ento; static bool isArc4RandomAvailable(const ASTContext &Ctx) { - const llvm::Triple &T = Ctx.Target.getTriple(); + const llvm::Triple &T = Ctx.getTargetInfo().getTriple(); return T.getVendor() == llvm::Triple::Apple || T.getOS() == llvm::Triple::FreeBSD || T.getOS() == llvm::Triple::NetBSD || @@ -34,14 +36,16 @@ static bool isArc4RandomAvailable(const ASTContext &Ctx) { namespace { class WalkAST : public StmtVisitor<WalkAST> { BugReporter &BR; + AnalysisContext* AC; enum { num_setids = 6 }; IdentifierInfo *II_setid[num_setids]; const bool CheckRand; public: - WalkAST(BugReporter &br) : BR(br), II_setid(), - CheckRand(isArc4RandomAvailable(BR.getContext())) {} + WalkAST(BugReporter &br, AnalysisContext* ac) + : BR(br), AC(ac), II_setid(), + CheckRand(isArc4RandomAvailable(BR.getContext())) {} // Statement visitor methods. void VisitCallExpr(CallExpr *CE); @@ -52,7 +56,6 @@ public: void VisitChildren(Stmt *S); // Helpers. - IdentifierInfo *getIdentifier(IdentifierInfo *& II, const char *str); bool checkCall_strCommon(const CallExpr *CE, const FunctionDecl *FD); typedef void (WalkAST::*FnCheck)(const CallExpr *, @@ -67,22 +70,12 @@ public: void checkCall_strcat(const CallExpr *CE, const FunctionDecl *FD); void checkCall_rand(const CallExpr *CE, const FunctionDecl *FD); void checkCall_random(const CallExpr *CE, const FunctionDecl *FD); + void checkCall_vfork(const CallExpr *CE, const FunctionDecl *FD); void checkUncheckedReturnValue(CallExpr *CE); }; } // end anonymous namespace //===----------------------------------------------------------------------===// -// Helper methods. -//===----------------------------------------------------------------------===// - -IdentifierInfo *WalkAST::getIdentifier(IdentifierInfo *& II, const char *str) { - if (!II) - II = &BR.getContext().Idents.get(str); - - return II; -} - -//===----------------------------------------------------------------------===// // AST walking. //===----------------------------------------------------------------------===// @@ -103,7 +96,7 @@ void WalkAST::VisitCallExpr(CallExpr *CE) { IdentifierInfo *II = FD->getIdentifier(); if (!II) // if no identifier, not a simple C function return; - llvm::StringRef Name = II->getName(); + StringRef Name = II->getName(); if (Name.startswith("__builtin_")) Name = Name.substr(10); @@ -124,6 +117,7 @@ void WalkAST::VisitCallExpr(CallExpr *CE) { .Case("rand", &WalkAST::checkCall_rand) .Case("rand_r", &WalkAST::checkCall_rand) .Case("random", &WalkAST::checkCall_random) + .Case("vfork", &WalkAST::checkCall_vfork) .Default(NULL); // If the callee isn't defined, it is not of security concern. @@ -247,7 +241,7 @@ void WalkAST::checkLoopConditionForFloat(const ForStmt *FS) { // referenced the compared variable. const DeclRefExpr *drCond = vdLHS == drInc->getDecl() ? drLHS : drRHS; - llvm::SmallVector<SourceRange, 2> ranges; + SmallVector<SourceRange, 2> ranges; llvm::SmallString<256> sbuf; llvm::raw_svector_ostream os(sbuf); @@ -259,8 +253,11 @@ void WalkAST::checkLoopConditionForFloat(const ForStmt *FS) { ranges.push_back(drInc->getSourceRange()); const char *bugType = "Floating point variable used as loop counter"; + + PathDiagnosticLocation FSLoc = + PathDiagnosticLocation::createBegin(FS, BR.getSourceManager(), AC); BR.EmitBasicReport(bugType, "Security", os.str(), - FS->getLocStart(), ranges.data(), ranges.size()); + FSLoc, ranges.data(), ranges.size()); } //===----------------------------------------------------------------------===// @@ -290,11 +287,13 @@ void WalkAST::checkCall_gets(const CallExpr *CE, const FunctionDecl *FD) { // Issue a warning. SourceRange R = CE->getCallee()->getSourceRange(); + PathDiagnosticLocation CELoc = + PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); BR.EmitBasicReport("Potential buffer overflow in call to 'gets'", "Security", "Call to function 'gets' is extremely insecure as it can " "always result in a buffer overflow", - CE->getLocStart(), &R, 1); + CELoc, &R, 1); } //===----------------------------------------------------------------------===// @@ -326,11 +325,13 @@ void WalkAST::checkCall_getpw(const CallExpr *CE, const FunctionDecl *FD) { // Issue a warning. SourceRange R = CE->getCallee()->getSourceRange(); + PathDiagnosticLocation CELoc = + PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); BR.EmitBasicReport("Potential buffer overflow in call to 'getpw'", "Security", "The getpw() function is dangerous as it may overflow the " "provided buffer. It is obsoleted by getpwuid().", - CE->getLocStart(), &R, 1); + CELoc, &R, 1); } //===----------------------------------------------------------------------===// @@ -359,11 +360,13 @@ void WalkAST::checkCall_mktemp(const CallExpr *CE, const FunctionDecl *FD) { // Issue a waring. SourceRange R = CE->getCallee()->getSourceRange(); + PathDiagnosticLocation CELoc = + PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); BR.EmitBasicReport("Potential insecure temporary file in call 'mktemp'", "Security", "Call to function 'mktemp' is insecure as it always " "creates or uses insecure temporary file. Use 'mkstemp' instead", - CE->getLocStart(), &R, 1); + CELoc, &R, 1); } //===----------------------------------------------------------------------===// @@ -378,6 +381,8 @@ void WalkAST::checkCall_strcpy(const CallExpr *CE, const FunctionDecl *FD) { // Issue a warning. SourceRange R = CE->getCallee()->getSourceRange(); + PathDiagnosticLocation CELoc = + PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); BR.EmitBasicReport("Potential insecure memory buffer bounds restriction in " "call 'strcpy'", "Security", @@ -385,7 +390,7 @@ void WalkAST::checkCall_strcpy(const CallExpr *CE, const FunctionDecl *FD) { "provide bounding of the memory buffer. Replace " "unbounded copy functions with analogous functions that " "support length arguments such as 'strncpy'. CWE-119.", - CE->getLocStart(), &R, 1); + CELoc, &R, 1); } //===----------------------------------------------------------------------===// @@ -400,6 +405,8 @@ void WalkAST::checkCall_strcat(const CallExpr *CE, const FunctionDecl *FD) { // Issue a warning. SourceRange R = CE->getCallee()->getSourceRange(); + PathDiagnosticLocation CELoc = + PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); BR.EmitBasicReport("Potential insecure memory buffer bounds restriction in " "call 'strcat'", "Security", @@ -407,7 +414,7 @@ void WalkAST::checkCall_strcat(const CallExpr *CE, const FunctionDecl *FD) { "provide bounding of the memory buffer. Replace " "unbounded copy functions with analogous functions that " "support length arguments such as 'strncat'. CWE-119.", - CE->getLocStart(), &R, 1); + CELoc, &R, 1); } //===----------------------------------------------------------------------===// @@ -470,16 +477,18 @@ void WalkAST::checkCall_rand(const CallExpr *CE, const FunctionDecl *FD) { // Issue a warning. llvm::SmallString<256> buf1; llvm::raw_svector_ostream os1(buf1); - os1 << '\'' << FD << "' is a poor random number generator"; + os1 << '\'' << *FD << "' is a poor random number generator"; llvm::SmallString<256> buf2; llvm::raw_svector_ostream os2(buf2); - os2 << "Function '" << FD + os2 << "Function '" << *FD << "' is obsolete because it implements a poor random number generator." << " Use 'arc4random' instead"; SourceRange R = CE->getCallee()->getSourceRange(); - BR.EmitBasicReport(os1.str(), "Security", os2.str(),CE->getLocStart(), &R, 1); + PathDiagnosticLocation CELoc = + PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); + BR.EmitBasicReport(os1.str(), "Security", os2.str(), CELoc, &R, 1); } //===----------------------------------------------------------------------===// @@ -502,11 +511,33 @@ void WalkAST::checkCall_random(const CallExpr *CE, const FunctionDecl *FD) { // Issue a warning. SourceRange R = CE->getCallee()->getSourceRange(); + PathDiagnosticLocation CELoc = + PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); BR.EmitBasicReport("'random' is not a secure random number generator", "Security", "The 'random' function produces a sequence of values that " "an adversary may be able to predict. Use 'arc4random' " - "instead", CE->getLocStart(), &R, 1); + "instead", CELoc, &R, 1); +} + +//===----------------------------------------------------------------------===// +// Check: 'vfork' should not be used. +// POS33-C: Do not use vfork(). +//===----------------------------------------------------------------------===// + +void WalkAST::checkCall_vfork(const CallExpr *CE, const FunctionDecl *FD) { + // All calls to vfork() are insecure, issue a warning. + SourceRange R = CE->getCallee()->getSourceRange(); + PathDiagnosticLocation CELoc = + PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); + BR.EmitBasicReport("Potential insecure implementation-specific behavior in " + "call 'vfork'", + "Security", + "Call to function 'vfork' is insecure as it can lead to " + "denial of service situations in the parent process. " + "Replace calls to vfork with calls to the safer " + "'posix_spawn' function", + CELoc, &R, 1); } //===----------------------------------------------------------------------===// @@ -557,16 +588,18 @@ void WalkAST::checkUncheckedReturnValue(CallExpr *CE) { // Issue a warning. llvm::SmallString<256> buf1; llvm::raw_svector_ostream os1(buf1); - os1 << "Return value is not checked in call to '" << FD << '\''; + os1 << "Return value is not checked in call to '" << *FD << '\''; llvm::SmallString<256> buf2; llvm::raw_svector_ostream os2(buf2); - os2 << "The return value from the call to '" << FD - << "' is not checked. If an error occurs in '" << FD + os2 << "The return value from the call to '" << *FD + << "' is not checked. If an error occurs in '" << *FD << "', the following code may execute with unexpected privileges"; SourceRange R = CE->getCallee()->getSourceRange(); - BR.EmitBasicReport(os1.str(), "Security", os2.str(),CE->getLocStart(), &R, 1); + PathDiagnosticLocation CELoc = + PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); + BR.EmitBasicReport(os1.str(), "Security", os2.str(), CELoc, &R, 1); } //===----------------------------------------------------------------------===// @@ -578,7 +611,7 @@ class SecuritySyntaxChecker : public Checker<check::ASTCodeBody> { public: void checkASTCodeBody(const Decl *D, AnalysisManager& mgr, BugReporter &BR) const { - WalkAST walker(BR); + WalkAST walker(BR, mgr.getAnalysisContext(D)); walker.Visit(D->getBody()); } }; diff --git a/lib/StaticAnalyzer/Checkers/CheckSizeofPointer.cpp b/lib/StaticAnalyzer/Checkers/CheckSizeofPointer.cpp index abf53fd3db28..469be05fb71f 100644 --- a/lib/StaticAnalyzer/Checkers/CheckSizeofPointer.cpp +++ b/lib/StaticAnalyzer/Checkers/CheckSizeofPointer.cpp @@ -13,9 +13,10 @@ //===----------------------------------------------------------------------===// #include "ClangSACheckers.h" +#include "clang/AST/StmtVisitor.h" #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" -#include "clang/AST/StmtVisitor.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" using namespace clang; using namespace ento; @@ -23,9 +24,10 @@ using namespace ento; namespace { class WalkAST : public StmtVisitor<WalkAST> { BugReporter &BR; + AnalysisContext* AC; public: - WalkAST(BugReporter &br) : BR(br) {} + WalkAST(BugReporter &br, AnalysisContext* ac) : BR(br), AC(ac) {} void VisitUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *E); void VisitStmt(Stmt *S) { VisitChildren(S); } void VisitChildren(Stmt *S); @@ -59,11 +61,13 @@ void WalkAST::VisitUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *E) { return; SourceRange R = ArgEx->getSourceRange(); + PathDiagnosticLocation ELoc = + PathDiagnosticLocation::createBegin(E, BR.getSourceManager(), AC); BR.EmitBasicReport("Potential unintended use of sizeof() on pointer type", "Logic", "The code calls sizeof() on a pointer type. " "This can produce an unexpected result.", - E->getLocStart(), &R, 1); + ELoc, &R, 1); } } @@ -76,7 +80,7 @@ class SizeofPointerChecker : public Checker<check::ASTCodeBody> { public: void checkASTCodeBody(const Decl *D, AnalysisManager& mgr, BugReporter &BR) const { - WalkAST walker(BR); + WalkAST walker(BR, mgr.getAnalysisContext(D)); walker.Visit(D->getBody()); } }; diff --git a/lib/StaticAnalyzer/Checkers/Checkers.td b/lib/StaticAnalyzer/Checkers/Checkers.td index 2c196b597b7d..d53e0b887e75 100644 --- a/lib/StaticAnalyzer/Checkers/Checkers.td +++ b/lib/StaticAnalyzer/Checkers/Checkers.td @@ -10,41 +10,32 @@ include "clang/StaticAnalyzer/Checkers/CheckerBase.td" //===----------------------------------------------------------------------===// -// Groups. -//===----------------------------------------------------------------------===// - -def AllExperimental : CheckerGroup<"all-experimental">; - -//===----------------------------------------------------------------------===// // Packages. //===----------------------------------------------------------------------===// +def Experimental : Package<"experimental">; + def Core : Package<"core">; def CoreBuiltin : Package<"builtin">, InPackage<Core>; def CoreUninitialized : Package<"uninitialized">, InPackage<Core>; -def CoreExperimental : Package<"experimental">, InPackage<Core>, - InGroup<AllExperimental>, Hidden; +def CoreExperimental : Package<"core">, InPackage<Experimental>, Hidden; def Cplusplus : Package<"cplusplus">; -def CplusplusExperimental : Package<"experimental">, InPackage<Cplusplus>, - InGroup<AllExperimental>, Hidden; +def CplusplusExperimental : Package<"cplusplus">, InPackage<Experimental>, Hidden; def DeadCode : Package<"deadcode">; -def DeadCodeExperimental : Package<"experimental">, InPackage<DeadCode>, - InGroup<AllExperimental>, Hidden; +def DeadCodeExperimental : Package<"deadcode">, InPackage<Experimental>, Hidden; def Security : Package <"security">; -def SecurityExperimental : Package<"experimental">, InPackage<Security>, - InGroup<AllExperimental>, Hidden; +def SecurityExperimental : Package<"security">, InPackage<Experimental>, Hidden; def Unix : Package<"unix">; -def UnixExperimental : Package<"experimental">, InPackage<Unix>, - InGroup<AllExperimental>, Hidden; +def UnixExperimental : Package<"unix">, InPackage<Experimental>, Hidden; def OSX : Package<"osx">; +def OSXExperimental : Package<"osx">, InPackage<Experimental>, Hidden; def Cocoa : Package<"cocoa">, InPackage<OSX>; -def CocoaExperimental : Package<"experimental">, InPackage<Cocoa>, - InGroup<AllExperimental>, Hidden; +def CocoaExperimental : Package<"cocoa">, InPackage<OSXExperimental>, Hidden; def CoreFoundation : Package<"coreFoundation">, InPackage<OSX>; def LLVM : Package<"llvm">; @@ -220,6 +211,10 @@ def ReturnPointerRangeChecker : Checker<"ReturnPtrRange">, HelpText<"Check for an out-of-bound pointer being returned to callers">, DescFile<"ReturnPointerRangeChecker.cpp">; +def MallocOverflowSecurityChecker : Checker<"MallocOverflow">, + HelpText<"Check for overflows in the arguments to malloc()">, + DescFile<"MallocOverflowSecurityChecker.cpp">; + } // end "security.experimental" //===----------------------------------------------------------------------===// @@ -274,6 +269,11 @@ def OSAtomicChecker : Checker<"AtomicCAS">, HelpText<"Evaluate calls to OSAtomic functions">, DescFile<"OSAtomicChecker.cpp">; +def MacOSKeychainAPIChecker : Checker<"SecKeychainAPI">, + InPackage<OSX>, + HelpText<"Check for proper uses of Secure Keychain APIs">, + DescFile<"MacOSKeychainAPIChecker.cpp">; + } // end "macosx" let ParentPackage = Cocoa in { @@ -311,6 +311,10 @@ def NSErrorChecker : Checker<"NSError">, HelpText<"Check usage of NSError** parameters">, DescFile<"NSErrorChecker.cpp">; +def RetainCountChecker : Checker<"RetainCount">, + HelpText<"Check for leaks and improper reference count management">, + DescFile<"RetainCountChecker.cpp">; + } // end "cocoa" let ParentPackage = CocoaExperimental in { diff --git a/lib/StaticAnalyzer/Checkers/ChrootChecker.cpp b/lib/StaticAnalyzer/Checkers/ChrootChecker.cpp index 50b57d1ae497..3c9238190da4 100644 --- a/lib/StaticAnalyzer/Checkers/ChrootChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/ChrootChecker.cpp @@ -16,8 +16,8 @@ #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/GRState.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/GRStateTrait.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h" #include "llvm/ADT/ImmutableMap.h" using namespace clang; @@ -62,7 +62,7 @@ private: } // end anonymous namespace bool ChrootChecker::evalCall(const CallExpr *CE, CheckerContext &C) const { - const GRState *state = C.getState(); + const ProgramState *state = C.getState(); const Expr *Callee = CE->getCallee(); SVal L = state->getSVal(Callee); const FunctionDecl *FD = L.getAsFunctionDecl(); @@ -88,8 +88,8 @@ bool ChrootChecker::evalCall(const CallExpr *CE, CheckerContext &C) const { } void ChrootChecker::Chroot(CheckerContext &C, const CallExpr *CE) const { - const GRState *state = C.getState(); - GRStateManager &Mgr = state->getStateManager(); + const ProgramState *state = C.getState(); + ProgramStateManager &Mgr = state->getStateManager(); // Once encouter a chroot(), set the enum value ROOT_CHANGED directly in // the GDM. @@ -98,11 +98,11 @@ void ChrootChecker::Chroot(CheckerContext &C, const CallExpr *CE) const { } void ChrootChecker::Chdir(CheckerContext &C, const CallExpr *CE) const { - const GRState *state = C.getState(); - GRStateManager &Mgr = state->getStateManager(); + const ProgramState *state = C.getState(); + ProgramStateManager &Mgr = state->getStateManager(); // If there are no jail state in the GDM, just return. - const void* k = state->FindGDM(ChrootChecker::getTag()); + const void *k = state->FindGDM(ChrootChecker::getTag()); if (!k) return; @@ -125,7 +125,7 @@ void ChrootChecker::Chdir(CheckerContext &C, const CallExpr *CE) const { // Check the jail state before any function call except chroot and chdir(). void ChrootChecker::checkPreStmt(const CallExpr *CE, CheckerContext &C) const { - const GRState *state = C.getState(); + const ProgramState *state = C.getState(); const Expr *Callee = CE->getCallee(); SVal L = state->getSVal(Callee); const FunctionDecl *FD = L.getAsFunctionDecl(); @@ -143,7 +143,7 @@ void ChrootChecker::checkPreStmt(const CallExpr *CE, CheckerContext &C) const { return; // If jail state is ROOT_CHANGED, generate BugReport. - void* const* k = state->FindGDM(ChrootChecker::getTag()); + void *const* k = state->FindGDM(ChrootChecker::getTag()); if (k) if (isRootChanged((intptr_t) *k)) if (ExplodedNode *N = C.generateNode()) { diff --git a/lib/StaticAnalyzer/Checkers/ClangCheckers.cpp b/lib/StaticAnalyzer/Checkers/ClangCheckers.cpp new file mode 100644 index 000000000000..77a5a7226453 --- /dev/null +++ b/lib/StaticAnalyzer/Checkers/ClangCheckers.cpp @@ -0,0 +1,32 @@ +//===--- ClangCheckers.h - Provides builtin checkers ------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Checkers/ClangCheckers.h" +#include "clang/StaticAnalyzer/Core/CheckerRegistry.h" + +// FIXME: This is only necessary as long as there are checker registration +// functions that do additional work besides mgr.registerChecker<CLASS>(). +// The only checkers that currently do this are: +// - NSAutoreleasePoolChecker +// - NSErrorChecker +// - ObjCAtSyncChecker +// It's probably worth including this information in Checkers.td to minimize +// boilerplate code. +#include "ClangSACheckers.h" + +using namespace clang; +using namespace ento; + +void ento::registerBuiltinCheckers(CheckerRegistry ®istry) { +#define GET_CHECKERS +#define CHECKER(FULLNAME,CLASS,DESCFILE,HELPTEXT,GROUPINDEX,HIDDEN) \ + registry.addChecker(register##CLASS, FULLNAME, HELPTEXT); +#include "Checkers.inc" +#undef GET_CHECKERS +} diff --git a/lib/StaticAnalyzer/Checkers/ClangSACheckerProvider.cpp b/lib/StaticAnalyzer/Checkers/ClangSACheckerProvider.cpp deleted file mode 100644 index 291f8e01f4b9..000000000000 --- a/lib/StaticAnalyzer/Checkers/ClangSACheckerProvider.cpp +++ /dev/null @@ -1,289 +0,0 @@ -//===--- ClangSACheckerProvider.cpp - Clang SA Checkers Provider ----------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// Defines the CheckerProvider for the checkers defined in -// libclangStaticAnalyzerCheckers. -// -//===----------------------------------------------------------------------===// - -#include "ClangSACheckerProvider.h" -#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; - -namespace { - -/// \brief Provider for all the checkers in libclangStaticAnalyzerCheckers. -class ClangSACheckerProvider : public CheckerProvider { -public: - virtual void registerCheckers(CheckerManager &checkerMgr, - CheckerOptInfo *checkOpts, unsigned numCheckOpts); - virtual void printHelp(llvm::raw_ostream &OS); -}; - -} - -CheckerProvider *ento::createClangSACheckerProvider() { - return new ClangSACheckerProvider(); -} - -namespace { - -struct StaticCheckerInfoRec { - const char *FullName; - void (*RegFunc)(CheckerManager &mgr); - const char *HelpText; - int GroupIndex; - bool Hidden; -}; - -struct StaticPackageInfoRec { - const char *FullName; - int GroupIndex; - bool Hidden; -}; - -struct StaticGroupInfoRec { - const char *FullName; -}; - -} // end anonymous namespace. - -static const StaticPackageInfoRec StaticPackageInfo[] = { -#define GET_PACKAGES -#define PACKAGE(FULLNAME, GROUPINDEX, HIDDEN) \ - { FULLNAME, GROUPINDEX, HIDDEN }, -#include "Checkers.inc" - { 0, -1, 0 } -#undef PACKAGE -#undef GET_PACKAGES -}; - -static const unsigned NumPackages = sizeof(StaticPackageInfo) - / sizeof(StaticPackageInfoRec) - 1; - -static const StaticGroupInfoRec StaticGroupInfo[] = { -#define GET_GROUPS -#define GROUP(FULLNAME) \ - { FULLNAME }, -#include "Checkers.inc" - { 0 } -#undef GROUP -#undef GET_GROUPS -}; - -static const unsigned NumGroups = sizeof(StaticGroupInfo) - / sizeof(StaticGroupInfoRec) - 1; - -static const StaticCheckerInfoRec StaticCheckerInfo[] = { -#define GET_CHECKERS -#define CHECKER(FULLNAME,CLASS,DESCFILE,HELPTEXT,GROUPINDEX,HIDDEN) \ - { FULLNAME, register##CLASS, HELPTEXT, GROUPINDEX, HIDDEN }, -#include "Checkers.inc" - { 0, 0, 0, -1, 0} -#undef CHECKER -#undef GET_CHECKERS -}; - -static const unsigned NumCheckers = sizeof(StaticCheckerInfo) - / sizeof(StaticCheckerInfoRec) - 1; - -namespace { - -struct CheckNameOption { - const char *Name; - const short *Members; - const short *SubGroups; - bool Hidden; -}; - -} // end anonymous namespace. - -#define GET_MEMBER_ARRAYS -#include "Checkers.inc" -#undef GET_MEMBER_ARRAYS - -// The table of check name options, sorted by name for fast binary lookup. -static const CheckNameOption CheckNameTable[] = { -#define GET_CHECKNAME_TABLE -#include "Checkers.inc" -#undef GET_CHECKNAME_TABLE -}; -static const size_t - CheckNameTableSize = sizeof(CheckNameTable) / sizeof(CheckNameTable[0]); - -static bool CheckNameOptionCompare(const CheckNameOption &LHS, - const CheckNameOption &RHS) { - return strcmp(LHS.Name, RHS.Name) < 0; -} - -static void collectCheckers(const CheckNameOption *checkName, - bool enable, - llvm::DenseSet<const StaticCheckerInfoRec *> &checkers, - bool collectHidden) { - if (checkName->Hidden && !collectHidden) - return; - - if (const short *member = checkName->Members) { - if (enable) { - for (; *member != -1; ++member) - if (collectHidden || !StaticCheckerInfo[*member].Hidden) - checkers.insert(&StaticCheckerInfo[*member]); - } else { - for (; *member != -1; ++member) - checkers.erase(&StaticCheckerInfo[*member]); - } - } - - // Enable/disable all subgroups along with this one. - if (const short *subGroups = checkName->SubGroups) { - for (; *subGroups != -1; ++subGroups) { - const CheckNameOption *sub = &CheckNameTable[*subGroups]; - collectCheckers(sub, enable, checkers, collectHidden && !sub->Hidden); - } - } -} - -static void collectCheckers(CheckerOptInfo &opt, - llvm::DenseSet<const StaticCheckerInfoRec *> &checkers) { - const char *optName = opt.getName(); - CheckNameOption key = { optName, 0, 0, false }; - const CheckNameOption *found = - std::lower_bound(CheckNameTable, CheckNameTable + CheckNameTableSize, key, - CheckNameOptionCompare); - if (found == CheckNameTable + CheckNameTableSize || - strcmp(found->Name, optName) != 0) - return; // Check name not found. - - opt.claim(); - collectCheckers(found, opt.isEnabled(), checkers, /*collectHidden=*/true); -} - -void ClangSACheckerProvider::registerCheckers(CheckerManager &checkerMgr, - CheckerOptInfo *checkOpts, unsigned numCheckOpts) { - llvm::DenseSet<const StaticCheckerInfoRec *> enabledCheckers; - for (unsigned i = 0; i != numCheckOpts; ++i) - collectCheckers(checkOpts[i], enabledCheckers); - for (llvm::DenseSet<const StaticCheckerInfoRec *>::iterator - I = enabledCheckers.begin(), E = enabledCheckers.end(); I != E; ++I) { - (*I)->RegFunc(checkerMgr); - } -} - -//===----------------------------------------------------------------------===// -// Printing Help. -//===----------------------------------------------------------------------===// - -static void printPackageOption(llvm::raw_ostream &OS) { - // Find the maximum option length. - unsigned OptionFieldWidth = 0; - for (unsigned i = 0; i != NumPackages; ++i) { - // Limit the amount of padding we are willing to give up for alignment. - unsigned Length = strlen(StaticPackageInfo[i].FullName); - if (Length <= 30) - OptionFieldWidth = std::max(OptionFieldWidth, Length); - } - - const unsigned InitialPad = 2; - for (unsigned i = 0; i != NumPackages; ++i) { - const StaticPackageInfoRec &package = StaticPackageInfo[i]; - const std::string &Option = package.FullName; - int Pad = OptionFieldWidth - int(Option.size()); - OS.indent(InitialPad) << Option; - - if (package.GroupIndex != -1 || package.Hidden) { - // Break on long option names. - if (Pad < 0) { - OS << "\n"; - Pad = OptionFieldWidth + InitialPad; - } - OS.indent(Pad + 1) << "["; - if (package.GroupIndex != -1) { - OS << "Group=" << StaticGroupInfo[package.GroupIndex].FullName; - if (package.Hidden) - OS << ", "; - } - if (package.Hidden) - OS << "Hidden"; - OS << "]"; - } - - OS << "\n"; - } -} - -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; - const StaticCheckerInfoRec &checker = *I->second; - 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) << checker.HelpText; - - if (checker.GroupIndex != -1 || checker.Hidden) { - OS << " ["; - if (checker.GroupIndex != -1) { - OS << "Group=" << StaticGroupInfo[checker.GroupIndex].FullName; - if (checker.Hidden) - OS << ", "; - } - if (checker.Hidden) - OS << "Hidden"; - OS << "]"; - } - - OS << "\n"; - } -} - -void ClangSACheckerProvider::printHelp(llvm::raw_ostream &OS) { - OS << "USAGE: -analyzer-checker <CHECKER or PACKAGE or GROUP,...>\n"; - - OS << "\nGROUPS:\n"; - for (unsigned i = 0; i != NumGroups; ++i) - OS.indent(2) << StaticGroupInfo[i].FullName << "\n"; - - OS << "\nPACKAGES:\n"; - printPackageOption(OS); - - OS << "\nCHECKERS:\n"; - - // 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/ClangSACheckerProvider.h b/lib/StaticAnalyzer/Checkers/ClangSACheckerProvider.h deleted file mode 100644 index f6c80119fdc0..000000000000 --- a/lib/StaticAnalyzer/Checkers/ClangSACheckerProvider.h +++ /dev/null @@ -1,29 +0,0 @@ -//===--- ClangSACheckerProvider.h - Clang SA Checkers Provider --*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// Defines the entry point for creating the provider for the checkers defined -// in libclangStaticAnalyzerCheckers. -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_CLANG_SA_CHECKERS_CLANGSACHECKERPROVIDER_H -#define LLVM_CLANG_SA_CHECKERS_CLANGSACHECKERPROVIDER_H - -namespace clang { - -namespace ento { - class CheckerProvider; - -CheckerProvider *createClangSACheckerProvider(); - -} // end ento namespace - -} // end clang namespace - -#endif diff --git a/lib/StaticAnalyzer/Checkers/ClangSACheckers.h b/lib/StaticAnalyzer/Checkers/ClangSACheckers.h index 5524b0f53276..289ce8d8c3ba 100644 --- a/lib/StaticAnalyzer/Checkers/ClangSACheckers.h +++ b/lib/StaticAnalyzer/Checkers/ClangSACheckers.h @@ -19,6 +19,7 @@ namespace clang { namespace ento { class CheckerManager; +class CheckerRegistry; #define GET_CHECKERS #define CHECKER(FULLNAME,CLASS,CXXFILE,HELPTEXT,GROUPINDEX,HIDDEN) \ diff --git a/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp b/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp index ec2a88a55681..901af43c5f26 100644 --- a/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp @@ -50,7 +50,7 @@ void ReachableCode::computeReachableBlocks() { if (!cfg.getNumBlockIDs()) return; - llvm::SmallVector<const CFGBlock*, 10> worklist; + SmallVector<const CFGBlock*, 10> worklist; worklist.push_back(&cfg.getEntry()); while (!worklist.empty()) { @@ -68,12 +68,13 @@ void ReachableCode::computeReachableBlocks() { } namespace { -class DeadStoreObs : public LiveVariables::ObserverTy { +class DeadStoreObs : public LiveVariables::Observer { const CFG &cfg; ASTContext &Ctx; BugReporter& BR; + AnalysisContext* AC; ParentMap& Parents; - llvm::SmallPtrSet<VarDecl*, 20> Escaped; + llvm::SmallPtrSet<const VarDecl*, 20> Escaped; llvm::OwningPtr<ReachableCode> reachableCode; const CFGBlock *currentBlock; @@ -81,14 +82,15 @@ class DeadStoreObs : public LiveVariables::ObserverTy { public: DeadStoreObs(const CFG &cfg, ASTContext &ctx, - BugReporter& br, ParentMap& parents, - llvm::SmallPtrSet<VarDecl*, 20> &escaped) - : cfg(cfg), Ctx(ctx), BR(br), Parents(parents), + BugReporter& br, AnalysisContext* ac, ParentMap& parents, + llvm::SmallPtrSet<const VarDecl*, 20> &escaped) + : cfg(cfg), Ctx(ctx), BR(br), AC(ac), Parents(parents), Escaped(escaped), currentBlock(0) {} virtual ~DeadStoreObs() {} - void Report(VarDecl* V, DeadStoreKind dsk, SourceLocation L, SourceRange R) { + void Report(const VarDecl *V, DeadStoreKind dsk, + PathDiagnosticLocation L, SourceRange R) { if (Escaped.count(V)) return; @@ -102,26 +104,25 @@ public: if (!reachableCode->isReachable(currentBlock)) return; - const std::string &name = V->getNameAsString(); - - const char* BugType = 0; - std::string msg; + llvm::SmallString<64> buf; + llvm::raw_svector_ostream os(buf); + const char *BugType = 0; switch (dsk) { default: - assert(false && "Impossible dead store type."); + llvm_unreachable("Impossible dead store type."); case DeadInit: BugType = "Dead initialization"; - msg = "Value stored to '" + name + - "' during its initialization is never read"; + os << "Value stored to '" << *V + << "' during its initialization is never read"; break; case DeadIncrement: BugType = "Dead increment"; case Standard: if (!BugType) BugType = "Dead assignment"; - msg = "Value stored to '" + name + "' is never read"; + os << "Value stored to '" << *V << "' is never read"; break; case Enclosing: @@ -131,13 +132,12 @@ public: return; } - BR.EmitBasicReport(BugType, "Dead store", msg, L, R); + BR.EmitBasicReport(BugType, "Dead store", os.str(), L, R); } - void CheckVarDecl(VarDecl* VD, Expr* Ex, Expr* Val, + void CheckVarDecl(const VarDecl *VD, const Expr *Ex, const Expr *Val, DeadStoreKind dsk, - const LiveVariables::AnalysisDataTy& AD, - const LiveVariables::ValTy& Live) { + const LiveVariables::LivenessValues &Live) { if (!VD->hasLocalStorage()) return; @@ -146,30 +146,32 @@ public: if (VD->getType()->getAs<ReferenceType>()) return; - if (!Live(VD, AD) && - !(VD->getAttr<UnusedAttr>() || VD->getAttr<BlocksAttr>())) - Report(VD, dsk, Ex->getSourceRange().getBegin(), - Val->getSourceRange()); + if (!Live.isLive(VD) && + !(VD->getAttr<UnusedAttr>() || VD->getAttr<BlocksAttr>())) { + + PathDiagnosticLocation ExLoc = + PathDiagnosticLocation::createBegin(Ex, BR.getSourceManager(), AC); + Report(VD, dsk, ExLoc, Val->getSourceRange()); + } } - void CheckDeclRef(DeclRefExpr* DR, Expr* Val, DeadStoreKind dsk, - const LiveVariables::AnalysisDataTy& AD, - const LiveVariables::ValTy& Live) { - if (VarDecl* VD = dyn_cast<VarDecl>(DR->getDecl())) - CheckVarDecl(VD, DR, Val, dsk, AD, Live); + void CheckDeclRef(const DeclRefExpr *DR, const Expr *Val, DeadStoreKind dsk, + const LiveVariables::LivenessValues& Live) { + if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) + CheckVarDecl(VD, DR, Val, dsk, Live); } - bool isIncrement(VarDecl* VD, BinaryOperator* B) { + bool isIncrement(VarDecl *VD, const BinaryOperator* B) { if (B->isCompoundAssignmentOp()) return true; - Expr* RHS = B->getRHS()->IgnoreParenCasts(); - BinaryOperator* BRHS = dyn_cast<BinaryOperator>(RHS); + const Expr *RHS = B->getRHS()->IgnoreParenCasts(); + const BinaryOperator* BRHS = dyn_cast<BinaryOperator>(RHS); if (!BRHS) return false; - DeclRefExpr *DR; + const DeclRefExpr *DR; if ((DR = dyn_cast<DeclRefExpr>(BRHS->getLHS()->IgnoreParenCasts()))) if (DR->getDecl() == VD) @@ -182,9 +184,8 @@ public: return false; } - virtual void ObserveStmt(Stmt* S, const CFGBlock *block, - const LiveVariables::AnalysisDataTy& AD, - const LiveVariables::ValTy& Live) { + virtual void observeStmt(const Stmt *S, const CFGBlock *block, + const LiveVariables::LivenessValues &Live) { currentBlock = block; @@ -194,10 +195,10 @@ public: // Only cover dead stores from regular assignments. ++/-- dead stores // have never flagged a real bug. - if (BinaryOperator* B = dyn_cast<BinaryOperator>(S)) { + if (const BinaryOperator* B = dyn_cast<BinaryOperator>(S)) { if (!B->isAssignmentOp()) return; // Skip non-assignments. - if (DeclRefExpr* DR = dyn_cast<DeclRefExpr>(B->getLHS())) + if (DeclRefExpr *DR = dyn_cast<DeclRefExpr>(B->getLHS())) if (VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) { // Special case: check for assigning null to a pointer. // This is a common form of defensive programming. @@ -208,10 +209,10 @@ public: return; } - Expr* RHS = B->getRHS()->IgnoreParenCasts(); + Expr *RHS = B->getRHS()->IgnoreParenCasts(); // Special case: self-assignments. These are often used to shut up // "unused variable" compiler warnings. - if (DeclRefExpr* RhsDR = dyn_cast<DeclRefExpr>(RHS)) + if (DeclRefExpr *RhsDR = dyn_cast<DeclRefExpr>(RHS)) if (VD == dyn_cast<VarDecl>(RhsDR->getDecl())) return; @@ -220,29 +221,29 @@ public: ? Enclosing : (isIncrement(VD,B) ? DeadIncrement : Standard); - CheckVarDecl(VD, DR, B->getRHS(), dsk, AD, Live); + CheckVarDecl(VD, DR, B->getRHS(), dsk, Live); } } - else if (UnaryOperator* U = dyn_cast<UnaryOperator>(S)) { + else if (const UnaryOperator* U = dyn_cast<UnaryOperator>(S)) { if (!U->isIncrementOp() || U->isPrefix()) return; - Stmt *parent = Parents.getParentIgnoreParenCasts(U); + const Stmt *parent = Parents.getParentIgnoreParenCasts(U); if (!parent || !isa<ReturnStmt>(parent)) return; - Expr *Ex = U->getSubExpr()->IgnoreParenCasts(); + const Expr *Ex = U->getSubExpr()->IgnoreParenCasts(); - if (DeclRefExpr* DR = dyn_cast<DeclRefExpr>(Ex)) - CheckDeclRef(DR, U, DeadIncrement, AD, Live); + if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(Ex)) + CheckDeclRef(DR, U, DeadIncrement, Live); } - else if (DeclStmt* DS = dyn_cast<DeclStmt>(S)) + else if (const DeclStmt *DS = dyn_cast<DeclStmt>(S)) // Iterate through the decls. Warn if any initializers are complex // expressions that are not live (never used). - for (DeclStmt::decl_iterator DI=DS->decl_begin(), DE=DS->decl_end(); + for (DeclStmt::const_decl_iterator DI=DS->decl_begin(), DE=DS->decl_end(); DI != DE; ++DI) { - VarDecl* V = dyn_cast<VarDecl>(*DI); + VarDecl *V = dyn_cast<VarDecl>(*DI); if (!V) continue; @@ -253,7 +254,7 @@ public: if (V->getType()->getAs<ReferenceType>()) return; - if (Expr* E = V->getInit()) { + if (Expr *E = V->getInit()) { while (ExprWithCleanups *exprClean = dyn_cast<ExprWithCleanups>(E)) E = exprClean->getSubExpr(); @@ -265,7 +266,7 @@ public: // A dead initialization is a variable that is dead after it // is initialized. We don't flag warnings for those variables // marked 'unused'. - if (!Live(V, AD) && V->getAttr<UnusedAttr>() == 0) { + if (!Live.isLive(V) && V->getAttr<UnusedAttr>() == 0) { // Special case: check for initializations with constants. // // e.g. : int x = 0; @@ -296,7 +297,9 @@ public: return; } - Report(V, DeadInit, V->getLocation(), E->getSourceRange()); + PathDiagnosticLocation Loc = + PathDiagnosticLocation::create(V, BR.getSourceManager()); + Report(V, DeadInit, Loc, E->getSourceRange()); } } } @@ -318,15 +321,15 @@ public: CFG& getCFG() { return *cfg; } - llvm::SmallPtrSet<VarDecl*, 20> Escaped; + llvm::SmallPtrSet<const VarDecl*, 20> Escaped; void VisitUnaryOperator(UnaryOperator* U) { // Check for '&'. Any VarDecl whose value has its address-taken we // treat as escaped. - Expr* E = U->getSubExpr()->IgnoreParenCasts(); + Expr *E = U->getSubExpr()->IgnoreParenCasts(); if (U->getOpcode() == UO_AddrOf) - if (DeclRefExpr* DR = dyn_cast<DeclRefExpr>(E)) - if (VarDecl* VD = dyn_cast<VarDecl>(DR->getDecl())) { + if (DeclRefExpr *DR = dyn_cast<DeclRefExpr>(E)) + if (VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) { Escaped.insert(VD); return; } @@ -345,13 +348,14 @@ class DeadStoresChecker : public Checker<check::ASTCodeBody> { public: void checkASTCodeBody(const Decl *D, AnalysisManager& mgr, BugReporter &BR) const { - if (LiveVariables *L = mgr.getLiveVariables(D)) { + if (LiveVariables *L = mgr.getAnalysis<LiveVariables>(D)) { CFG &cfg = *mgr.getCFG(D); + AnalysisContext *AC = mgr.getAnalysisContext(D); ParentMap &pmap = mgr.getParentMap(D); FindEscaped FS(&cfg); FS.getCFG().VisitBlockStmts(FS); - DeadStoreObs A(cfg, BR.getContext(), BR, pmap, FS.Escaped); - L->runOnAllBlocks(cfg, &A); + DeadStoreObs A(cfg, BR.getContext(), BR, AC, pmap, FS.Escaped); + L->runOnAllBlocks(A); } } }; diff --git a/lib/StaticAnalyzer/Checkers/DebugCheckers.cpp b/lib/StaticAnalyzer/Checkers/DebugCheckers.cpp index 486b7f7feb1b..d9d569423d3c 100644 --- a/lib/StaticAnalyzer/Checkers/DebugCheckers.cpp +++ b/lib/StaticAnalyzer/Checkers/DebugCheckers.cpp @@ -28,7 +28,7 @@ class LiveVariablesDumper : public Checker<check::ASTCodeBody> { public: void checkASTCodeBody(const Decl *D, AnalysisManager& mgr, BugReporter &BR) const { - if (LiveVariables* L = mgr.getLiveVariables(D)) { + if (LiveVariables* L = mgr.getAnalysis<LiveVariables>(D)) { L->dumpBlockLiveness(mgr.getSourceManager()); } } diff --git a/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp b/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp index baaf8b3aff8a..eeda734a0766 100644 --- a/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp @@ -29,16 +29,17 @@ class DereferenceChecker mutable llvm::OwningPtr<BuiltinBug> BT_undef; public: - void checkLocation(SVal location, bool isLoad, CheckerContext &C) const; + void checkLocation(SVal location, bool isLoad, const Stmt* S, + CheckerContext &C) const; - static void AddDerefSource(llvm::raw_ostream &os, - llvm::SmallVectorImpl<SourceRange> &Ranges, + static void AddDerefSource(raw_ostream &os, + SmallVectorImpl<SourceRange> &Ranges, const Expr *Ex, bool loadedFrom = false); }; } // end anonymous namespace -void DereferenceChecker::AddDerefSource(llvm::raw_ostream &os, - llvm::SmallVectorImpl<SourceRange> &Ranges, +void DereferenceChecker::AddDerefSource(raw_ostream &os, + SmallVectorImpl<SourceRange> &Ranges, const Expr *Ex, bool loadedFrom) { Ex = Ex->IgnoreParenLValueCasts(); @@ -65,7 +66,7 @@ void DereferenceChecker::AddDerefSource(llvm::raw_ostream &os, } } -void DereferenceChecker::checkLocation(SVal l, bool isLoad, +void DereferenceChecker::checkLocation(SVal l, bool isLoad, const Stmt* S, CheckerContext &C) const { // Check for dereference of an undefined value. if (l.isUndef()) { @@ -73,10 +74,10 @@ void DereferenceChecker::checkLocation(SVal l, bool isLoad, if (!BT_undef) BT_undef.reset(new BuiltinBug("Dereference of undefined pointer value")); - EnhancedBugReport *report = - new EnhancedBugReport(*BT_undef, BT_undef->getDescription(), N); - report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, - bugreporter::GetDerefExpr(N)); + BugReport *report = + new BugReport(*BT_undef, BT_undef->getDescription(), N); + report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, + bugreporter::GetDerefExpr(N))); C.EmitReport(report); } return; @@ -88,9 +89,8 @@ void DereferenceChecker::checkLocation(SVal l, bool isLoad, if (!isa<Loc>(location)) return; - const Stmt *S = C.getStmt(); - const GRState *state = C.getState(); - const GRState *notNullState, *nullState; + const ProgramState *state = C.getState(); + const ProgramState *notNullState, *nullState; llvm::tie(notNullState, nullState) = state->assume(location); // The explicit NULL case. @@ -107,7 +107,7 @@ void DereferenceChecker::checkLocation(SVal l, bool isLoad, BT_null.reset(new BuiltinBug("Dereference of null pointer")); llvm::SmallString<100> buf; - llvm::SmallVector<SourceRange, 2> Ranges; + SmallVector<SourceRange, 2> Ranges; // Walk through lvalue casts to get the original expression // that syntactically caused the load. @@ -157,15 +157,15 @@ void DereferenceChecker::checkLocation(SVal l, bool isLoad, break; } - EnhancedBugReport *report = - new EnhancedBugReport(*BT_null, + BugReport *report = + new BugReport(*BT_null, buf.empty() ? BT_null->getDescription():buf.str(), N); - report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, - bugreporter::GetDerefExpr(N)); + report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, + bugreporter::GetDerefExpr(N))); - for (llvm::SmallVectorImpl<SourceRange>::iterator + for (SmallVectorImpl<SourceRange>::iterator I = Ranges.begin(), E = Ranges.end(); I!=E; ++I) report->addRange(*I); diff --git a/lib/StaticAnalyzer/Checkers/DivZeroChecker.cpp b/lib/StaticAnalyzer/Checkers/DivZeroChecker.cpp index 07fb5aa998be..75b7cc47aa79 100644 --- a/lib/StaticAnalyzer/Checkers/DivZeroChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/DivZeroChecker.cpp @@ -52,7 +52,7 @@ void DivZeroChecker::checkPreStmt(const BinaryOperator *B, // Check for divide by zero. ConstraintManager &CM = C.getConstraintManager(); - const GRState *stateNotZero, *stateZero; + const ProgramState *stateNotZero, *stateZero; llvm::tie(stateNotZero, stateZero) = CM.assumeDual(C.getState(), *DV); if (stateZero && !stateNotZero) { @@ -60,11 +60,11 @@ void DivZeroChecker::checkPreStmt(const BinaryOperator *B, if (!BT) BT.reset(new BuiltinBug("Division by zero")); - EnhancedBugReport *R = - new EnhancedBugReport(*BT, BT->getDescription(), N); + BugReport *R = + new BugReport(*BT, BT->getDescription(), N); - R->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, - bugreporter::GetDenomExpr(N)); + R->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, + bugreporter::GetDenomExpr(N))); C.EmitReport(R); } diff --git a/lib/StaticAnalyzer/Checkers/FixedAddressChecker.cpp b/lib/StaticAnalyzer/Checkers/FixedAddressChecker.cpp index d699deecd768..531d87e42631 100644 --- a/lib/StaticAnalyzer/Checkers/FixedAddressChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/FixedAddressChecker.cpp @@ -44,7 +44,7 @@ void FixedAddressChecker::checkPreStmt(const BinaryOperator *B, if (!T->isPointerType()) return; - const GRState *state = C.getState(); + const ProgramState *state = C.getState(); SVal RV = state->getSVal(B->getRHS()); @@ -57,7 +57,7 @@ void FixedAddressChecker::checkPreStmt(const BinaryOperator *B, "Using a fixed address is not portable because that " "address will probably not be valid in all " "environments or platforms.")); - RangedBugReport *R = new RangedBugReport(*BT, BT->getDescription(), N); + BugReport *R = new BugReport(*BT, BT->getDescription(), N); R->addRange(B->getRHS()->getSourceRange()); C.EmitReport(R); } diff --git a/lib/StaticAnalyzer/Checkers/IdempotentOperationChecker.cpp b/lib/StaticAnalyzer/Checkers/IdempotentOperationChecker.cpp index b0c07fc7d264..5c257e595be3 100644 --- a/lib/StaticAnalyzer/Checkers/IdempotentOperationChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/IdempotentOperationChecker.cpp @@ -96,10 +96,9 @@ private: // Hash table and related data structures struct BinaryOperatorData { - BinaryOperatorData() : assumption(Possible), analysisContext(0) {} + BinaryOperatorData() : assumption(Possible) {} Assumption assumption; - AnalysisContext *analysisContext; ExplodedNodeSet explodedNodes; // Set of ExplodedNodes that refer to a // BinaryOperator }; @@ -118,7 +117,6 @@ void IdempotentOperationChecker::checkPreStmt(const BinaryOperator *B, BinaryOperatorData &Data = hash[B]; Assumption &A = Data.assumption; AnalysisContext *AC = C.getCurrentAnalysisContext(); - Data.analysisContext = AC; // If we already have visited this node on a path that does not contain an // idempotent operation, return immediately. @@ -143,7 +141,7 @@ void IdempotentOperationChecker::checkPreStmt(const BinaryOperator *B, || containsNonLocalVarDecl(RHS); } - const GRState *state = C.getState(); + const ProgramState *state = C.getState(); SVal LHSVal = state->getSVal(LHS); SVal RHSVal = state->getSVal(RHS); @@ -351,9 +349,14 @@ void IdempotentOperationChecker::checkEndAnalysis(ExplodedGraph &G, // Unpack the hash contents const BinaryOperatorData &Data = i->second; const Assumption &A = Data.assumption; - AnalysisContext *AC = Data.analysisContext; const ExplodedNodeSet &ES = Data.explodedNodes; + // If there are no nodes accosted with the expression, nothing to report. + // FIXME: This is possible because the checker does part of processing in + // checkPreStmt and part in checkPostStmt. + if (ES.begin() == ES.end()) + continue; + const BinaryOperator *B = i->first; if (A == Impossible) @@ -363,6 +366,8 @@ void IdempotentOperationChecker::checkEndAnalysis(ExplodedGraph &G, // warning if (Eng.hasWorkRemaining()) { // If we can trace back + AnalysisContext *AC = (*ES.begin())->getLocationContext() + ->getAnalysisContext(); if (!pathWasCompletelyAnalyzed(AC, AC->getCFGStmtMap()->getBlock(B), Eng.getCoreEngine())) @@ -407,18 +412,18 @@ void IdempotentOperationChecker::checkEndAnalysis(ExplodedGraph &G, // Add a report for each ExplodedNode for (ExplodedNodeSet::iterator I = ES.begin(), E = ES.end(); I != E; ++I) { - EnhancedBugReport *report = new EnhancedBugReport(*BT, os.str(), *I); + BugReport *report = new BugReport(*BT, os.str(), *I); // Add source ranges and visitor hooks if (LHSRelevant) { const Expr *LHS = i->first->getLHS(); report->addRange(LHS->getSourceRange()); - report->addVisitorCreator(bugreporter::registerVarDeclsLastStore, LHS); + FindLastStoreBRVisitor::registerStatementVarDecls(*report, LHS); } if (RHSRelevant) { const Expr *RHS = i->first->getRHS(); report->addRange(i->first->getRHS()->getSourceRange()); - report->addVisitorCreator(bugreporter::registerVarDeclsLastStore, RHS); + FindLastStoreBRVisitor::registerStatementVarDecls(*report, RHS); } BR.EmitReport(report); diff --git a/lib/StaticAnalyzer/Checkers/IteratorsChecker.cpp b/lib/StaticAnalyzer/Checkers/IteratorsChecker.cpp index de6da4f8c377..fbc57d336f90 100644 --- a/lib/StaticAnalyzer/Checkers/IteratorsChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/IteratorsChecker.cpp @@ -20,7 +20,7 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/GRStateTrait.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/Decl.h" #include "clang/AST/Type.h" @@ -117,16 +117,28 @@ public: CheckerContext &C) const; private: - const GRState *handleAssign(const GRState *state, const Expr *lexp, - const Expr *rexp, const LocationContext *LC) const; - const GRState *handleAssign(const GRState *state, const MemRegion *MR, - const Expr *rexp, const LocationContext *LC) const; - const GRState *invalidateIterators(const GRState *state, const MemRegion *MR, - const MemberExpr *ME) const; + const ProgramState *handleAssign(const ProgramState *state, + const Expr *lexp, + const Expr *rexp, + const LocationContext *LC) const; + + const ProgramState *handleAssign(const ProgramState *state, + const MemRegion *MR, + const Expr *rexp, + const LocationContext *LC) const; + + const ProgramState *invalidateIterators(const ProgramState *state, + const MemRegion *MR, + const MemberExpr *ME) const; + void checkExpr(CheckerContext &C, const Expr *E) const; + void checkArgs(CheckerContext &C, const CallExpr *CE) const; - const MemRegion *getRegion(const GRState *state, const Expr *E, - const LocationContext *LC) const; + + const MemRegion *getRegion(const ProgramState *state, + const Expr *E, + const LocationContext *LC) const; + const DeclRefExpr *getDeclRefExpr(const Expr *E) const; }; @@ -139,8 +151,8 @@ public: namespace clang { namespace ento { template <> - struct GRStateTrait<IteratorState> - : public GRStatePartialTrait<IteratorState::EntryMap> { + struct ProgramStateTrait<IteratorState> + : public ProgramStatePartialTrait<IteratorState::EntryMap> { static void *GDMIndex() { return IteratorsChecker::getTag(); } }; } @@ -162,7 +174,7 @@ static RefKind getTemplateKind(const NamedDecl *td) { || nameSpace->getName() != "std") return NoKind; - llvm::StringRef name = td->getName(); + StringRef name = td->getName(); return llvm::StringSwitch<RefKind>(name) .Cases("vector", "deque", VectorKind) .Default(NoKind); @@ -215,7 +227,7 @@ static RefKind getTemplateKind(QualType T) { // Iterate through our map and invalidate any iterators that were // initialized fromt the specified instance MemRegion. -const GRState *IteratorsChecker::invalidateIterators(const GRState *state, +const ProgramState *IteratorsChecker::invalidateIterators(const ProgramState *state, const MemRegion *MR, const MemberExpr *ME) const { IteratorState::EntryMap Map = state->get<IteratorState>(); if (Map.isEmpty()) @@ -234,7 +246,7 @@ const GRState *IteratorsChecker::invalidateIterators(const GRState *state, } // Handle assigning to an iterator where we don't have the LValue MemRegion. -const GRState *IteratorsChecker::handleAssign(const GRState *state, +const ProgramState *IteratorsChecker::handleAssign(const ProgramState *state, const Expr *lexp, const Expr *rexp, const LocationContext *LC) const { // Skip the cast if present. if (const MaterializeTemporaryExpr *M @@ -259,7 +271,7 @@ const GRState *IteratorsChecker::handleAssign(const GRState *state, } // handle assigning to an iterator -const GRState *IteratorsChecker::handleAssign(const GRState *state, +const ProgramState *IteratorsChecker::handleAssign(const ProgramState *state, const MemRegion *MR, const Expr *rexp, const LocationContext *LC) const { // Assume unknown until we find something definite. state = state->set<IteratorState>(MR, RefState::getUnknown()); @@ -287,7 +299,7 @@ const GRState *IteratorsChecker::handleAssign(const GRState *state, return state; // Finally, see if it is one of the calls that will create // a valid iterator and mark it if so, else mark as Unknown. - llvm::StringRef mName = ME->getMemberDecl()->getName(); + StringRef mName = ME->getMemberDecl()->getName(); if (llvm::StringSwitch<bool>(mName) .Cases("begin", "insert", "erase", true).Default(false)) { @@ -364,7 +376,7 @@ const DeclRefExpr *IteratorsChecker::getDeclRefExpr(const Expr *E) const { } // Get the MemRegion associated with the expresssion. -const MemRegion *IteratorsChecker::getRegion(const GRState *state, +const MemRegion *IteratorsChecker::getRegion(const ProgramState *state, const Expr *E, const LocationContext *LC) const { const DeclRefExpr *DRE = getDeclRefExpr(E); if (!DRE) @@ -382,7 +394,7 @@ const MemRegion *IteratorsChecker::getRegion(const GRState *state, // use those nodes. We also cannot create multiple nodes at one ProgramPoint // with the same tag. void IteratorsChecker::checkExpr(CheckerContext &C, const Expr *E) const { - const GRState *state = C.getState(); + const ProgramState *state = C.getState(); const MemRegion *MR = getRegion(state, E, C.getPredecessor()->getLocationContext()); if (!MR) @@ -410,7 +422,7 @@ void IteratorsChecker::checkExpr(CheckerContext &C, const Expr *E) const { "container to its container"; } - EnhancedBugReport *R = new EnhancedBugReport(*BT_Invalid, msg, N); + BugReport *R = new BugReport(*BT_Invalid, msg, N); R->addRange(getDeclRefExpr(E)->getSourceRange()); C.EmitReport(R); } @@ -422,7 +434,7 @@ void IteratorsChecker::checkExpr(CheckerContext &C, const Expr *E) const { const_cast<IteratorsChecker*>(this)->BT_Undefined = new BuiltinBug("Use of iterator that is not defined"); - EnhancedBugReport *R = new EnhancedBugReport(*BT_Undefined, + BugReport *R = new BugReport(*BT_Undefined, BT_Undefined->getDescription(), N); R->addRange(getDeclRefExpr(E)->getSourceRange()); C.EmitReport(R); @@ -455,7 +467,7 @@ void IteratorsChecker::checkPreStmt(const CXXOperatorCallExpr *OCE, CheckerContext &C) const { const LocationContext *LC = C.getPredecessor()->getLocationContext(); - const GRState *state = C.getState(); + const ProgramState *state = C.getState(); OverloadedOperatorKind Kind = OCE->getOperator(); if (Kind == OO_Equal) { checkExpr(C, OCE->getArg(1)); @@ -491,8 +503,8 @@ void IteratorsChecker::checkPreStmt(const CXXOperatorCallExpr *OCE, new BuiltinBug( "Cannot compare iterators from different containers"); - EnhancedBugReport *R = new EnhancedBugReport(*BT_Incompatible, - BT_Incompatible->getDescription(), N); + BugReport *R = new BugReport(*BT_Incompatible, + BT_Incompatible->getDescription(), N); R->addRange(OCE->getSourceRange()); C.EmitReport(R); } @@ -505,14 +517,14 @@ void IteratorsChecker::checkPreStmt(const CXXOperatorCallExpr *OCE, // uninitialized ones as Undefined. void IteratorsChecker::checkPreStmt(const DeclStmt *DS, CheckerContext &C) const { - const Decl* D = *DS->decl_begin(); - const VarDecl* VD = dyn_cast<VarDecl>(D); + const Decl *D = *DS->decl_begin(); + const VarDecl *VD = dyn_cast<VarDecl>(D); // Only care about iterators. if (getTemplateKind(VD->getType()) != VectorIteratorKind) return; // Get the MemRegion associated with the iterator and mark it as Undefined. - const GRState *state = C.getState(); + const ProgramState *state = C.getState(); Loc VarLoc = state->getLValue(VD, C.getPredecessor()->getLocationContext()); const MemRegion *MR = VarLoc.getAsRegion(); if (!MR) @@ -520,7 +532,7 @@ void IteratorsChecker::checkPreStmt(const DeclStmt *DS, state = state->set<IteratorState>(MR, RefState::getUndefined()); // if there is an initializer, handle marking Valid if a proper initializer - const Expr* InitEx = VD->getInit(); + const Expr *InitEx = VD->getInit(); if (InitEx) { // FIXME: This is too syntactic. Since 'InitEx' will be analyzed first // it should resolve to an SVal that we can check for validity @@ -544,8 +556,8 @@ void IteratorsChecker::checkPreStmt(const DeclStmt *DS, namespace { struct CalledReserved {}; } namespace clang { namespace ento { -template<> struct GRStateTrait<CalledReserved> - : public GRStatePartialTrait<llvm::ImmutableSet<const MemRegion*> > { +template<> struct ProgramStateTrait<CalledReserved> + : public ProgramStatePartialTrait<llvm::ImmutableSet<const MemRegion*> > { static void *GDMIndex() { static int index = 0; return &index; } }; }} @@ -571,8 +583,8 @@ void IteratorsChecker::checkPreStmt(const CXXMemberCallExpr *MCE, return; // If we are calling a function that invalidates iterators, mark them // appropriately by finding matching instances. - const GRState *state = C.getState(); - llvm::StringRef mName = ME->getMemberDecl()->getName(); + const ProgramState *state = C.getState(); + StringRef mName = ME->getMemberDecl()->getName(); if (llvm::StringSwitch<bool>(mName) .Cases("insert", "reserve", "push_back", true) .Cases("erase", "pop_back", "clear", "resize", true) diff --git a/lib/StaticAnalyzer/Checkers/LLVMConventionsChecker.cpp b/lib/StaticAnalyzer/Checkers/LLVMConventionsChecker.cpp index 3d1b5e2d1051..e398fcb32257 100644 --- a/lib/StaticAnalyzer/Checkers/LLVMConventionsChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/LLVMConventionsChecker.cpp @@ -32,13 +32,13 @@ static bool IsLLVMStringRef(QualType T) { if (!RT) return false; - return llvm::StringRef(QualType(RT, 0).getAsString()) == - "class llvm::StringRef"; + return StringRef(QualType(RT, 0).getAsString()) == + "class StringRef"; } /// Check whether the declaration is semantically inside the top-level /// namespace named by ns. -static bool InNamespace(const Decl *D, llvm::StringRef NS) { +static bool InNamespace(const Decl *D, StringRef NS) { const NamespaceDecl *ND = dyn_cast<NamespaceDecl>(D->getDeclContext()); if (!ND) return false; @@ -109,7 +109,7 @@ static bool IsSmallVector(QualType T) { } //===----------------------------------------------------------------------===// -// CHECK: a llvm::StringRef should not be bound to a temporary std::string whose +// CHECK: a StringRef should not be bound to a temporary std::string whose // lifetime is shorter than the StringRef's. //===----------------------------------------------------------------------===// @@ -150,7 +150,7 @@ void StringRefCheckerVisitor::VisitVarDecl(VarDecl *VD) { return; // Pattern match for: - // llvm::StringRef x = call() (where call returns std::string) + // StringRef x = call() (where call returns std::string) if (!IsLLVMStringRef(VD->getType())) return; ExprWithCleanups *Ex1 = dyn_cast<ExprWithCleanups>(Init); @@ -175,9 +175,10 @@ void StringRefCheckerVisitor::VisitVarDecl(VarDecl *VD) { // Okay, badness! Report an error. const char *desc = "StringRef should not be bound to temporary " "std::string that it outlives"; - + PathDiagnosticLocation VDLoc = + PathDiagnosticLocation::createBegin(VD, BR.getSourceManager()); BR.EmitBasicReport(desc, "LLVM Conventions", desc, - VD->getLocStart(), Init->getSourceRange()); + VDLoc, Init->getSourceRange()); } //===----------------------------------------------------------------------===// @@ -210,7 +211,7 @@ static bool IsPartOfAST(const CXXRecordDecl *R) { namespace { class ASTFieldVisitor { - llvm::SmallVector<FieldDecl*, 10> FieldChain; + SmallVector<FieldDecl*, 10> FieldChain; const CXXRecordDecl *Root; BugReporter &BR; public: @@ -260,7 +261,7 @@ void ASTFieldVisitor::ReportError(QualType T) { if (FieldChain.size() > 1) { os << " via the following chain: "; bool isFirst = true; - for (llvm::SmallVectorImpl<FieldDecl*>::iterator I=FieldChain.begin(), + for (SmallVectorImpl<FieldDecl*>::iterator I=FieldChain.begin(), E=FieldChain.end(); I!=E; ++I) { if (!isFirst) os << '.'; @@ -279,8 +280,10 @@ void ASTFieldVisitor::ReportError(QualType T) { // just report warnings when we see an out-of-line method definition for a // class, as that heuristic doesn't always work (the complete definition of // the class may be in the header file, for example). + PathDiagnosticLocation L = PathDiagnosticLocation::createBegin( + FieldChain.front(), BR.getSourceManager()); BR.EmitBasicReport("AST node allocates heap memory", "LLVM Conventions", - os.str(), FieldChain.front()->getLocStart()); + os.str(), L); } //===----------------------------------------------------------------------===// @@ -294,7 +297,7 @@ class LLVMConventionsChecker : public Checker< public: void checkASTDecl(const CXXRecordDecl *R, AnalysisManager& mgr, BugReporter &BR) const { - if (R->isDefinition()) + if (R->isCompleteDefinition()) CheckASTMemory(R, BR); } diff --git a/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp b/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp new file mode 100644 index 000000000000..2607db80ba8e --- /dev/null +++ b/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp @@ -0,0 +1,632 @@ +//==--- MacOSKeychainAPIChecker.cpp ------------------------------*- C++ -*-==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// This checker flags misuses of KeyChainAPI. In particular, the password data +// allocated/returned by SecKeychainItemCopyContent, +// SecKeychainFindGenericPassword, SecKeychainFindInternetPassword functions has +// to be freed using a call to SecKeychainItemFreeContent. +//===----------------------------------------------------------------------===// + +#include "ClangSACheckers.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" + +using namespace clang; +using namespace ento; + +namespace { +class MacOSKeychainAPIChecker : public Checker<check::PreStmt<CallExpr>, + check::PreStmt<ReturnStmt>, + check::PostStmt<CallExpr>, + check::EndPath, + check::DeadSymbols> { + mutable llvm::OwningPtr<BugType> BT; + +public: + /// AllocationState is a part of the checker specific state together with the + /// MemRegion corresponding to the allocated data. + struct AllocationState { + /// The index of the allocator function. + unsigned int AllocatorIdx; + SymbolRef Region; + + AllocationState(const Expr *E, unsigned int Idx, SymbolRef R) : + AllocatorIdx(Idx), + Region(R) {} + + bool operator==(const AllocationState &X) const { + return (AllocatorIdx == X.AllocatorIdx && + Region == X.Region); + } + + void Profile(llvm::FoldingSetNodeID &ID) const { + ID.AddInteger(AllocatorIdx); + ID.AddPointer(Region); + } + }; + + void checkPreStmt(const CallExpr *S, CheckerContext &C) const; + void checkPreStmt(const ReturnStmt *S, CheckerContext &C) const; + void checkPostStmt(const CallExpr *S, CheckerContext &C) const; + void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const; + void checkEndPath(EndOfFunctionNodeBuilder &B, ExprEngine &Eng) const; + +private: + typedef std::pair<SymbolRef, const AllocationState*> AllocationPair; + typedef llvm::SmallVector<AllocationPair, 2> AllocationPairVec; + + enum APIKind { + /// Denotes functions tracked by this checker. + ValidAPI = 0, + /// The functions commonly/mistakenly used in place of the given API. + ErrorAPI = 1, + /// The functions which may allocate the data. These are tracked to reduce + /// the false alarm rate. + PossibleAPI = 2 + }; + /// Stores the information about the allocator and deallocator functions - + /// these are the functions the checker is tracking. + struct ADFunctionInfo { + const char* Name; + unsigned int Param; + unsigned int DeallocatorIdx; + APIKind Kind; + }; + static const unsigned InvalidIdx = 100000; + static const unsigned FunctionsToTrackSize = 8; + static const ADFunctionInfo FunctionsToTrack[FunctionsToTrackSize]; + /// The value, which represents no error return value for allocator functions. + static const unsigned NoErr = 0; + + /// Given the function name, returns the index of the allocator/deallocator + /// function. + static unsigned getTrackedFunctionIndex(StringRef Name, bool IsAllocator); + + inline void initBugType() const { + if (!BT) + BT.reset(new BugType("Improper use of SecKeychain API", "Mac OS API")); + } + + void generateDeallocatorMismatchReport(const AllocationPair &AP, + const Expr *ArgExpr, + CheckerContext &C) const; + + BugReport *generateAllocatedDataNotReleasedReport(const AllocationPair &AP, + ExplodedNode *N) const; + + /// Check if RetSym evaluates to an error value in the current state. + bool definitelyReturnedError(SymbolRef RetSym, + const ProgramState *State, + SValBuilder &Builder, + bool noError = false) const; + + /// Check if RetSym evaluates to a NoErr value in the current state. + bool definitelyDidnotReturnError(SymbolRef RetSym, + const ProgramState *State, + SValBuilder &Builder) const { + return definitelyReturnedError(RetSym, State, Builder, true); + } + + /// The bug visitor which allows us to print extra diagnostics along the + /// BugReport path. For example, showing the allocation site of the leaked + /// region. + class SecKeychainBugVisitor : public BugReporterVisitor { + protected: + // The allocated region symbol tracked by the main analysis. + SymbolRef Sym; + + public: + SecKeychainBugVisitor(SymbolRef S) : Sym(S) {} + virtual ~SecKeychainBugVisitor() {} + + void Profile(llvm::FoldingSetNodeID &ID) const { + static int X = 0; + ID.AddPointer(&X); + ID.AddPointer(Sym); + } + + PathDiagnosticPiece *VisitNode(const ExplodedNode *N, + const ExplodedNode *PrevN, + BugReporterContext &BRC, + BugReport &BR); + }; +}; +} + +/// ProgramState traits to store the currently allocated (and not yet freed) +/// symbols. This is a map from the allocated content symbol to the +/// corresponding AllocationState. +typedef llvm::ImmutableMap<SymbolRef, + MacOSKeychainAPIChecker::AllocationState> AllocatedSetTy; + +namespace { struct AllocatedData {}; } +namespace clang { namespace ento { +template<> struct ProgramStateTrait<AllocatedData> + : public ProgramStatePartialTrait<AllocatedSetTy > { + static void *GDMIndex() { static int index = 0; return &index; } +}; +}} + +static bool isEnclosingFunctionParam(const Expr *E) { + E = E->IgnoreParenCasts(); + if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E)) { + const ValueDecl *VD = DRE->getDecl(); + if (isa<ImplicitParamDecl>(VD) || isa<ParmVarDecl>(VD)) + return true; + } + return false; +} + +const MacOSKeychainAPIChecker::ADFunctionInfo + MacOSKeychainAPIChecker::FunctionsToTrack[FunctionsToTrackSize] = { + {"SecKeychainItemCopyContent", 4, 3, ValidAPI}, // 0 + {"SecKeychainFindGenericPassword", 6, 3, ValidAPI}, // 1 + {"SecKeychainFindInternetPassword", 13, 3, ValidAPI}, // 2 + {"SecKeychainItemFreeContent", 1, InvalidIdx, ValidAPI}, // 3 + {"SecKeychainItemCopyAttributesAndData", 5, 5, ValidAPI}, // 4 + {"SecKeychainItemFreeAttributesAndData", 1, InvalidIdx, ValidAPI}, // 5 + {"free", 0, InvalidIdx, ErrorAPI}, // 6 + {"CFStringCreateWithBytesNoCopy", 1, InvalidIdx, PossibleAPI}, // 7 +}; + +unsigned MacOSKeychainAPIChecker::getTrackedFunctionIndex(StringRef Name, + bool IsAllocator) { + for (unsigned I = 0; I < FunctionsToTrackSize; ++I) { + ADFunctionInfo FI = FunctionsToTrack[I]; + if (FI.Name != Name) + continue; + // Make sure the function is of the right type (allocator vs deallocator). + if (IsAllocator && (FI.DeallocatorIdx == InvalidIdx)) + return InvalidIdx; + if (!IsAllocator && (FI.DeallocatorIdx != InvalidIdx)) + return InvalidIdx; + + return I; + } + // The function is not tracked. + return InvalidIdx; +} + +static SymbolRef getSymbolForRegion(CheckerContext &C, + const MemRegion *R) { + // Implicit casts (ex: void* -> char*) can turn Symbolic region into element + // region, if that is the case, get the underlining region. + R = R->StripCasts(); + if (!isa<SymbolicRegion>(R)) { + return 0; + } + return cast<SymbolicRegion>(R)->getSymbol(); +} + +static bool isBadDeallocationArgument(const MemRegion *Arg) { + if (isa<AllocaRegion>(Arg) || + isa<BlockDataRegion>(Arg) || + isa<TypedRegion>(Arg)) { + return true; + } + return false; +} +/// Given the address expression, retrieve the value it's pointing to. Assume +/// that value is itself an address, and return the corresponding symbol. +static SymbolRef getAsPointeeSymbol(const Expr *Expr, + CheckerContext &C) { + const ProgramState *State = C.getState(); + SVal ArgV = State->getSVal(Expr); + + if (const loc::MemRegionVal *X = dyn_cast<loc::MemRegionVal>(&ArgV)) { + StoreManager& SM = C.getStoreManager(); + const MemRegion *V = SM.Retrieve(State->getStore(), *X).getAsRegion(); + if (V) + return getSymbolForRegion(C, V); + } + return 0; +} + +// When checking for error code, we need to consider the following cases: +// 1) noErr / [0] +// 2) someErr / [1, inf] +// 3) unknown +// If noError, returns true iff (1). +// If !noError, returns true iff (2). +bool MacOSKeychainAPIChecker::definitelyReturnedError(SymbolRef RetSym, + const ProgramState *State, + SValBuilder &Builder, + bool noError) const { + DefinedOrUnknownSVal NoErrVal = Builder.makeIntVal(NoErr, + Builder.getSymbolManager().getType(RetSym)); + DefinedOrUnknownSVal NoErr = Builder.evalEQ(State, NoErrVal, + nonloc::SymbolVal(RetSym)); + const ProgramState *ErrState = State->assume(NoErr, noError); + if (ErrState == State) { + return true; + } + + return false; +} + +// Report deallocator mismatch. Remove the region from tracking - reporting a +// missing free error after this one is redundant. +void MacOSKeychainAPIChecker:: + generateDeallocatorMismatchReport(const AllocationPair &AP, + const Expr *ArgExpr, + CheckerContext &C) const { + const ProgramState *State = C.getState(); + State = State->remove<AllocatedData>(AP.first); + ExplodedNode *N = C.generateNode(State); + + if (!N) + return; + initBugType(); + llvm::SmallString<80> sbuf; + llvm::raw_svector_ostream os(sbuf); + unsigned int PDeallocIdx = + FunctionsToTrack[AP.second->AllocatorIdx].DeallocatorIdx; + + os << "Deallocator doesn't match the allocator: '" + << FunctionsToTrack[PDeallocIdx].Name << "' should be used."; + BugReport *Report = new BugReport(*BT, os.str(), N); + Report->addVisitor(new SecKeychainBugVisitor(AP.first)); + Report->addRange(ArgExpr->getSourceRange()); + C.EmitReport(Report); +} + +void MacOSKeychainAPIChecker::checkPreStmt(const CallExpr *CE, + CheckerContext &C) const { + const ProgramState *State = C.getState(); + const Expr *Callee = CE->getCallee(); + SVal L = State->getSVal(Callee); + unsigned idx = InvalidIdx; + + const FunctionDecl *funDecl = L.getAsFunctionDecl(); + if (!funDecl) + return; + IdentifierInfo *funI = funDecl->getIdentifier(); + if (!funI) + return; + StringRef funName = funI->getName(); + + // If it is a call to an allocator function, it could be a double allocation. + idx = getTrackedFunctionIndex(funName, true); + if (idx != InvalidIdx) { + const Expr *ArgExpr = CE->getArg(FunctionsToTrack[idx].Param); + if (SymbolRef V = getAsPointeeSymbol(ArgExpr, C)) + if (const AllocationState *AS = State->get<AllocatedData>(V)) { + if (!definitelyReturnedError(AS->Region, State, C.getSValBuilder())) { + // Remove the value from the state. The new symbol will be added for + // tracking when the second allocator is processed in checkPostStmt(). + State = State->remove<AllocatedData>(V); + ExplodedNode *N = C.generateNode(State); + if (!N) + return; + initBugType(); + llvm::SmallString<128> sbuf; + llvm::raw_svector_ostream os(sbuf); + unsigned int DIdx = FunctionsToTrack[AS->AllocatorIdx].DeallocatorIdx; + os << "Allocated data should be released before another call to " + << "the allocator: missing a call to '" + << FunctionsToTrack[DIdx].Name + << "'."; + BugReport *Report = new BugReport(*BT, os.str(), N); + Report->addVisitor(new SecKeychainBugVisitor(V)); + Report->addRange(ArgExpr->getSourceRange()); + C.EmitReport(Report); + } + } + return; + } + + // Is it a call to one of deallocator functions? + idx = getTrackedFunctionIndex(funName, false); + if (idx == InvalidIdx) + return; + + // Check the argument to the deallocator. + const Expr *ArgExpr = CE->getArg(FunctionsToTrack[idx].Param); + SVal ArgSVal = State->getSVal(ArgExpr); + + // Undef is reported by another checker. + if (ArgSVal.isUndef()) + return; + + const MemRegion *Arg = ArgSVal.getAsRegion(); + if (!Arg) + return; + + SymbolRef ArgSM = getSymbolForRegion(C, Arg); + bool RegionArgIsBad = ArgSM ? false : isBadDeallocationArgument(Arg); + // If the argument is coming from the heap, globals, or unknown, do not + // report it. + if (!ArgSM && !RegionArgIsBad) + return; + + // Is the argument to the call being tracked? + const AllocationState *AS = State->get<AllocatedData>(ArgSM); + if (!AS && FunctionsToTrack[idx].Kind != ValidAPI) { + return; + } + // If trying to free data which has not been allocated yet, report as a bug. + // TODO: We might want a more precise diagnostic for double free + // (that would involve tracking all the freed symbols in the checker state). + if (!AS || RegionArgIsBad) { + // It is possible that this is a false positive - the argument might + // have entered as an enclosing function parameter. + if (isEnclosingFunctionParam(ArgExpr)) + return; + + ExplodedNode *N = C.generateNode(State); + if (!N) + return; + initBugType(); + BugReport *Report = new BugReport(*BT, + "Trying to free data which has not been allocated.", N); + Report->addRange(ArgExpr->getSourceRange()); + C.EmitReport(Report); + return; + } + + // Process functions which might deallocate. + if (FunctionsToTrack[idx].Kind == PossibleAPI) { + + if (funName == "CFStringCreateWithBytesNoCopy") { + const Expr *DeallocatorExpr = CE->getArg(5)->IgnoreParenCasts(); + // NULL ~ default deallocator, so warn. + if (DeallocatorExpr->isNullPointerConstant(C.getASTContext(), + Expr::NPC_ValueDependentIsNotNull)) { + const AllocationPair AP = std::make_pair(ArgSM, AS); + generateDeallocatorMismatchReport(AP, ArgExpr, C); + return; + } + // One of the default allocators, so warn. + if (const DeclRefExpr *DE = dyn_cast<DeclRefExpr>(DeallocatorExpr)) { + StringRef DeallocatorName = DE->getFoundDecl()->getName(); + if (DeallocatorName == "kCFAllocatorDefault" || + DeallocatorName == "kCFAllocatorSystemDefault" || + DeallocatorName == "kCFAllocatorMalloc") { + const AllocationPair AP = std::make_pair(ArgSM, AS); + generateDeallocatorMismatchReport(AP, ArgExpr, C); + return; + } + // If kCFAllocatorNull, which does not deallocate, we still have to + // find the deallocator. Otherwise, assume that the user had written a + // custom deallocator which does the right thing. + if (DE->getFoundDecl()->getName() != "kCFAllocatorNull") { + State = State->remove<AllocatedData>(ArgSM); + C.addTransition(State); + return; + } + } + } + return; + } + + // The call is deallocating a value we previously allocated, so remove it + // from the next state. + State = State->remove<AllocatedData>(ArgSM); + + // Check if the proper deallocator is used. + unsigned int PDeallocIdx = FunctionsToTrack[AS->AllocatorIdx].DeallocatorIdx; + if (PDeallocIdx != idx || (FunctionsToTrack[idx].Kind == ErrorAPI)) { + const AllocationPair AP = std::make_pair(ArgSM, AS); + generateDeallocatorMismatchReport(AP, ArgExpr, C); + return; + } + + // If the return status is undefined or is error, report a bad call to free. + if (!definitelyDidnotReturnError(AS->Region, State, C.getSValBuilder())) { + ExplodedNode *N = C.generateNode(State); + if (!N) + return; + initBugType(); + BugReport *Report = new BugReport(*BT, + "Call to free data when error was returned during allocation.", N); + Report->addVisitor(new SecKeychainBugVisitor(ArgSM)); + Report->addRange(ArgExpr->getSourceRange()); + C.EmitReport(Report); + return; + } + + C.addTransition(State); +} + +void MacOSKeychainAPIChecker::checkPostStmt(const CallExpr *CE, + CheckerContext &C) const { + const ProgramState *State = C.getState(); + const Expr *Callee = CE->getCallee(); + SVal L = State->getSVal(Callee); + + const FunctionDecl *funDecl = L.getAsFunctionDecl(); + if (!funDecl) + return; + IdentifierInfo *funI = funDecl->getIdentifier(); + if (!funI) + return; + StringRef funName = funI->getName(); + + // If a value has been allocated, add it to the set for tracking. + unsigned idx = getTrackedFunctionIndex(funName, true); + if (idx == InvalidIdx) + return; + + const Expr *ArgExpr = CE->getArg(FunctionsToTrack[idx].Param); + // If the argument entered as an enclosing function parameter, skip it to + // avoid false positives. + if (isEnclosingFunctionParam(ArgExpr)) + return; + + if (SymbolRef V = getAsPointeeSymbol(ArgExpr, C)) { + // If the argument points to something that's not a symbolic region, it + // can be: + // - unknown (cannot reason about it) + // - undefined (already reported by other checker) + // - constant (null - should not be tracked, + // other constant will generate a compiler warning) + // - goto (should be reported by other checker) + + // The call return value symbol should stay alive for as long as the + // allocated value symbol, since our diagnostics depend on the value + // returned by the call. Ex: Data should only be freed if noErr was + // returned during allocation.) + SymbolRef RetStatusSymbol = State->getSVal(CE).getAsSymbol(); + C.getSymbolManager().addSymbolDependency(V, RetStatusSymbol); + + // Track the allocated value in the checker state. + State = State->set<AllocatedData>(V, AllocationState(ArgExpr, idx, + RetStatusSymbol)); + assert(State); + C.addTransition(State); + } +} + +void MacOSKeychainAPIChecker::checkPreStmt(const ReturnStmt *S, + CheckerContext &C) const { + const Expr *retExpr = S->getRetValue(); + if (!retExpr) + return; + + // Check if the value is escaping through the return. + const ProgramState *state = C.getState(); + const MemRegion *V = state->getSVal(retExpr).getAsRegion(); + if (!V) + return; + state = state->remove<AllocatedData>(getSymbolForRegion(C, V)); + + // Proceed from the new state. + C.addTransition(state); +} + +BugReport *MacOSKeychainAPIChecker:: + generateAllocatedDataNotReleasedReport(const AllocationPair &AP, + ExplodedNode *N) const { + const ADFunctionInfo &FI = FunctionsToTrack[AP.second->AllocatorIdx]; + initBugType(); + llvm::SmallString<70> sbuf; + llvm::raw_svector_ostream os(sbuf); + + os << "Allocated data is not released: missing a call to '" + << FunctionsToTrack[FI.DeallocatorIdx].Name << "'."; + BugReport *Report = new BugReport(*BT, os.str(), N); + Report->addVisitor(new SecKeychainBugVisitor(AP.first)); + Report->addRange(SourceRange()); + return Report; +} + +void MacOSKeychainAPIChecker::checkDeadSymbols(SymbolReaper &SR, + CheckerContext &C) const { + const ProgramState *State = C.getState(); + AllocatedSetTy ASet = State->get<AllocatedData>(); + if (ASet.isEmpty()) + return; + + bool Changed = false; + AllocationPairVec Errors; + for (AllocatedSetTy::iterator I = ASet.begin(), E = ASet.end(); I != E; ++I) { + if (SR.isLive(I->first)) + continue; + + Changed = true; + State = State->remove<AllocatedData>(I->first); + // If the allocated symbol is null or if the allocation call might have + // returned an error, do not report. + if (State->getSymVal(I->first) || + definitelyReturnedError(I->second.Region, State, C.getSValBuilder())) + continue; + Errors.push_back(std::make_pair(I->first, &I->second)); + } + if (!Changed) + return; + + // Generate the new, cleaned up state. + ExplodedNode *N = C.generateNode(State); + if (!N) + return; + + // Generate the error reports. + for (AllocationPairVec::iterator I = Errors.begin(), E = Errors.end(); + I != E; ++I) { + C.EmitReport(generateAllocatedDataNotReleasedReport(*I, N)); + } +} + +// TODO: Remove this after we ensure that checkDeadSymbols are always called. +void MacOSKeychainAPIChecker::checkEndPath(EndOfFunctionNodeBuilder &B, + ExprEngine &Eng) const { + const ProgramState *state = B.getState(); + AllocatedSetTy AS = state->get<AllocatedData>(); + if (AS.isEmpty()) + return; + + // Anything which has been allocated but not freed (nor escaped) will be + // found here, so report it. + bool Changed = false; + AllocationPairVec Errors; + for (AllocatedSetTy::iterator I = AS.begin(), E = AS.end(); I != E; ++I ) { + Changed = true; + state = state->remove<AllocatedData>(I->first); + // If the allocated symbol is null or if error code was returned at + // allocation, do not report. + if (state->getSymVal(I.getKey()) || + definitelyReturnedError(I->second.Region, state, + Eng.getSValBuilder())) { + continue; + } + Errors.push_back(std::make_pair(I->first, &I->second)); + } + + // If no change, do not generate a new state. + if (!Changed) + return; + + ExplodedNode *N = B.generateNode(state); + if (!N) + return; + + // Generate the error reports. + for (AllocationPairVec::iterator I = Errors.begin(), E = Errors.end(); + I != E; ++I) { + Eng.getBugReporter().EmitReport( + generateAllocatedDataNotReleasedReport(*I, N)); + } +} + + +PathDiagnosticPiece *MacOSKeychainAPIChecker::SecKeychainBugVisitor::VisitNode( + const ExplodedNode *N, + const ExplodedNode *PrevN, + BugReporterContext &BRC, + BugReport &BR) { + const AllocationState *AS = N->getState()->get<AllocatedData>(Sym); + if (!AS) + return 0; + const AllocationState *ASPrev = PrevN->getState()->get<AllocatedData>(Sym); + if (ASPrev) + return 0; + + // (!ASPrev && AS) ~ We started tracking symbol in node N, it must be the + // allocation site. + const CallExpr *CE = cast<CallExpr>(cast<StmtPoint>(N->getLocation()) + .getStmt()); + const FunctionDecl *funDecl = CE->getDirectCallee(); + assert(funDecl && "We do not support indirect function calls as of now."); + StringRef funName = funDecl->getName(); + + // Get the expression of the corresponding argument. + unsigned Idx = getTrackedFunctionIndex(funName, true); + assert(Idx != InvalidIdx && "This should be a call to an allocator."); + const Expr *ArgExpr = CE->getArg(FunctionsToTrack[Idx].Param); + PathDiagnosticLocation Pos(ArgExpr, BRC.getSourceManager(), + N->getLocationContext()); + return new PathDiagnosticEventPiece(Pos, "Data is allocated here."); +} + +void ento::registerMacOSKeychainAPIChecker(CheckerManager &mgr) { + mgr.registerChecker<MacOSKeychainAPIChecker>(); +} diff --git a/lib/StaticAnalyzer/Checkers/MacOSXAPIChecker.cpp b/lib/StaticAnalyzer/Checkers/MacOSXAPIChecker.cpp index f8d076b54e3d..88d492e8d9a2 100644 --- a/lib/StaticAnalyzer/Checkers/MacOSXAPIChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/MacOSXAPIChecker.cpp @@ -20,7 +20,7 @@ #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/GRStateTrait.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" #include "clang/Basic/TargetInfo.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringSwitch.h" @@ -56,7 +56,7 @@ void MacOSXAPIChecker::CheckDispatchOnce(CheckerContext &C, const CallExpr *CE, // Check if the first argument is stack allocated. If so, issue a warning // because that's likely to be bad news. - const GRState *state = C.getState(); + const ProgramState *state = C.getState(); const MemRegion *R = state->getSVal(CE->getArg(0)).getAsRegion(); if (!R || !isa<StackSpaceRegion>(R->getMemorySpace())) return; @@ -81,7 +81,7 @@ void MacOSXAPIChecker::CheckDispatchOnce(CheckerContext &C, const CallExpr *CE, if (isa<VarRegion>(R) && isa<StackLocalsSpaceRegion>(R->getMemorySpace())) os << " Perhaps you intended to declare the variable as 'static'?"; - RangedBugReport *report = new RangedBugReport(*BT_dispatchOnce, os.str(), N); + BugReport *report = new BugReport(*BT_dispatchOnce, os.str(), N); report->addRange(CE->getArg(0)->getSourceRange()); C.EmitReport(report); } @@ -94,7 +94,7 @@ void MacOSXAPIChecker::checkPreStmt(const CallExpr *CE, CheckerContext &C) const { // FIXME: This sort of logic is common to several checkers, including // UnixAPIChecker, PthreadLockChecker, and CStringChecker. Should refactor. - const GRState *state = C.getState(); + const ProgramState *state = C.getState(); const Expr *Callee = CE->getCallee(); const FunctionDecl *Fn = state->getSVal(Callee).getAsFunctionDecl(); diff --git a/lib/StaticAnalyzer/Checkers/Makefile b/lib/StaticAnalyzer/Checkers/Makefile index 97f46424e472..2582908b95d0 100644 --- a/lib/StaticAnalyzer/Checkers/Makefile +++ b/lib/StaticAnalyzer/Checkers/Makefile @@ -19,6 +19,6 @@ TABLEGEN_INC_FILES_COMMON = 1 include $(CLANG_LEVEL)/Makefile -$(ObjDir)/Checkers.inc.tmp : Checkers.td $(PROJ_SRC_DIR)/$(CLANG_LEVEL)/include/clang/StaticAnalyzer/Checkers/CheckerBase.td $(TBLGEN) $(ObjDir)/.dir +$(ObjDir)/Checkers.inc.tmp : Checkers.td $(PROJ_SRC_DIR)/$(CLANG_LEVEL)/include/clang/StaticAnalyzer/Checkers/CheckerBase.td $(CLANG_TBLGEN) $(ObjDir)/.dir $(Echo) "Building Clang SA Checkers tables with tblgen" - $(Verb) $(TableGen) -gen-clang-sa-checkers -I $(PROJ_SRC_DIR)/$(CLANG_LEVEL)/include -o $(call SYSPATH, $@) $< + $(Verb) $(ClangTableGen) -gen-clang-sa-checkers -I $(PROJ_SRC_DIR)/$(CLANG_LEVEL)/include -o $(call SYSPATH, $@) $< diff --git a/lib/StaticAnalyzer/Checkers/MallocChecker.cpp b/lib/StaticAnalyzer/Checkers/MallocChecker.cpp index 91002158c57f..5631802b7c34 100644 --- a/lib/StaticAnalyzer/Checkers/MallocChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/MallocChecker.cpp @@ -17,8 +17,8 @@ #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/GRState.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/GRStateTrait.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h" #include "llvm/ADT/ImmutableMap.h" using namespace clang; @@ -80,35 +80,37 @@ public: void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const; void checkEndPath(EndOfFunctionNodeBuilder &B, ExprEngine &Eng) const; void checkPreStmt(const ReturnStmt *S, CheckerContext &C) const; - const GRState *evalAssume(const GRState *state, SVal Cond, + const ProgramState *evalAssume(const ProgramState *state, SVal Cond, bool Assumption) const; - void checkLocation(SVal l, bool isLoad, CheckerContext &C) const; - void checkBind(SVal location, SVal val, CheckerContext &C) const; + void checkLocation(SVal l, bool isLoad, const Stmt *S, + CheckerContext &C) const; + void checkBind(SVal location, SVal val, const Stmt*S, + CheckerContext &C) const; private: static void MallocMem(CheckerContext &C, const CallExpr *CE); static void MallocMemReturnsAttr(CheckerContext &C, const CallExpr *CE, const OwnershipAttr* Att); - static const GRState *MallocMemAux(CheckerContext &C, const CallExpr *CE, + static const ProgramState *MallocMemAux(CheckerContext &C, const CallExpr *CE, const Expr *SizeEx, SVal Init, - const GRState *state) { + const ProgramState *state) { return MallocMemAux(C, CE, state->getSVal(SizeEx), Init, state); } - static const GRState *MallocMemAux(CheckerContext &C, const CallExpr *CE, + static const ProgramState *MallocMemAux(CheckerContext &C, const CallExpr *CE, SVal SizeEx, SVal Init, - const GRState *state); + const ProgramState *state); void FreeMem(CheckerContext &C, const CallExpr *CE) const; void FreeMemAttr(CheckerContext &C, const CallExpr *CE, const OwnershipAttr* Att) const; - const GRState *FreeMemAux(CheckerContext &C, const CallExpr *CE, - const GRState *state, unsigned Num, bool Hold) const; + const ProgramState *FreeMemAux(CheckerContext &C, const CallExpr *CE, + const ProgramState *state, unsigned Num, bool Hold) const; void ReallocMem(CheckerContext &C, const CallExpr *CE) const; static void CallocMem(CheckerContext &C, const CallExpr *CE); - static bool SummarizeValue(llvm::raw_ostream& os, SVal V); - static bool SummarizeRegion(llvm::raw_ostream& os, const MemRegion *MR); + static bool SummarizeValue(raw_ostream &os, SVal V); + static bool SummarizeRegion(raw_ostream &os, const MemRegion *MR); void ReportBadFree(CheckerContext &C, SVal ArgVal, SourceRange range) const; }; } // end anonymous namespace @@ -118,15 +120,15 @@ typedef llvm::ImmutableMap<SymbolRef, RefState> RegionStateTy; namespace clang { namespace ento { template <> - struct GRStateTrait<RegionState> - : public GRStatePartialTrait<RegionStateTy> { + struct ProgramStateTrait<RegionState> + : public ProgramStatePartialTrait<RegionStateTy> { static void *GDMIndex() { static int x; return &x; } }; } } bool MallocChecker::evalCall(const CallExpr *CE, CheckerContext &C) const { - const GRState *state = C.getState(); + const ProgramState *state = C.getState(); const Expr *Callee = CE->getCallee(); SVal L = state->getSVal(Callee); @@ -193,7 +195,7 @@ bool MallocChecker::evalCall(const CallExpr *CE, CheckerContext &C) const { } void MallocChecker::MallocMem(CheckerContext &C, const CallExpr *CE) { - const GRState *state = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), + const ProgramState *state = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), C.getState()); C.addTransition(state); } @@ -205,21 +207,21 @@ void MallocChecker::MallocMemReturnsAttr(CheckerContext &C, const CallExpr *CE, OwnershipAttr::args_iterator I = Att->args_begin(), E = Att->args_end(); if (I != E) { - const GRState *state = + const ProgramState *state = MallocMemAux(C, CE, CE->getArg(*I), UndefinedVal(), C.getState()); C.addTransition(state); return; } - const GRState *state = MallocMemAux(C, CE, UnknownVal(), UndefinedVal(), + const ProgramState *state = MallocMemAux(C, CE, UnknownVal(), UndefinedVal(), C.getState()); C.addTransition(state); } -const GRState *MallocChecker::MallocMemAux(CheckerContext &C, +const ProgramState *MallocChecker::MallocMemAux(CheckerContext &C, const CallExpr *CE, SVal Size, SVal Init, - const GRState *state) { - unsigned Count = C.getNodeBuilder().getCurrentBlockCount(); + const ProgramState *state) { + unsigned Count = C.getCurrentBlockCount(); SValBuilder &svalBuilder = C.getSValBuilder(); // Set the return value. @@ -247,7 +249,7 @@ const GRState *MallocChecker::MallocMemAux(CheckerContext &C, } void MallocChecker::FreeMem(CheckerContext &C, const CallExpr *CE) const { - const GRState *state = FreeMemAux(C, CE, C.getState(), 0, false); + const ProgramState *state = FreeMemAux(C, CE, C.getState(), 0, false); if (state) C.addTransition(state); @@ -260,15 +262,15 @@ void MallocChecker::FreeMemAttr(CheckerContext &C, const CallExpr *CE, for (OwnershipAttr::args_iterator I = Att->args_begin(), E = Att->args_end(); I != E; ++I) { - const GRState *state = FreeMemAux(C, CE, C.getState(), *I, + const ProgramState *state = FreeMemAux(C, CE, C.getState(), *I, Att->getOwnKind() == OwnershipAttr::Holds); if (state) C.addTransition(state); } } -const GRState *MallocChecker::FreeMemAux(CheckerContext &C, const CallExpr *CE, - const GRState *state, unsigned Num, +const ProgramState *MallocChecker::FreeMemAux(CheckerContext &C, const CallExpr *CE, + const ProgramState *state, unsigned Num, bool Hold) const { const Expr *ArgExpr = CE->getArg(Num); SVal ArgVal = state->getSVal(ArgExpr); @@ -281,7 +283,7 @@ const GRState *MallocChecker::FreeMemAux(CheckerContext &C, const CallExpr *CE, // FIXME: Technically using 'Assume' here can result in a path // bifurcation. In such cases we need to return two states, not just one. - const GRState *notNullState, *nullState; + const ProgramState *notNullState, *nullState; llvm::tie(notNullState, nullState) = state->assume(location); // The explicit NULL case, no operation is performed. @@ -364,7 +366,7 @@ const GRState *MallocChecker::FreeMemAux(CheckerContext &C, const CallExpr *CE, return notNullState->set<RegionState>(Sym, RefState::getReleased(CE)); } -bool MallocChecker::SummarizeValue(llvm::raw_ostream& os, SVal V) { +bool MallocChecker::SummarizeValue(raw_ostream &os, SVal V) { if (nonloc::ConcreteInt *IntVal = dyn_cast<nonloc::ConcreteInt>(&V)) os << "an integer (" << IntVal->getValue() << ")"; else if (loc::ConcreteInt *ConstAddr = dyn_cast<loc::ConcreteInt>(&V)) @@ -377,13 +379,13 @@ bool MallocChecker::SummarizeValue(llvm::raw_ostream& os, SVal V) { return true; } -bool MallocChecker::SummarizeRegion(llvm::raw_ostream& os, +bool MallocChecker::SummarizeRegion(raw_ostream &os, const MemRegion *MR) { switch (MR->getKind()) { case MemRegion::FunctionTextRegionKind: { const FunctionDecl *FD = cast<FunctionTextRegion>(MR)->getDecl(); if (FD) - os << "the address of the function '" << FD << "'"; + os << "the address of the function '" << *FD << '\''; else os << "the address of a function"; return true; @@ -484,14 +486,14 @@ void MallocChecker::ReportBadFree(CheckerContext &C, SVal ArgVal, os << "not memory allocated by malloc()"; } - EnhancedBugReport *R = new EnhancedBugReport(*BT_BadFree, os.str(), N); + BugReport *R = new BugReport(*BT_BadFree, os.str(), N); R->addRange(range); C.EmitReport(R); } } void MallocChecker::ReallocMem(CheckerContext &C, const CallExpr *CE) const { - const GRState *state = C.getState(); + const ProgramState *state = C.getState(); const Expr *arg0Expr = CE->getArg(0); DefinedOrUnknownSVal arg0Val = cast<DefinedOrUnknownSVal>(state->getSVal(arg0Expr)); @@ -517,7 +519,7 @@ void MallocChecker::ReallocMem(CheckerContext &C, const CallExpr *CE) const { // If the ptr is NULL and the size is not 0, the call is equivalent to // malloc(size). - const GRState *stateEqual = state->assume(PtrEQ, true); + const ProgramState *stateEqual = state->assume(PtrEQ, true); if (stateEqual && state->assume(SizeZero, false)) { // Hack: set the NULL symbolic region to released to suppress false warning. // In the future we should add more states for allocated regions, e.g., @@ -527,28 +529,25 @@ void MallocChecker::ReallocMem(CheckerContext &C, const CallExpr *CE) const { if (Sym) stateEqual = stateEqual->set<RegionState>(Sym, RefState::getReleased(CE)); - const GRState *stateMalloc = MallocMemAux(C, CE, CE->getArg(1), + const ProgramState *stateMalloc = MallocMemAux(C, CE, CE->getArg(1), UndefinedVal(), stateEqual); C.addTransition(stateMalloc); } - if (const GRState *stateNotEqual = state->assume(PtrEQ, false)) { + if (const ProgramState *stateNotEqual = state->assume(PtrEQ, false)) { // If the size is 0, free the memory. - if (const GRState *stateSizeZero = stateNotEqual->assume(SizeZero, true)) - if (const GRState *stateFree = + if (const ProgramState *stateSizeZero = stateNotEqual->assume(SizeZero, true)) + if (const ProgramState *stateFree = FreeMemAux(C, CE, stateSizeZero, 0, false)) { - // Add the state transition to set input pointer argument to be free. - C.addTransition(stateFree); - - // Bind the return value to UndefinedVal because it is now free. - C.addTransition(stateFree->BindExpr(CE, UndefinedVal(), true)); + // Bind the return value to NULL because it is now free. + C.addTransition(stateFree->BindExpr(CE, svalBuilder.makeNull(), true)); } - if (const GRState *stateSizeNotZero = stateNotEqual->assume(SizeZero,false)) - if (const GRState *stateFree = FreeMemAux(C, CE, stateSizeNotZero, + if (const ProgramState *stateSizeNotZero = stateNotEqual->assume(SizeZero,false)) + if (const ProgramState *stateFree = FreeMemAux(C, CE, stateSizeNotZero, 0, false)) { // FIXME: We should copy the content of the original buffer. - const GRState *stateRealloc = MallocMemAux(C, CE, CE->getArg(1), + const ProgramState *stateRealloc = MallocMemAux(C, CE, CE->getArg(1), UnknownVal(), stateFree); C.addTransition(stateRealloc); } @@ -556,7 +555,7 @@ void MallocChecker::ReallocMem(CheckerContext &C, const CallExpr *CE) const { } void MallocChecker::CallocMem(CheckerContext &C, const CallExpr *CE) { - const GRState *state = C.getState(); + const ProgramState *state = C.getState(); SValBuilder &svalBuilder = C.getSValBuilder(); SVal count = state->getSVal(CE->getArg(0)); @@ -574,33 +573,40 @@ void MallocChecker::checkDeadSymbols(SymbolReaper &SymReaper, if (!SymReaper.hasDeadSymbols()) return; - const GRState *state = C.getState(); + const ProgramState *state = C.getState(); RegionStateTy RS = state->get<RegionState>(); RegionStateTy::Factory &F = state->get_context<RegionState>(); + bool generateReport = false; + for (RegionStateTy::iterator I = RS.begin(), E = RS.end(); I != E; ++I) { if (SymReaper.isDead(I->first)) { - if (I->second.isAllocated()) { - if (ExplodedNode *N = C.generateNode()) { - if (!BT_Leak) - BT_Leak.reset(new BuiltinBug("Memory leak", - "Allocated memory never released. Potential memory leak.")); - // FIXME: where it is allocated. - BugReport *R = new BugReport(*BT_Leak, BT_Leak->getDescription(), N); - C.EmitReport(R); - } - } + if (I->second.isAllocated()) + generateReport = true; // Remove the dead symbol from the map. RS = F.remove(RS, I->first); + } } - C.generateNode(state->set<RegionState>(RS)); + + ExplodedNode *N = C.generateNode(state->set<RegionState>(RS)); + + // FIXME: This does not handle when we have multiple leaks at a single + // place. + if (N && generateReport) { + if (!BT_Leak) + BT_Leak.reset(new BuiltinBug("Memory leak", + "Allocated memory never released. Potential memory leak.")); + // FIXME: where it is allocated. + BugReport *R = new BugReport(*BT_Leak, BT_Leak->getDescription(), N); + C.EmitReport(R); + } } void MallocChecker::checkEndPath(EndOfFunctionNodeBuilder &B, ExprEngine &Eng) const { - const GRState *state = B.getState(); + const ProgramState *state = B.getState(); RegionStateTy M = state->get<RegionState>(); for (RegionStateTy::iterator I = M.begin(), E = M.end(); I != E; ++I) { @@ -623,7 +629,7 @@ void MallocChecker::checkPreStmt(const ReturnStmt *S, CheckerContext &C) const { if (!retExpr) return; - const GRState *state = C.getState(); + const ProgramState *state = C.getState(); SymbolRef Sym = state->getSVal(retExpr).getAsSymbol(); if (!Sym) @@ -640,7 +646,7 @@ void MallocChecker::checkPreStmt(const ReturnStmt *S, CheckerContext &C) const { C.addTransition(state); } -const GRState *MallocChecker::evalAssume(const GRState *state, SVal Cond, +const ProgramState *MallocChecker::evalAssume(const ProgramState *state, SVal Cond, bool Assumption) const { // If a symblic region is assumed to NULL, set its state to AllocateFailed. // FIXME: should also check symbols assumed to non-null. @@ -657,7 +663,8 @@ const GRState *MallocChecker::evalAssume(const GRState *state, SVal Cond, } // Check if the location is a freed symbolic region. -void MallocChecker::checkLocation(SVal l, bool isLoad,CheckerContext &C) const { +void MallocChecker::checkLocation(SVal l, bool isLoad, const Stmt *S, + CheckerContext &C) const { SymbolRef Sym = l.getLocSymbolInBase(); if (Sym) { const RefState *RS = C.getState()->get<RegionState>(Sym); @@ -675,13 +682,14 @@ void MallocChecker::checkLocation(SVal l, bool isLoad,CheckerContext &C) const { } } -void MallocChecker::checkBind(SVal location, SVal val,CheckerContext &C) const { +void MallocChecker::checkBind(SVal location, SVal val, + const Stmt *BindS, CheckerContext &C) const { // The PreVisitBind implements the same algorithm as already used by the // Objective C ownership checker: if the pointer escaped from this scope by // assignment, let it go. However, assigning to fields of a stack-storage // structure does not transfer ownership. - const GRState *state = C.getState(); + const ProgramState *state = C.getState(); DefinedOrUnknownSVal l = cast<DefinedOrUnknownSVal>(location); // Check for null dereferences. @@ -694,7 +702,7 @@ void MallocChecker::checkBind(SVal location, SVal val,CheckerContext &C) const { if (Sym) { if (const RefState *RS = state->get<RegionState>(Sym)) { // If ptr is NULL, no operation is performed. - const GRState *notNullState, *nullState; + const ProgramState *notNullState, *nullState; llvm::tie(notNullState, nullState) = state->assume(l); // Generate a transition for 'nullState' to record the assumption @@ -724,7 +732,7 @@ void MallocChecker::checkBind(SVal location, SVal val,CheckerContext &C) const { // We no longer own this pointer. notNullState = notNullState->set<RegionState>(Sym, - RefState::getRelinquished(C.getStmt())); + RefState::getRelinquished(BindS)); } while (false); } diff --git a/lib/StaticAnalyzer/Checkers/MallocOverflowSecurityChecker.cpp b/lib/StaticAnalyzer/Checkers/MallocOverflowSecurityChecker.cpp new file mode 100644 index 000000000000..cf5501a4ac15 --- /dev/null +++ b/lib/StaticAnalyzer/Checkers/MallocOverflowSecurityChecker.cpp @@ -0,0 +1,268 @@ +// MallocOverflowSecurityChecker.cpp - Check for malloc overflows -*- C++ -*-=// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This checker detects a common memory allocation security flaw. +// Suppose 'unsigned int n' comes from an untrusted source. If the +// code looks like 'malloc (n * 4)', and an attacker can make 'n' be +// say MAX_UINT/4+2, then instead of allocating the correct 'n' 4-byte +// elements, this will actually allocate only two because of overflow. +// Then when the rest of the program attempts to store values past the +// second element, these values will actually overwrite other items in +// the heap, probably allowing the attacker to execute arbitrary code. +// +//===----------------------------------------------------------------------===// + +#include "ClangSACheckers.h" +#include "clang/AST/EvaluatedExprVisitor.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" +#include "llvm/ADT/SmallVector.h" + +using namespace clang; +using namespace ento; + +namespace { +struct MallocOverflowCheck { + const BinaryOperator *mulop; + const Expr *variable; + + MallocOverflowCheck (const BinaryOperator *m, const Expr *v) + : mulop(m), variable (v) + {} +}; + +class MallocOverflowSecurityChecker : public Checker<check::ASTCodeBody> { +public: + void checkASTCodeBody(const Decl *D, AnalysisManager &mgr, + BugReporter &BR) const; + + void CheckMallocArgument( + llvm::SmallVectorImpl<MallocOverflowCheck> &PossibleMallocOverflows, + const Expr *TheArgument, ASTContext &Context) const; + + void OutputPossibleOverflows( + llvm::SmallVectorImpl<MallocOverflowCheck> &PossibleMallocOverflows, + const Decl *D, BugReporter &BR, AnalysisManager &mgr) const; + +}; +} // end anonymous namespace + +void MallocOverflowSecurityChecker::CheckMallocArgument( + llvm::SmallVectorImpl<MallocOverflowCheck> &PossibleMallocOverflows, + const Expr *TheArgument, + ASTContext &Context) const { + + /* Look for a linear combination with a single variable, and at least + one multiplication. + Reject anything that applies to the variable: an explicit cast, + conditional expression, an operation that could reduce the range + of the result, or anything too complicated :-). */ + const Expr * e = TheArgument; + const BinaryOperator * mulop = NULL; + + for (;;) { + e = e->IgnoreParenImpCasts(); + if (isa<BinaryOperator>(e)) { + const BinaryOperator * binop = dyn_cast<BinaryOperator>(e); + BinaryOperatorKind opc = binop->getOpcode(); + // TODO: ignore multiplications by 1, reject if multiplied by 0. + if (mulop == NULL && opc == BO_Mul) + mulop = binop; + if (opc != BO_Mul && opc != BO_Add && opc != BO_Sub && opc != BO_Shl) + return; + + const Expr *lhs = binop->getLHS(); + const Expr *rhs = binop->getRHS(); + if (rhs->isEvaluatable(Context)) + e = lhs; + else if ((opc == BO_Add || opc == BO_Mul) + && lhs->isEvaluatable(Context)) + e = rhs; + else + return; + } + else if (isa<DeclRefExpr>(e) || isa<MemberExpr>(e)) + break; + else + return; + } + + if (mulop == NULL) + return; + + // We've found the right structure of malloc argument, now save + // the data so when the body of the function is completely available + // we can check for comparisons. + + // TODO: Could push this into the innermost scope where 'e' is + // defined, rather than the whole function. + PossibleMallocOverflows.push_back(MallocOverflowCheck(mulop, e)); +} + +namespace { +// A worker class for OutputPossibleOverflows. +class CheckOverflowOps : + public EvaluatedExprVisitor<CheckOverflowOps> { +public: + typedef llvm::SmallVectorImpl<MallocOverflowCheck> theVecType; + +private: + theVecType &toScanFor; + ASTContext &Context; + + bool isIntZeroExpr(const Expr *E) const { + if (!E->getType()->isIntegralOrEnumerationType()) + return false; + llvm::APSInt Result; + if (E->EvaluateAsInt(Result, Context)) + return Result == 0; + return false; + } + + void CheckExpr(const Expr *E_p) { + const Expr *E = E_p->IgnoreParenImpCasts(); + + theVecType::iterator i = toScanFor.end(); + theVecType::iterator e = toScanFor.begin(); + + if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(E)) { + const Decl * EdreD = DR->getDecl(); + while (i != e) { + --i; + if (const DeclRefExpr *DR_i = dyn_cast<DeclRefExpr>(i->variable)) { + if (DR_i->getDecl() == EdreD) + i = toScanFor.erase(i); + } + } + } + else if (isa<MemberExpr>(E)) { + // No points-to analysis, just look at the member + const Decl * EmeMD = dyn_cast<MemberExpr>(E)->getMemberDecl(); + while (i != e) { + --i; + if (isa<MemberExpr>(i->variable)) { + if (dyn_cast<MemberExpr>(i->variable)->getMemberDecl() == EmeMD) + i = toScanFor.erase (i); + } + } + } + } + + public: + void VisitBinaryOperator(BinaryOperator *E) { + if (E->isComparisonOp()) { + const Expr * lhs = E->getLHS(); + const Expr * rhs = E->getRHS(); + // Ignore comparisons against zero, since they generally don't + // protect against an overflow. + if (!isIntZeroExpr(lhs) && ! isIntZeroExpr(rhs)) { + CheckExpr(lhs); + CheckExpr(rhs); + } + } + EvaluatedExprVisitor<CheckOverflowOps>::VisitBinaryOperator(E); + } + + /* We specifically ignore loop conditions, because they're typically + not error checks. */ + void VisitWhileStmt(WhileStmt *S) { + return this->Visit(S->getBody()); + } + void VisitForStmt(ForStmt *S) { + return this->Visit(S->getBody()); + } + void VisitDoStmt(DoStmt *S) { + return this->Visit(S->getBody()); + } + + CheckOverflowOps(theVecType &v, ASTContext &ctx) + : EvaluatedExprVisitor<CheckOverflowOps>(ctx), + toScanFor(v), Context(ctx) + { } + }; +} + +// OutputPossibleOverflows - We've found a possible overflow earlier, +// now check whether Body might contain a comparison which might be +// preventing the overflow. +// This doesn't do flow analysis, range analysis, or points-to analysis; it's +// just a dumb "is there a comparison" scan. The aim here is to +// detect the most blatent cases of overflow and educate the +// programmer. +void MallocOverflowSecurityChecker::OutputPossibleOverflows( + llvm::SmallVectorImpl<MallocOverflowCheck> &PossibleMallocOverflows, + const Decl *D, BugReporter &BR, AnalysisManager &mgr) const { + // By far the most common case: nothing to check. + if (PossibleMallocOverflows.empty()) + return; + + // Delete any possible overflows which have a comparison. + CheckOverflowOps c(PossibleMallocOverflows, BR.getContext()); + c.Visit(mgr.getAnalysisContext(D)->getBody()); + + // Output warnings for all overflows that are left. + for (CheckOverflowOps::theVecType::iterator + i = PossibleMallocOverflows.begin(), + e = PossibleMallocOverflows.end(); + i != e; + ++i) { + SourceRange R = i->mulop->getSourceRange(); + BR.EmitBasicReport("MallocOverflowSecurityChecker", + "the computation of the size of the memory allocation may overflow", + PathDiagnosticLocation::createOperatorLoc(i->mulop, + BR.getSourceManager()), + &R, 1); + } +} + +void MallocOverflowSecurityChecker::checkASTCodeBody(const Decl *D, + AnalysisManager &mgr, + BugReporter &BR) const { + + CFG *cfg = mgr.getCFG(D); + if (!cfg) + return; + + // A list of variables referenced in possibly overflowing malloc operands. + llvm::SmallVector<MallocOverflowCheck, 2> PossibleMallocOverflows; + + for (CFG::iterator it = cfg->begin(), ei = cfg->end(); it != ei; ++it) { + CFGBlock *block = *it; + for (CFGBlock::iterator bi = block->begin(), be = block->end(); + bi != be; ++bi) { + if (const CFGStmt *CS = bi->getAs<CFGStmt>()) { + if (const CallExpr *TheCall = dyn_cast<CallExpr>(CS->getStmt())) { + // Get the callee. + const FunctionDecl *FD = TheCall->getDirectCallee(); + + if (!FD) + return; + + // Get the name of the callee. If it's a builtin, strip off the prefix. + IdentifierInfo *FnInfo = FD->getIdentifier(); + if (!FnInfo) + return; + + if (FnInfo->isStr ("malloc") || FnInfo->isStr ("_MALLOC")) { + if (TheCall->getNumArgs() == 1) + CheckMallocArgument(PossibleMallocOverflows, TheCall->getArg(0), + mgr.getASTContext()); + } + } + } + } + } + + OutputPossibleOverflows(PossibleMallocOverflows, D, BR, mgr); +} + +void ento::registerMallocOverflowSecurityChecker(CheckerManager &mgr) { + mgr.registerChecker<MallocOverflowSecurityChecker>(); +} diff --git a/lib/StaticAnalyzer/Checkers/NSAutoreleasePoolChecker.cpp b/lib/StaticAnalyzer/Checkers/NSAutoreleasePoolChecker.cpp index f11db6458cc0..7f74a7d015d6 100644 --- a/lib/StaticAnalyzer/Checkers/NSAutoreleasePoolChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/NSAutoreleasePoolChecker.cpp @@ -9,7 +9,7 @@ // // This file defines a NSAutoreleasePoolChecker, a small checker that warns // about subpar uses of NSAutoreleasePool. Note that while the check itself -// (in it's current form) could be written as a flow-insensitive check, in +// (in its current form) could be written as a flow-insensitive check, in // can be potentially enhanced in the future with flow-sensitive information. // It is also a good example of the CheckerVisitor interface. // @@ -18,9 +18,10 @@ #include "ClangSACheckers.h" #include "clang/StaticAnalyzer/Core/Checker.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/CheckerContext.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/Decl.h" @@ -53,7 +54,7 @@ void NSAutoreleasePoolChecker::checkPreObjCMessage(ObjCMessage msg, if (!PT) return; - const ObjCInterfaceDecl* OD = PT->getInterfaceDecl(); + const ObjCInterfaceDecl *OD = PT->getInterfaceDecl(); if (!OD) return; if (!OD->getIdentifier()->getName().equals("NSAutoreleasePool")) @@ -66,14 +67,18 @@ void NSAutoreleasePoolChecker::checkPreObjCMessage(ObjCMessage msg, return; SourceRange R = msg.getSourceRange(); - + BugReporter &BR = C.getBugReporter(); + const LocationContext *LC = C.getPredecessor()->getLocationContext(); + const SourceManager &SM = BR.getSourceManager(); + const Expr *E = msg.getMsgOrPropExpr(); + PathDiagnosticLocation L = PathDiagnosticLocation::createBegin(E, SM, LC); C.getBugReporter().EmitBasicReport("Use -drain instead of -release", "API Upgrade (Apple)", "Use -drain instead of -release when using NSAutoreleasePool " - "and garbage collection", R.getBegin(), &R, 1); + "and garbage collection", L, &R, 1); } void ento::registerNSAutoreleasePoolChecker(CheckerManager &mgr) { - if (mgr.getLangOptions().getGCMode() != LangOptions::NonGC) + if (mgr.getLangOptions().getGC() != LangOptions::NonGC) mgr.registerChecker<NSAutoreleasePoolChecker>(); } diff --git a/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp b/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp index a51d8e0d19ae..56789983593e 100644 --- a/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp @@ -19,7 +19,7 @@ #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/GRStateTrait.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/Decl.h" @@ -60,7 +60,7 @@ void NSErrorMethodChecker::checkASTDecl(const ObjCMethodDecl *D, II = &D->getASTContext().Idents.get("NSError"); bool hasNSError = false; - for (ObjCMethodDecl::param_iterator + for (ObjCMethodDecl::param_const_iterator I = D->param_begin(), E = D->param_end(); I != E; ++I) { if (IsNSError((*I)->getType(), II)) { hasNSError = true; @@ -72,8 +72,10 @@ void NSErrorMethodChecker::checkASTDecl(const ObjCMethodDecl *D, const char *err = "Method accepting NSError** " "should have a non-void return value to indicate whether or not an " "error occurred"; + PathDiagnosticLocation L = + PathDiagnosticLocation::create(D, BR.getSourceManager()); BR.EmitBasicReport("Bad return type when passing NSError**", - "Coding conventions (Apple)", err, D->getLocation()); + "Coding conventions (Apple)", err, L); } } @@ -118,8 +120,10 @@ void CFErrorFunctionChecker::checkASTDecl(const FunctionDecl *D, const char *err = "Function accepting CFErrorRef* " "should have a non-void return value to indicate whether or not an " "error occurred"; + PathDiagnosticLocation L = + PathDiagnosticLocation::create(D, BR.getSourceManager()); BR.EmitBasicReport("Bad return type when passing CFErrorRef*", - "Coding conventions (Apple)", err, D->getLocation()); + "Coding conventions (Apple)", err, L); } } @@ -153,7 +157,8 @@ public: NSOrCFErrorDerefChecker() : NSErrorII(0), CFErrorII(0), ShouldCheckNSError(0), ShouldCheckCFError(0) { } - void checkLocation(SVal loc, bool isLoad, CheckerContext &C) const; + void checkLocation(SVal loc, bool isLoad, const Stmt *S, + CheckerContext &C) const; void checkEvent(ImplicitNullDerefEvent event) const; }; } @@ -166,18 +171,18 @@ typedef llvm::ImmutableMap<SymbolRef, unsigned> ErrorOutFlag; namespace clang { namespace ento { template <> - struct GRStateTrait<NSErrorOut> : public GRStatePartialTrait<ErrorOutFlag> { + struct ProgramStateTrait<NSErrorOut> : public ProgramStatePartialTrait<ErrorOutFlag> { static void *GDMIndex() { static int index = 0; return &index; } }; template <> - struct GRStateTrait<CFErrorOut> : public GRStatePartialTrait<ErrorOutFlag> { + struct ProgramStateTrait<CFErrorOut> : public ProgramStatePartialTrait<ErrorOutFlag> { static void *GDMIndex() { static int index = 0; return &index; } }; } } template <typename T> -static bool hasFlag(SVal val, const GRState *state) { +static bool hasFlag(SVal val, const ProgramState *state) { if (SymbolRef sym = val.getAsSymbol()) if (const unsigned *attachedFlags = state->get<T>(sym)) return *attachedFlags; @@ -185,7 +190,7 @@ static bool hasFlag(SVal val, const GRState *state) { } template <typename T> -static void setFlag(const GRState *state, SVal val, CheckerContext &C) { +static void setFlag(const ProgramState *state, SVal val, CheckerContext &C) { // We tag the symbol that the SVal wraps. if (SymbolRef sym = val.getAsSymbol()) C.addTransition(state->set<T>(sym, true)); @@ -207,6 +212,7 @@ static QualType parameterTypeFromSVal(SVal val, CheckerContext &C) { } void NSOrCFErrorDerefChecker::checkLocation(SVal loc, bool isLoad, + const Stmt *S, CheckerContext &C) const { if (!isLoad) return; @@ -214,7 +220,7 @@ void NSOrCFErrorDerefChecker::checkLocation(SVal loc, bool isLoad, return; ASTContext &Ctx = C.getASTContext(); - const GRState *state = C.getState(); + const ProgramState *state = C.getState(); // If we are loading from NSError**/CFErrorRef* parameter, mark the resulting // SVal so that we can later check it when handling the @@ -247,7 +253,7 @@ void NSOrCFErrorDerefChecker::checkEvent(ImplicitNullDerefEvent event) const { return; SVal loc = event.Location; - const GRState *state = event.SinkNode->getState(); + const ProgramState *state = event.SinkNode->getState(); BugReporter &BR = *event.BR; bool isNSError = hasFlag<NSErrorOut>(loc, state); @@ -277,7 +283,7 @@ void NSOrCFErrorDerefChecker::checkEvent(ImplicitNullDerefEvent event) const { bug = new NSErrorDerefBug(); else bug = new CFErrorDerefBug(); - EnhancedBugReport *report = new EnhancedBugReport(*bug, os.str(), + BugReport *report = new BugReport(*bug, os.str(), event.SinkNode); BR.EmitReport(report); } diff --git a/lib/StaticAnalyzer/Checkers/NoReturnFunctionChecker.cpp b/lib/StaticAnalyzer/Checkers/NoReturnFunctionChecker.cpp index 2d0af9c978dc..81f1924b2c7a 100644 --- a/lib/StaticAnalyzer/Checkers/NoReturnFunctionChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/NoReturnFunctionChecker.cpp @@ -16,23 +16,27 @@ #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h" #include "llvm/ADT/StringSwitch.h" +#include <cstdarg> using namespace clang; using namespace ento; namespace { -class NoReturnFunctionChecker : public Checker< check::PostStmt<CallExpr> > { +class NoReturnFunctionChecker : public Checker< check::PostStmt<CallExpr>, + check::PostObjCMessage > { public: void checkPostStmt(const CallExpr *CE, CheckerContext &C) const; + void checkPostObjCMessage(const ObjCMessage &msg, CheckerContext &C) const; }; } void NoReturnFunctionChecker::checkPostStmt(const CallExpr *CE, CheckerContext &C) const { - const GRState *state = C.getState(); + const ProgramState *state = C.getState(); const Expr *Callee = CE->getCallee(); bool BuildSinks = getFunctionExtInfo(Callee->getType()).getNoReturn(); @@ -50,7 +54,7 @@ void NoReturnFunctionChecker::checkPostStmt(const CallExpr *CE, // Here are a few hardwired ones. If this takes too long, we can // potentially cache these results. BuildSinks - = llvm::StringSwitch<bool>(llvm::StringRef(II->getName())) + = llvm::StringSwitch<bool>(StringRef(II->getName())) .Case("exit", true) .Case("panic", true) .Case("error", true) @@ -73,9 +77,70 @@ void NoReturnFunctionChecker::checkPostStmt(const CallExpr *CE, } if (BuildSinks) - C.generateSink(CE); + C.generateSink(); } +static bool END_WITH_NULL isMultiArgSelector(const Selector *Sel, ...) { + va_list argp; + va_start(argp, Sel); + + unsigned Slot = 0; + const char *Arg; + while ((Arg = va_arg(argp, const char *))) { + if (!Sel->getNameForSlot(Slot).equals(Arg)) + break; // still need to va_end! + ++Slot; + } + + va_end(argp); + + // We only succeeded if we made it to the end of the argument list. + return (Arg == NULL); +} + +void NoReturnFunctionChecker::checkPostObjCMessage(const ObjCMessage &Msg, + CheckerContext &C) const { + // HACK: This entire check is to handle two messages in the Cocoa frameworks: + // -[NSAssertionHandler + // handleFailureInMethod:object:file:lineNumber:description:] + // -[NSAssertionHandler + // handleFailureInFunction:file:lineNumber:description:] + // Eventually these should be annotated with __attribute__((noreturn)). + // Because ObjC messages use dynamic dispatch, it is not generally safe to + // assume certain methods can't return. In cases where it is definitely valid, + // see if you can mark the methods noreturn or analyzer_noreturn instead of + // adding more explicit checks to this method. + + if (!Msg.isInstanceMessage()) + return; + + const ObjCInterfaceDecl *Receiver = Msg.getReceiverInterface(); + if (!Receiver) + return; + if (!Receiver->getIdentifier()->isStr("NSAssertionHandler")) + return; + + Selector Sel = Msg.getSelector(); + switch (Sel.getNumArgs()) { + default: + return; + case 4: + if (!isMultiArgSelector(&Sel, "handleFailureInFunction", "file", + "lineNumber", "description", NULL)) + return; + break; + case 5: + if (!isMultiArgSelector(&Sel, "handleFailureInMethod", "object", "file", + "lineNumber", "description", NULL)) + return; + break; + } + + // If we got here, it's one of the messages we care about. + C.generateSink(); +} + + void ento::registerNoReturnFunctionChecker(CheckerManager &mgr) { mgr.registerChecker<NoReturnFunctionChecker>(); } diff --git a/lib/StaticAnalyzer/Checkers/OSAtomicChecker.cpp b/lib/StaticAnalyzer/Checkers/OSAtomicChecker.cpp index 7262bc36404d..f426265f6713 100644 --- a/lib/StaticAnalyzer/Checkers/OSAtomicChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/OSAtomicChecker.cpp @@ -22,22 +22,32 @@ using namespace ento; namespace { -class OSAtomicChecker : public Checker<eval::Call> { +class OSAtomicChecker : public Checker<eval::InlineCall> { public: - bool evalCall(const CallExpr *CE, CheckerContext &C) const; + bool inlineCall(const CallExpr *CE, ExprEngine &Eng, + ExplodedNode *Pred, ExplodedNodeSet &Dst) const; private: - static bool evalOSAtomicCompareAndSwap(CheckerContext &C, const CallExpr *CE); + bool evalOSAtomicCompareAndSwap(const CallExpr *CE, + ExprEngine &Eng, + ExplodedNode *Pred, + ExplodedNodeSet &Dst) const; + + ExplodedNode *generateNode(const ProgramState *State, + ExplodedNode *Pred, const CallExpr *Statement, + StmtNodeBuilder &B, ExplodedNodeSet &Dst) const; }; - } -bool OSAtomicChecker::evalCall(const CallExpr *CE, CheckerContext &C) const { - const GRState *state = C.getState(); +bool OSAtomicChecker::inlineCall(const CallExpr *CE, + ExprEngine &Eng, + ExplodedNode *Pred, + ExplodedNodeSet &Dst) const { + const ProgramState *state = Pred->getState(); const Expr *Callee = CE->getCallee(); SVal L = state->getSVal(Callee); - const FunctionDecl* FD = L.getAsFunctionDecl(); + const FunctionDecl *FD = L.getAsFunctionDecl(); if (!FD) return false; @@ -45,24 +55,38 @@ bool OSAtomicChecker::evalCall(const CallExpr *CE, CheckerContext &C) const { if (!II) return false; - llvm::StringRef FName(II->getName()); + StringRef FName(II->getName()); // Check for compare and swap. if (FName.startswith("OSAtomicCompareAndSwap") || FName.startswith("objc_atomicCompareAndSwap")) - return evalOSAtomicCompareAndSwap(C, CE); + return evalOSAtomicCompareAndSwap(CE, Eng, Pred, Dst); // FIXME: Other atomics. return false; } -bool OSAtomicChecker::evalOSAtomicCompareAndSwap(CheckerContext &C, - const CallExpr *CE) { +ExplodedNode *OSAtomicChecker::generateNode(const ProgramState *State, + ExplodedNode *Pred, + const CallExpr *Statement, + StmtNodeBuilder &B, + ExplodedNodeSet &Dst) const { + ExplodedNode *N = B.generateNode(Statement, State, Pred, this); + if (N) + Dst.Add(N); + return N; +} + +bool OSAtomicChecker::evalOSAtomicCompareAndSwap(const CallExpr *CE, + ExprEngine &Eng, + ExplodedNode *Pred, + ExplodedNodeSet &Dst) const { // Not enough arguments to match OSAtomicCompareAndSwap? if (CE->getNumArgs() != 3) return false; - ASTContext &Ctx = C.getASTContext(); + StmtNodeBuilder &Builder = Eng.getBuilder(); + ASTContext &Ctx = Eng.getContext(); const Expr *oldValueExpr = CE->getArg(0); QualType oldValueType = Ctx.getCanonicalType(oldValueExpr->getType()); @@ -87,15 +111,11 @@ bool OSAtomicChecker::evalOSAtomicCompareAndSwap(CheckerContext &C, if (theValueTypePointee != newValueType) return false; - static unsigned magic_load = 0; - static unsigned magic_store = 0; - - const void *OSAtomicLoadTag = &magic_load; - const void *OSAtomicStoreTag = &magic_store; - + static SimpleProgramPointTag OSAtomicLoadTag("OSAtomicChecker : Load"); + static SimpleProgramPointTag OSAtomicStoreTag("OSAtomicChecker : Store"); + // Load 'theValue'. - ExprEngine &Engine = C.getEngine(); - const GRState *state = C.getState(); + const ProgramState *state = Pred->getState(); ExplodedNodeSet Tmp; SVal location = state->getSVal(theValueExpr); // Here we should use the value type of the region as the load type, because @@ -106,19 +126,19 @@ bool OSAtomicChecker::evalOSAtomicCompareAndSwap(CheckerContext &C, // LoadTy specifying can be omitted. But we put it here to emphasize the // semantics. QualType LoadTy; - if (const TypedRegion *TR = - dyn_cast_or_null<TypedRegion>(location.getAsRegion())) { + if (const TypedValueRegion *TR = + dyn_cast_or_null<TypedValueRegion>(location.getAsRegion())) { LoadTy = TR->getValueType(); } - Engine.evalLoad(Tmp, theValueExpr, C.getPredecessor(), - state, location, OSAtomicLoadTag, LoadTy); + Eng.evalLoad(Tmp, theValueExpr, Pred, + state, location, &OSAtomicLoadTag, LoadTy); if (Tmp.empty()) { // If no nodes were generated, other checkers must generated sinks. But // since the builder state was restored, we set it manually to prevent // auto transition. // FIXME: there should be a better approach. - C.getNodeBuilder().BuildSinks = true; + Builder.BuildSinks = true; return true; } @@ -126,7 +146,7 @@ bool OSAtomicChecker::evalOSAtomicCompareAndSwap(CheckerContext &C, I != E; ++I) { ExplodedNode *N = *I; - const GRState *stateLoad = N->getState(); + const ProgramState *stateLoad = N->getState(); // Use direct bindings from the environment since we are forcing a load // from a location that the Environment would typically not be used @@ -145,12 +165,13 @@ bool OSAtomicChecker::evalOSAtomicCompareAndSwap(CheckerContext &C, DefinedOrUnknownSVal oldValueVal = cast<DefinedOrUnknownSVal>(oldValueVal_untested); - SValBuilder &svalBuilder = Engine.getSValBuilder(); + SValBuilder &svalBuilder = Eng.getSValBuilder(); // Perform the comparison. - DefinedOrUnknownSVal Cmp = svalBuilder.evalEQ(stateLoad,theValueVal,oldValueVal); + DefinedOrUnknownSVal Cmp = + svalBuilder.evalEQ(stateLoad,theValueVal,oldValueVal); - const GRState *stateEqual = stateLoad->assume(Cmp, true); + const ProgramState *stateEqual = stateLoad->assume(Cmp, true); // Were they equal? if (stateEqual) { @@ -159,20 +180,20 @@ bool OSAtomicChecker::evalOSAtomicCompareAndSwap(CheckerContext &C, SVal val = stateEqual->getSVal(newValueExpr); // Handle implicit value casts. - if (const TypedRegion *R = - dyn_cast_or_null<TypedRegion>(location.getAsRegion())) { + if (const TypedValueRegion *R = + dyn_cast_or_null<TypedValueRegion>(location.getAsRegion())) { val = svalBuilder.evalCast(val,R->getValueType(), newValueExpr->getType()); } - Engine.evalStore(TmpStore, NULL, theValueExpr, N, - stateEqual, location, val, OSAtomicStoreTag); + Eng.evalStore(TmpStore, NULL, theValueExpr, N, + stateEqual, location, val, &OSAtomicStoreTag); if (TmpStore.empty()) { // If no nodes were generated, other checkers must generated sinks. But // since the builder state was restored, we set it manually to prevent // auto transition. // FIXME: there should be a better approach. - C.getNodeBuilder().BuildSinks = true; + Builder.BuildSinks = true; return true; } @@ -180,24 +201,24 @@ bool OSAtomicChecker::evalOSAtomicCompareAndSwap(CheckerContext &C, for (ExplodedNodeSet::iterator I2 = TmpStore.begin(), E2 = TmpStore.end(); I2 != E2; ++I2) { ExplodedNode *predNew = *I2; - const GRState *stateNew = predNew->getState(); + const ProgramState *stateNew = predNew->getState(); // Check for 'void' return type if we have a bogus function prototype. SVal Res = UnknownVal(); QualType T = CE->getType(); if (!T->isVoidType()) - Res = Engine.getSValBuilder().makeTruthVal(true, T); - C.generateNode(stateNew->BindExpr(CE, Res), predNew); + Res = Eng.getSValBuilder().makeTruthVal(true, T); + generateNode(stateNew->BindExpr(CE, Res), predNew, CE, Builder, Dst); } } // Were they not equal? - if (const GRState *stateNotEqual = stateLoad->assume(Cmp, false)) { + if (const ProgramState *stateNotEqual = stateLoad->assume(Cmp, false)) { // Check for 'void' return type if we have a bogus function prototype. SVal Res = UnknownVal(); QualType T = CE->getType(); if (!T->isVoidType()) - Res = Engine.getSValBuilder().makeTruthVal(false, CE->getType()); - C.generateNode(stateNotEqual->BindExpr(CE, Res), N); + Res = Eng.getSValBuilder().makeTruthVal(false, CE->getType()); + generateNode(stateNotEqual->BindExpr(CE, Res), N, CE, Builder, Dst); } } diff --git a/lib/StaticAnalyzer/Checkers/ObjCAtSyncChecker.cpp b/lib/StaticAnalyzer/Checkers/ObjCAtSyncChecker.cpp index a1180492a937..3e4e07b65057 100644 --- a/lib/StaticAnalyzer/Checkers/ObjCAtSyncChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/ObjCAtSyncChecker.cpp @@ -38,7 +38,7 @@ void ObjCAtSyncChecker::checkPreStmt(const ObjCAtSynchronizedStmt *S, CheckerContext &C) const { const Expr *Ex = S->getSynchExpr(); - const GRState *state = C.getState(); + const ProgramState *state = C.getState(); SVal V = state->getSVal(Ex); // Uninitialized value used for the mutex? @@ -47,9 +47,9 @@ void ObjCAtSyncChecker::checkPreStmt(const ObjCAtSynchronizedStmt *S, if (!BT_undef) 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); + BugReport *report = + new BugReport(*BT_undef, BT_undef->getDescription(), N); + report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, Ex)); C.EmitReport(report); } return; @@ -59,7 +59,7 @@ void ObjCAtSyncChecker::checkPreStmt(const ObjCAtSynchronizedStmt *S, return; // Check for null mutexes. - const GRState *notNullState, *nullState; + const ProgramState *notNullState, *nullState; llvm::tie(notNullState, nullState) = state->assume(cast<DefinedSVal>(V)); if (nullState) { @@ -70,10 +70,9 @@ void ObjCAtSyncChecker::checkPreStmt(const ObjCAtSynchronizedStmt *S, if (!BT_null) 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, - Ex); + BugReport *report = + new BugReport(*BT_null, BT_null->getDescription(), N); + report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, Ex)); C.EmitReport(report); return; diff --git a/lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp b/lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp index 4c05867631f3..2fb9944afaa7 100644 --- a/lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp @@ -50,7 +50,8 @@ #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/GRStateTrait.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/AST/ParentMap.h" @@ -76,7 +77,8 @@ public: 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; + void checkLocation(SVal location, bool isLoad, const Stmt *S, + CheckerContext &C) const; }; } // end anonymous namespace @@ -109,11 +111,11 @@ namespace { struct PreCallSelfFlags {}; } namespace clang { namespace ento { template<> - struct GRStateTrait<SelfFlag> : public GRStatePartialTrait<SelfFlag> { - static void* GDMIndex() { static int index = 0; return &index; } + struct ProgramStateTrait<SelfFlag> : public ProgramStatePartialTrait<SelfFlag> { + static void *GDMIndex() { static int index = 0; return &index; } }; template <> - struct GRStateTrait<CalledInit> : public GRStatePartialTrait<bool> { + struct ProgramStateTrait<CalledInit> : public ProgramStatePartialTrait<bool> { static void *GDMIndex() { static int index = 0; return &index; } }; @@ -122,13 +124,13 @@ namespace ento { /// 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> { + struct ProgramStateTrait<PreCallSelfFlags> : public ProgramStatePartialTrait<unsigned> { static void *GDMIndex() { static int index = 0; return &index; } }; } } -static SelfFlagEnum getSelfFlags(SVal val, const GRState *state) { +static SelfFlagEnum getSelfFlags(SVal val, const ProgramState *state) { if (SymbolRef sym = val.getAsSymbol()) if (const unsigned *attachedFlags = state->get<SelfFlag>(sym)) return (SelfFlagEnum)*attachedFlags; @@ -139,7 +141,7 @@ static SelfFlagEnum getSelfFlags(SVal val, CheckerContext &C) { return getSelfFlags(val, C.getState()); } -static void addSelfFlag(const GRState *state, SVal val, +static void addSelfFlag(const ProgramState *state, SVal val, SelfFlagEnum flag, CheckerContext &C) { // We tag the symbol that the SVal wraps. if (SymbolRef sym = val.getAsSymbol()) @@ -179,8 +181,8 @@ static void checkForInvalidSelf(const Expr *E, CheckerContext &C, if (!N) return; - EnhancedBugReport *report = - new EnhancedBugReport(*new InitSelfBug(), errorStr, N); + BugReport *report = + new BugReport(*new InitSelfBug(), errorStr, N); C.EmitReport(report); } @@ -197,7 +199,7 @@ void ObjCSelfInitChecker::checkPostObjCMessage(ObjCMessage msg, if (isInitMessage(msg)) { // Tag the return value as the result of an initializer. - const GRState *state = C.getState(); + const ProgramState *state = C.getState(); // FIXME this really should be context sensitive, where we record // the current stack frame (for IPA). Also, we need to clean this @@ -257,7 +259,7 @@ void ObjCSelfInitChecker::checkPreStmt(const ReturnStmt *S, void ObjCSelfInitChecker::checkPreStmt(const CallExpr *CE, CheckerContext &C) const { - const GRState *state = C.getState(); + const ProgramState *state = C.getState(); for (CallExpr::const_arg_iterator I = CE->arg_begin(), E = CE->arg_end(); I != E; ++I) { SVal argV = state->getSVal(*I); @@ -275,7 +277,7 @@ void ObjCSelfInitChecker::checkPreStmt(const CallExpr *CE, void ObjCSelfInitChecker::checkPostStmt(const CallExpr *CE, CheckerContext &C) const { - const GRState *state = C.getState(); + const ProgramState *state = C.getState(); for (CallExpr::const_arg_iterator I = CE->arg_begin(), E = CE->arg_end(); I != E; ++I) { SVal argV = state->getSVal(*I); @@ -294,10 +296,11 @@ void ObjCSelfInitChecker::checkPostStmt(const CallExpr *CE, } void ObjCSelfInitChecker::checkLocation(SVal location, bool isLoad, + const Stmt *S, 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(); + const ProgramState *state = C.getState(); if (isSelfVar(location, C)) addSelfFlag(state, state->getSVal(cast<Loc>(location)), SelfFlag_Self, C); } @@ -315,9 +318,9 @@ static bool shouldRunOnFunctionOrMethod(const NamedDecl *ND) { // self = [super init] applies only to NSObject subclasses. // For instance, NSProxy doesn't implement -init. - ASTContext& Ctx = MD->getASTContext(); + ASTContext &Ctx = MD->getASTContext(); IdentifierInfo* NSObjectII = &Ctx.Idents.get("NSObject"); - ObjCInterfaceDecl* ID = MD->getClassInterface()->getSuperClass(); + ObjCInterfaceDecl *ID = MD->getClassInterface()->getSuperClass(); for ( ; ID ; ID = ID->getSuperClass()) { IdentifierInfo *II = ID->getIdentifier(); diff --git a/lib/StaticAnalyzer/Checkers/ObjCUnusedIVarsChecker.cpp b/lib/StaticAnalyzer/Checkers/ObjCUnusedIVarsChecker.cpp index d78e5ceb533d..bbc262fe8ef7 100644 --- a/lib/StaticAnalyzer/Checkers/ObjCUnusedIVarsChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/ObjCUnusedIVarsChecker.cpp @@ -29,7 +29,7 @@ using namespace ento; enum IVarState { Unused, Used }; typedef llvm::DenseMap<const ObjCIvarDecl*,IVarState> IvarUsageMap; -static void Scan(IvarUsageMap& M, const Stmt* S) { +static void Scan(IvarUsageMap& M, const Stmt *S) { if (!S) return; @@ -51,11 +51,11 @@ static void Scan(IvarUsageMap& M, const Stmt* S) { Scan(M, *I); } -static void Scan(IvarUsageMap& M, const ObjCPropertyImplDecl* D) { +static void Scan(IvarUsageMap& M, const ObjCPropertyImplDecl *D) { if (!D) return; - const ObjCIvarDecl* ID = D->getPropertyIvarDecl(); + const ObjCIvarDecl *ID = D->getPropertyIvarDecl(); if (!ID) return; @@ -65,7 +65,7 @@ static void Scan(IvarUsageMap& M, const ObjCPropertyImplDecl* D) { I->second = Used; } -static void Scan(IvarUsageMap& M, const ObjCContainerDecl* D) { +static void Scan(IvarUsageMap& M, const ObjCContainerDecl *D) { // Scan the methods for accesses. for (ObjCContainerDecl::instmeth_iterator I = D->instmeth_begin(), E = D->instmeth_end(); I!=E; ++I) @@ -102,14 +102,14 @@ static void Scan(IvarUsageMap &M, const DeclContext *C, const FileID FID, static void checkObjCUnusedIvar(const ObjCImplementationDecl *D, BugReporter &BR) { - const ObjCInterfaceDecl* ID = D->getClassInterface(); + const ObjCInterfaceDecl *ID = D->getClassInterface(); IvarUsageMap M; // Iterate over the ivars. for (ObjCInterfaceDecl::ivar_iterator I=ID->ivar_begin(), E=ID->ivar_end(); I!=E; ++I) { - const ObjCIvarDecl* ID = *I; + const ObjCIvarDecl *ID = *I; // Ignore ivars that... // (a) aren't private @@ -155,12 +155,14 @@ static void checkObjCUnusedIvar(const ObjCImplementationDecl *D, if (I->second == Unused) { std::string sbuf; llvm::raw_string_ostream os(sbuf); - os << "Instance variable '" << I->first << "' in class '" << ID + os << "Instance variable '" << *I->first << "' in class '" << *ID << "' is never used by the methods in its @implementation " "(although it may be used by category methods)."; + PathDiagnosticLocation L = + PathDiagnosticLocation::create(I->first, BR.getSourceManager()); BR.EmitBasicReport("Unused instance variable", "Optimization", - os.str(), I->first->getLocation()); + os.str(), L); } } diff --git a/lib/StaticAnalyzer/Checkers/PointerArithChecker.cpp b/lib/StaticAnalyzer/Checkers/PointerArithChecker.cpp index 7c21acc5bee7..202522b1949c 100644 --- a/lib/StaticAnalyzer/Checkers/PointerArithChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/PointerArithChecker.cpp @@ -36,7 +36,7 @@ void PointerArithChecker::checkPreStmt(const BinaryOperator *B, if (B->getOpcode() != BO_Sub && B->getOpcode() != BO_Add) return; - const GRState *state = C.getState(); + const ProgramState *state = C.getState(); SVal LV = state->getSVal(B->getLHS()); SVal RV = state->getSVal(B->getRHS()); @@ -56,7 +56,7 @@ void PointerArithChecker::checkPreStmt(const BinaryOperator *B, "Pointer arithmetic done on non-array variables " "means reliance on memory layout, which is " "dangerous.")); - RangedBugReport *R = new RangedBugReport(*BT, BT->getDescription(), N); + BugReport *R = new BugReport(*BT, BT->getDescription(), N); R->addRange(B->getSourceRange()); C.EmitReport(R); } diff --git a/lib/StaticAnalyzer/Checkers/PointerSubChecker.cpp b/lib/StaticAnalyzer/Checkers/PointerSubChecker.cpp index 16ede2095eae..924c7f2ad3ea 100644 --- a/lib/StaticAnalyzer/Checkers/PointerSubChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/PointerSubChecker.cpp @@ -39,7 +39,7 @@ void PointerSubChecker::checkPreStmt(const BinaryOperator *B, if (B->getOpcode() != BO_Sub) return; - const GRState *state = C.getState(); + const ProgramState *state = C.getState(); SVal LV = state->getSVal(B->getLHS()); SVal RV = state->getSVal(B->getRHS()); @@ -64,7 +64,7 @@ void PointerSubChecker::checkPreStmt(const BinaryOperator *B, BT.reset(new BuiltinBug("Pointer subtraction", "Subtraction of two pointers that do not point to " "the same memory chunk may cause incorrect result.")); - RangedBugReport *R = new RangedBugReport(*BT, BT->getDescription(), N); + BugReport *R = new BugReport(*BT, BT->getDescription(), N); R->addRange(B->getSourceRange()); C.EmitReport(R); } diff --git a/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp b/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp index 74199bb1f6db..c02b5b14ca64 100644 --- a/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp @@ -1,4 +1,4 @@ -//===--- PthreadLockChecker.h - Undefined arguments checker ----*- C++ -*--===// +//===--- PthreadLockChecker.cpp - Check for locking problems ---*- C++ -*--===// // // The LLVM Compiler Infrastructure // @@ -7,8 +7,8 @@ // //===----------------------------------------------------------------------===// // -// This defines PthreadLockChecker, a simple lock -> unlock checker. Eventually -// this shouldn't be registered with ExprEngineInternalChecks. +// This defines PthreadLockChecker, a simple lock -> unlock checker. +// Also handles XNU locks, which behave similarly enough to share code. // //===----------------------------------------------------------------------===// @@ -16,25 +16,29 @@ #include "clang/StaticAnalyzer/Core/Checker.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/GRStateTrait.h" -#include "llvm/ADT/ImmutableSet.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" +#include "llvm/ADT/ImmutableList.h" using namespace clang; using namespace ento; namespace { -class PthreadLockChecker - : public Checker< check::PostStmt<CallExpr> > { +class PthreadLockChecker : public Checker< check::PostStmt<CallExpr> > { + mutable llvm::OwningPtr<BugType> BT_doublelock; + mutable llvm::OwningPtr<BugType> BT_lor; + enum LockingSemantics { + NotApplicable = 0, + PthreadSemantics, + XNUSemantics + }; public: void checkPostStmt(const CallExpr *CE, CheckerContext &C) const; - void AcquireLock(CheckerContext &C, const CallExpr *CE, - SVal lock, bool isTryLock) const; + void AcquireLock(CheckerContext &C, const CallExpr *CE, SVal lock, + bool isTryLock, enum LockingSemantics semantics) const; - void ReleaseLock(CheckerContext &C, const CallExpr *CE, - SVal lock) const; - + void ReleaseLock(CheckerContext &C, const CallExpr *CE, SVal lock) const; }; } // end anonymous namespace @@ -42,80 +46,117 @@ public: namespace { class LockSet {}; } namespace clang { namespace ento { -template <> struct GRStateTrait<LockSet> : - public GRStatePartialTrait<llvm::ImmutableSet<const MemRegion*> > { - static void* GDMIndex() { static int x = 0; return &x; } +template <> struct ProgramStateTrait<LockSet> : + public ProgramStatePartialTrait<llvm::ImmutableList<const MemRegion*> > { + static void *GDMIndex() { static int x = 0; return &x; } }; -} // end GR namespace +} // end of ento (ProgramState) namespace } // end clang namespace void PthreadLockChecker::checkPostStmt(const CallExpr *CE, CheckerContext &C) const { - const GRState *state = C.getState(); + const ProgramState *state = C.getState(); const Expr *Callee = CE->getCallee(); - const FunctionTextRegion *R = - dyn_cast_or_null<FunctionTextRegion>(state->getSVal(Callee).getAsRegion()); - - if (!R) + const FunctionDecl *FD = state->getSVal(Callee).getAsFunctionDecl(); + + if (!FD) return; - - IdentifierInfo *II = R->getDecl()->getIdentifier(); + + // Get the name of the callee. + IdentifierInfo *II = FD->getIdentifier(); if (!II) // if no identifier, not a simple C function return; - llvm::StringRef FName = II->getName(); - - if (FName == "pthread_mutex_lock") { - if (CE->getNumArgs() != 1) - return; - AcquireLock(C, CE, state->getSVal(CE->getArg(0)), false); - } - else if (FName == "pthread_mutex_trylock") { - if (CE->getNumArgs() != 1) - return; - AcquireLock(C, CE, state->getSVal(CE->getArg(0)), true); - } - else if (FName == "pthread_mutex_unlock") { - if (CE->getNumArgs() != 1) - return; + StringRef FName = II->getName(); + + if (CE->getNumArgs() != 1) + return; + + if (FName == "pthread_mutex_lock" || + FName == "pthread_rwlock_rdlock" || + FName == "pthread_rwlock_wrlock") + AcquireLock(C, CE, state->getSVal(CE->getArg(0)), false, PthreadSemantics); + else if (FName == "lck_mtx_lock" || + FName == "lck_rw_lock_exclusive" || + FName == "lck_rw_lock_shared") + AcquireLock(C, CE, state->getSVal(CE->getArg(0)), false, XNUSemantics); + else if (FName == "pthread_mutex_trylock" || + FName == "pthread_rwlock_tryrdlock" || + FName == "pthread_rwlock_tryrwlock") + AcquireLock(C, CE, state->getSVal(CE->getArg(0)), true, PthreadSemantics); + else if (FName == "lck_mtx_try_lock" || + FName == "lck_rw_try_lock_exclusive" || + FName == "lck_rw_try_lock_shared") + AcquireLock(C, CE, state->getSVal(CE->getArg(0)), true, XNUSemantics); + else if (FName == "pthread_mutex_unlock" || + FName == "pthread_rwlock_unlock" || + FName == "lck_mtx_unlock" || + FName == "lck_rw_done") ReleaseLock(C, CE, state->getSVal(CE->getArg(0))); - } } void PthreadLockChecker::AcquireLock(CheckerContext &C, const CallExpr *CE, - SVal lock, bool isTryLock) const { + SVal lock, bool isTryLock, + enum LockingSemantics semantics) const { const MemRegion *lockR = lock.getAsRegion(); if (!lockR) return; - const GRState *state = C.getState(); + const ProgramState *state = C.getState(); SVal X = state->getSVal(CE); if (X.isUnknownOrUndef()) return; DefinedSVal retVal = cast<DefinedSVal>(X); - const GRState *lockSucc = state; - + + if (state->contains<LockSet>(lockR)) { + if (!BT_doublelock) + BT_doublelock.reset(new BugType("Double locking", "Lock checker")); + ExplodedNode *N = C.generateSink(); + if (!N) + return; + BugReport *report = new BugReport(*BT_doublelock, + "This lock has already " + "been acquired", N); + report->addRange(CE->getArg(0)->getSourceRange()); + C.EmitReport(report); + return; + } + + const ProgramState *lockSucc = state; if (isTryLock) { - // Bifurcate the state, and allow a mode where the lock acquisition fails. - const GRState *lockFail; - llvm::tie(lockFail, lockSucc) = state->assume(retVal); + // Bifurcate the state, and allow a mode where the lock acquisition fails. + const ProgramState *lockFail; + switch (semantics) { + case PthreadSemantics: + llvm::tie(lockFail, lockSucc) = state->assume(retVal); + break; + case XNUSemantics: + llvm::tie(lockSucc, lockFail) = state->assume(retVal); + break; + default: + llvm_unreachable("Unknown tryLock locking semantics"); + break; + } assert(lockFail && lockSucc); - C.addTransition(C.generateNode(CE, lockFail)); - } - else { - // Assume that the return value was 0. + C.addTransition(lockFail); + + } else if (semantics == PthreadSemantics) { + // Assume that the return value was 0. lockSucc = state->assume(retVal, false); assert(lockSucc); + + } else { + // XNU locking semantics return void on non-try locks + assert((semantics == XNUSemantics) && "Unknown locking semantics"); + lockSucc = state; } - // Record that the lock was acquired. + // Record that the lock was acquired. lockSucc = lockSucc->add<LockSet>(lockR); - - C.addTransition(lockSucc != state ? C.generateNode(CE, lockSucc) : - C.getPredecessor()); + C.addTransition(lockSucc); } void PthreadLockChecker::ReleaseLock(CheckerContext &C, const CallExpr *CE, @@ -125,19 +166,37 @@ void PthreadLockChecker::ReleaseLock(CheckerContext &C, const CallExpr *CE, if (!lockR) return; - const GRState *state = C.getState(); + const ProgramState *state = C.getState(); + llvm::ImmutableList<const MemRegion*> LS = state->get<LockSet>(); - // Record that the lock was released. - // FIXME: Handle unlocking locks that were never acquired. This may - // require IPA for wrappers. - const GRState *unlockState = state->remove<LockSet>(lockR); - - if (state == unlockState) + // FIXME: Better analysis requires IPA for wrappers. + // FIXME: check for double unlocks + if (LS.isEmpty()) return; - C.addTransition(C.generateNode(CE, unlockState)); + const MemRegion *firstLockR = LS.getHead(); + if (firstLockR != lockR) { + if (!BT_lor) + BT_lor.reset(new BugType("Lock order reversal", "Lock checker")); + ExplodedNode *N = C.generateSink(); + if (!N) + return; + BugReport *report = new BugReport(*BT_lor, + "This was not the most " + "recently acquired lock. " + "Possible lock order " + "reversal", N); + report->addRange(CE->getArg(0)->getSourceRange()); + C.EmitReport(report); + return; + } + + // Record that the lock was released. + state = state->set<LockSet>(LS.getTail()); + C.addTransition(state); } + void ento::registerPthreadLockChecker(CheckerManager &mgr) { mgr.registerChecker<PthreadLockChecker>(); } diff --git a/lib/StaticAnalyzer/Core/CFRefCount.cpp b/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp index bf5302920819..93e0fe5b4f96 100644 --- a/lib/StaticAnalyzer/Core/CFRefCount.cpp +++ b/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp @@ -1,4 +1,4 @@ -// CFRefCount.cpp - Transfer functions for tracking simple values -*- C++ -*--// +//==-- RetainCountChecker.cpp - Checks for leaks and other issues -*- C++ -*--// // // The LLVM Compiler Infrastructure // @@ -7,111 +7,56 @@ // //===----------------------------------------------------------------------===// // -// This file defines the methods for CFRefCount, which implements -// a reference count checker for Core Foundation (Mac OS X). +// This file defines the methods for RetainCountChecker, which implements +// a reference count checker for Core Foundation and Cocoa on (Mac OS X). // //===----------------------------------------------------------------------===// -#include "clang/StaticAnalyzer/Core/Checker.h" -#include "clang/StaticAnalyzer/Core/CheckerManager.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "ClangSACheckers.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/DeclCXX.h" -#include "clang/AST/StmtVisitor.h" #include "clang/Basic/LangOptions.h" #include "clang/Basic/SourceManager.h" +#include "clang/Analysis/DomainSpecific/CocoaConventions.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" -#include "clang/StaticAnalyzer/Checkers/LocalCheckers.h" -#include "clang/Analysis/DomainSpecific/CocoaConventions.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngineBuilders.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/GRStateTrait.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/TransferFuncs.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/FoldingSet.h" #include "llvm/ADT/ImmutableList.h" #include "llvm/ADT/ImmutableMap.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringExtras.h" -#include <stdarg.h> +#include <cstdarg> using namespace clang; using namespace ento; -using llvm::StringRef; using llvm::StrInStrNoCase; namespace { -class InstanceReceiver { - ObjCMessage Msg; - const LocationContext *LC; -public: - InstanceReceiver() : LC(0) { } - InstanceReceiver(const ObjCMessage &msg, - const LocationContext *lc = 0) : Msg(msg), LC(lc) {} - - bool isValid() const { - return Msg.isValid() && Msg.isInstanceMessage(); - } - operator bool() const { - return isValid(); - } - - SVal getSValAsScalarOrLoc(const GRState *state) { - assert(isValid()); - // We have an expression for the receiver? Fetch the value - // of that expression. - if (const Expr *Ex = Msg.getInstanceReceiver()) - return state->getSValAsScalarOrLoc(Ex); - - // Otherwise we are sending a message to super. In this case the - // object reference is the same as 'self'. - if (const ImplicitParamDecl *SelfDecl = LC->getSelfDecl()) - return state->getSVal(state->getRegion(SelfDecl, LC)); - - return UnknownVal(); - } - - SourceRange getSourceRange() const { - assert(isValid()); - if (const Expr *Ex = Msg.getInstanceReceiver()) - return Ex->getSourceRange(); - - // Otherwise we are sending a message to super. - SourceLocation L = Msg.getSuperLoc(); - assert(L.isValid()); - return SourceRange(L, L); - } -}; -} - -static const ObjCMethodDecl* -ResolveToInterfaceMethodDecl(const ObjCMethodDecl *MD) { - const ObjCInterfaceDecl *ID = MD->getClassInterface(); - - return MD->isInstanceMethod() - ? ID->lookupInstanceMethod(MD->getSelector()) - : ID->lookupClassMethod(MD->getSelector()); -} - -namespace { +/// Wrapper around different kinds of node builder, so that helper functions +/// can have a common interface. class GenericNodeBuilderRefCount { - StmtNodeBuilder *SNB; - const Stmt *S; - const void *tag; + CheckerContext *C; + const ProgramPointTag *tag; EndOfFunctionNodeBuilder *ENB; public: - GenericNodeBuilderRefCount(StmtNodeBuilder &snb, const Stmt *s, - const void *t) - : SNB(&snb), S(s), tag(t), ENB(0) {} + GenericNodeBuilderRefCount(CheckerContext &c, + const ProgramPointTag *t) + : C(&c), tag(t), ENB(0) {} GenericNodeBuilderRefCount(EndOfFunctionNodeBuilder &enb) - : SNB(0), S(0), tag(0), ENB(&enb) {} + : C(0), tag(0), ENB(&enb) {} - ExplodedNode *MakeNode(const GRState *state, ExplodedNode *Pred) { - if (SNB) - return SNB->generateNode(PostStmt(S, Pred->getLocationContext(), tag), - state, Pred); + ExplodedNode *MakeNode(const ProgramState *state, ExplodedNode *Pred) { + if (C) + return C->generateNode(state, Pred, tag, false); assert(ENB); return ENB->generateNode(state, Pred); @@ -125,9 +70,9 @@ public: /// ArgEffect is used to summarize a function/method call's effect on a /// particular argument. -enum ArgEffect { Autorelease, Dealloc, DecRef, DecRefMsg, DoNothing, +enum ArgEffect { DoNothing, Autorelease, Dealloc, DecRef, DecRefMsg, DecRefBridgedTransfered, - DoNothingByRef, IncRefMsg, IncRef, MakeCollectable, MayEscape, + IncRefMsg, IncRef, MakeCollectable, MayEscape, NewAutoreleasePool, SelfOwn, StopTracking }; namespace llvm { @@ -148,9 +93,8 @@ namespace { /// respect to its return value. class RetEffect { public: - enum Kind { NoRet, Alias, OwnedSymbol, OwnedAllocatedSymbol, + enum Kind { NoRet, OwnedSymbol, OwnedAllocatedSymbol, NotOwnedSymbol, GCNotOwnedSymbol, ARCNotOwnedSymbol, - ReceiverAlias, OwnedWhenTrackedReceiver }; enum ObjKind { CF, ObjC, AnyObj }; @@ -158,36 +102,27 @@ public: private: Kind K; ObjKind O; - unsigned index; - RetEffect(Kind k, unsigned idx = 0) : K(k), O(AnyObj), index(idx) {} - RetEffect(Kind k, ObjKind o) : K(k), O(o), index(0) {} + RetEffect(Kind k, ObjKind o = AnyObj) : K(k), O(o) {} public: Kind getKind() const { return K; } ObjKind getObjKind() const { return O; } - unsigned getIndex() const { - assert(getKind() == Alias); - return index; - } - bool isOwned() const { return K == OwnedSymbol || K == OwnedAllocatedSymbol || K == OwnedWhenTrackedReceiver; } + bool operator==(const RetEffect &Other) const { + return K == Other.K && O == Other.O; + } + static RetEffect MakeOwnedWhenTrackedReceiver() { return RetEffect(OwnedWhenTrackedReceiver, ObjC); } - static RetEffect MakeAlias(unsigned Idx) { - return RetEffect(Alias, Idx); - } - static RetEffect MakeReceiverAlias() { - return RetEffect(ReceiverAlias); - } static RetEffect MakeOwned(ObjKind o, bool isAllocated = false) { return RetEffect(isAllocated ? OwnedAllocatedSymbol : OwnedSymbol, o); } @@ -314,15 +249,15 @@ public: ID.Add(T); } - void print(llvm::raw_ostream& Out) const; + void print(raw_ostream &Out) const; }; -void RefVal::print(llvm::raw_ostream& Out) const { +void RefVal::print(raw_ostream &Out) const { if (!T.isNull()) - Out << "Tracked Type:" << T.getAsString() << '\n'; + Out << "Tracked " << T.getAsString() << '/'; switch (getKind()) { - default: assert(false); + default: llvm_unreachable("Invalid RefVal kind"); case Owned: { Out << "Owned"; unsigned cnt = getCount(); @@ -406,18 +341,19 @@ typedef llvm::ImmutableMap<SymbolRef, RefVal> RefBindings; namespace clang { namespace ento { - template<> - struct GRStateTrait<RefBindings> : public GRStatePartialTrait<RefBindings> { - static void* GDMIndex() { - static int RefBIndex = 0; - return &RefBIndex; - } - }; +template<> +struct ProgramStateTrait<RefBindings> + : public ProgramStatePartialTrait<RefBindings> { + static void *GDMIndex() { + static int RefBIndex = 0; + return &RefBIndex; + } +}; } } //===----------------------------------------------------------------------===// -// Summaries +// Function/Method behavior summaries. //===----------------------------------------------------------------------===// namespace { @@ -436,19 +372,13 @@ class RetainSummary { ArgEffect Receiver; /// Ret - The effect on the return value. Used to indicate if the - /// function/method call returns a new tracked symbol, returns an - /// alias of one of the arguments in the call, and so on. + /// function/method call returns a new tracked symbol. RetEffect Ret; - /// EndPath - Indicates that execution of this method/function should - /// terminate the simulation of a path. - bool EndPath; - public: RetainSummary(ArgEffects A, RetEffect R, ArgEffect defaultEff, - ArgEffect ReceiverEff, bool endpath = false) - : Args(A), DefaultArgEffect(defaultEff), Receiver(ReceiverEff), Ret(R), - EndPath(endpath) {} + ArgEffect ReceiverEff) + : Args(A), DefaultArgEffect(defaultEff), Receiver(ReceiverEff), Ret(R) {} /// getArg - Return the argument effect on the argument specified by /// idx (starting from 0). @@ -474,10 +404,6 @@ public: /// setRetEffect - Set the effect of the return value of the call. void setRetEffect(RetEffect E) { Ret = E; } - /// isEndPath - Returns true if executing the given method/function should - /// terminate the path. - bool isEndPath() const { return EndPath; } - /// Sets the effect on the receiver of the message. void setReceiverEffect(ArgEffect e) { Receiver = e; } @@ -485,6 +411,14 @@ public: /// getReceiverEffect - Returns the effect on the receiver of the call. /// This is only meaningful if the summary applies to an ObjCMessageExpr*. ArgEffect getReceiverEffect() const { return Receiver; } + + /// Test if two retain summaries are identical. Note that merely equivalent + /// summaries are not necessarily identical (for example, if an explicit + /// argument effect matches the default effect). + bool operator==(const RetainSummary &Other) const { + return Args == Other.Args && DefaultArgEffect == Other.DefaultArgEffect && + Receiver == Other.Receiver && Ret == Other.Ret; + } }; } // end anonymous namespace @@ -500,10 +434,10 @@ public: ObjCSummaryKey(IdentifierInfo* ii, Selector s) : II(ii), S(s) {} - ObjCSummaryKey(const ObjCInterfaceDecl* d, Selector s) + ObjCSummaryKey(const ObjCInterfaceDecl *d, Selector s) : II(d ? d->getIdentifier() : 0), S(s) {} - ObjCSummaryKey(const ObjCInterfaceDecl* d, IdentifierInfo *ii, Selector s) + ObjCSummaryKey(const ObjCInterfaceDecl *d, IdentifierInfo *ii, Selector s) : II(d ? d->getIdentifier() : ii), S(s) {} ObjCSummaryKey(Selector s) @@ -547,19 +481,19 @@ struct isPodLike<ObjCSummaryKey> { static const bool value = true; }; namespace { class ObjCSummaryCache { - typedef llvm::DenseMap<ObjCSummaryKey, RetainSummary*> MapTy; + typedef llvm::DenseMap<ObjCSummaryKey, const RetainSummary *> MapTy; MapTy M; public: ObjCSummaryCache() {} - RetainSummary* find(const ObjCInterfaceDecl* D, IdentifierInfo *ClsName, + const RetainSummary * find(const ObjCInterfaceDecl *D, IdentifierInfo *ClsName, Selector S) { // Lookup the method using the decl for the class @interface. If we // have no decl, lookup using the class name. return D ? find(D, S) : find(ClsName, S); } - RetainSummary* find(const ObjCInterfaceDecl* D, Selector S) { + const RetainSummary * find(const ObjCInterfaceDecl *D, Selector S) { // Do a lookup with the (D,S) pair. If we find a match return // the iterator. ObjCSummaryKey K(D, S); @@ -574,7 +508,7 @@ public: // generate initial summaries without having to worry about NSObject // being declared. // FIXME: We may change this at some point. - for (ObjCInterfaceDecl* C=D->getSuperClass() ;; C=C->getSuperClass()) { + for (ObjCInterfaceDecl *C=D->getSuperClass() ;; C=C->getSuperClass()) { if ((I = M.find(ObjCSummaryKey(C, S))) != M.end()) break; @@ -584,12 +518,12 @@ public: // Cache the summary with original key to make the next lookup faster // and return the iterator. - RetainSummary *Summ = I->second; + const RetainSummary *Summ = I->second; M[K] = Summ; return Summ; } - RetainSummary* find(IdentifierInfo* II, Selector S) { + const RetainSummary * find(IdentifierInfo* II, Selector S) { // FIXME: Class method lookup. Right now we dont' have a good way // of going between IdentifierInfo* and the class hierarchy. MapTy::iterator I = M.find(ObjCSummaryKey(II, S)); @@ -600,11 +534,11 @@ public: return I == M.end() ? NULL : I->second; } - RetainSummary*& operator[](ObjCSummaryKey K) { + const RetainSummary *& operator[](ObjCSummaryKey K) { return M[K]; } - RetainSummary*& operator[](Selector S) { + const RetainSummary *& operator[](Selector S) { return M[ ObjCSummaryKey(S) ]; } }; @@ -621,7 +555,7 @@ class RetainSummaryManager { // Typedefs. //==-----------------------------------------------------------------==// - typedef llvm::DenseMap<const FunctionDecl*, RetainSummary*> + typedef llvm::DenseMap<const FunctionDecl*, const RetainSummary *> FuncSummariesTy; typedef ObjCSummaryCache ObjCMethodSummariesTy; @@ -631,11 +565,7 @@ class RetainSummaryManager { //==-----------------------------------------------------------------==// /// Ctx - The ASTContext object for the analyzed ASTs. - ASTContext& Ctx; - - /// CFDictionaryCreateII - An IdentifierInfo* representing the indentifier - /// "CFDictionaryCreate". - IdentifierInfo* CFDictionaryCreateII; + ASTContext &Ctx; /// GCEnabled - Records whether or not the analyzed code runs in GC mode. const bool GCEnabled; @@ -672,7 +602,7 @@ class RetainSummaryManager { RetEffect ObjCInitRetE; RetainSummary DefaultSummary; - RetainSummary* StopSummary; + const RetainSummary *StopSummary; //==-----------------------------------------------------------------==// // Methods. @@ -687,30 +617,28 @@ class RetainSummaryManager { public: RetEffect getObjAllocRetEffect() const { return ObjCAllocRetE; } - RetainSummary *getDefaultSummary() { - RetainSummary *Summ = (RetainSummary*) BPAlloc.Allocate<RetainSummary>(); - return new (Summ) RetainSummary(DefaultSummary); + const RetainSummary *getDefaultSummary() { + return &DefaultSummary; } + + const RetainSummary * getUnarySummary(const FunctionType* FT, + UnaryFuncKind func); - RetainSummary* getUnarySummary(const FunctionType* FT, UnaryFuncKind func); - - RetainSummary* getCFSummaryCreateRule(const FunctionDecl* FD); - RetainSummary* getCFSummaryGetRule(const FunctionDecl* FD); - RetainSummary* getCFCreateGetRuleSummary(const FunctionDecl* FD, - StringRef FName); + const RetainSummary * getCFSummaryCreateRule(const FunctionDecl *FD); + const RetainSummary * getCFSummaryGetRule(const FunctionDecl *FD); + const RetainSummary * getCFCreateGetRuleSummary(const FunctionDecl *FD); - RetainSummary* getPersistentSummary(ArgEffects AE, RetEffect RetEff, - ArgEffect ReceiverEff = DoNothing, - ArgEffect DefaultEff = MayEscape, - bool isEndPath = false); + const RetainSummary * getPersistentSummary(ArgEffects AE, RetEffect RetEff, + ArgEffect ReceiverEff = DoNothing, + ArgEffect DefaultEff = MayEscape); - RetainSummary* getPersistentSummary(RetEffect RE, - ArgEffect ReceiverEff = DoNothing, - ArgEffect DefaultEff = MayEscape) { + const RetainSummary * getPersistentSummary(RetEffect RE, + ArgEffect ReceiverEff = DoNothing, + ArgEffect DefaultEff = MayEscape) { return getPersistentSummary(getArgEffects(), RE, ReceiverEff, DefaultEff); } - RetainSummary *getPersistentStopSummary() { + const RetainSummary *getPersistentStopSummary() { if (StopSummary) return StopSummary; @@ -720,35 +648,35 @@ public: return StopSummary; } - RetainSummary *getInitMethodSummary(QualType RetTy); + const RetainSummary *getInitMethodSummary(QualType RetTy); void InitializeClassMethodSummaries(); void InitializeMethodSummaries(); private: - void addNSObjectClsMethSummary(Selector S, RetainSummary *Summ) { + void addNSObjectClsMethSummary(Selector S, const RetainSummary *Summ) { ObjCClassMethodSummaries[S] = Summ; } - void addNSObjectMethSummary(Selector S, RetainSummary *Summ) { + void addNSObjectMethSummary(Selector S, const RetainSummary *Summ) { ObjCMethodSummaries[S] = Summ; } void addClassMethSummary(const char* Cls, const char* nullaryName, - RetainSummary *Summ) { + const RetainSummary *Summ) { IdentifierInfo* ClsII = &Ctx.Idents.get(Cls); Selector S = GetNullarySelector(nullaryName, Ctx); ObjCClassMethodSummaries[ObjCSummaryKey(ClsII, S)] = Summ; } void addInstMethSummary(const char* Cls, const char* nullaryName, - RetainSummary *Summ) { + const RetainSummary *Summ) { IdentifierInfo* ClsII = &Ctx.Idents.get(Cls); Selector S = GetNullarySelector(nullaryName, Ctx); ObjCMethodSummaries[ObjCSummaryKey(ClsII, S)] = Summ; } Selector generateSelector(va_list argp) { - llvm::SmallVector<IdentifierInfo*, 10> II; + SmallVector<IdentifierInfo*, 10> II; while (const char* s = va_arg(argp, const char*)) II.push_back(&Ctx.Idents.get(s)); @@ -757,47 +685,36 @@ private: } void addMethodSummary(IdentifierInfo *ClsII, ObjCMethodSummariesTy& Summaries, - RetainSummary* Summ, va_list argp) { + const RetainSummary * Summ, va_list argp) { Selector S = generateSelector(argp); Summaries[ObjCSummaryKey(ClsII, S)] = Summ; } - void addInstMethSummary(const char* Cls, RetainSummary* Summ, ...) { + void addInstMethSummary(const char* Cls, const RetainSummary * Summ, ...) { va_list argp; va_start(argp, Summ); addMethodSummary(&Ctx.Idents.get(Cls), ObjCMethodSummaries, Summ, argp); va_end(argp); } - void addClsMethSummary(const char* Cls, RetainSummary* Summ, ...) { + void addClsMethSummary(const char* Cls, const RetainSummary * Summ, ...) { va_list argp; va_start(argp, Summ); addMethodSummary(&Ctx.Idents.get(Cls),ObjCClassMethodSummaries, Summ, argp); va_end(argp); } - void addClsMethSummary(IdentifierInfo *II, RetainSummary* Summ, ...) { + void addClsMethSummary(IdentifierInfo *II, const RetainSummary * Summ, ...) { va_list argp; va_start(argp, Summ); addMethodSummary(II, ObjCClassMethodSummaries, Summ, argp); va_end(argp); } - void addPanicSummary(const char* Cls, ...) { - RetainSummary* Summ = getPersistentSummary(AF.getEmptyMap(), - RetEffect::MakeNoRet(), - DoNothing, DoNothing, true); - va_list argp; - va_start (argp, Cls); - addMethodSummary(&Ctx.Idents.get(Cls), ObjCMethodSummaries, Summ, argp); - va_end(argp); - } - public: - RetainSummaryManager(ASTContext& ctx, bool gcenabled, bool usesARC) + RetainSummaryManager(ASTContext &ctx, bool gcenabled, bool usesARC) : Ctx(ctx), - CFDictionaryCreateII(&ctx.Idents.get("CFDictionaryCreate")), GCEnabled(gcenabled), ARCEnabled(usesARC), AF(BPAlloc), ScratchArgs(AF.getEmptyMap()), @@ -819,31 +736,31 @@ public: InitializeMethodSummaries(); } - ~RetainSummaryManager(); - - RetainSummary* getSummary(const FunctionDecl* FD); + const RetainSummary * getSummary(const FunctionDecl *FD); - RetainSummary *getInstanceMethodSummary(const ObjCMessage &msg, - const GRState *state, - const LocationContext *LC); + const RetainSummary *getInstanceMethodSummary(const ObjCMessage &msg, + const ProgramState *state, + const LocationContext *LC); - RetainSummary* getInstanceMethodSummary(const ObjCMessage &msg, - const ObjCInterfaceDecl* ID) { + const RetainSummary * getInstanceMethodSummary(const ObjCMessage &msg, + const ObjCInterfaceDecl *ID) { return getInstanceMethodSummary(msg.getSelector(), 0, ID, msg.getMethodDecl(), msg.getType(Ctx)); } - RetainSummary* getInstanceMethodSummary(Selector S, IdentifierInfo *ClsName, - const ObjCInterfaceDecl* ID, - const ObjCMethodDecl *MD, - QualType RetTy); + const RetainSummary * getInstanceMethodSummary(Selector S, + IdentifierInfo *ClsName, + const ObjCInterfaceDecl *ID, + const ObjCMethodDecl *MD, + QualType RetTy); - RetainSummary *getClassMethodSummary(Selector S, IdentifierInfo *ClsName, - const ObjCInterfaceDecl *ID, - const ObjCMethodDecl *MD, - QualType RetTy); + const RetainSummary *getClassMethodSummary(Selector S, + IdentifierInfo *ClsName, + const ObjCInterfaceDecl *ID, + const ObjCMethodDecl *MD, + QualType RetTy); - RetainSummary *getClassMethodSummary(const ObjCMessage &msg) { + const RetainSummary *getClassMethodSummary(const ObjCMessage &msg) { const ObjCInterfaceDecl *Class = 0; if (!msg.isInstanceMessage()) Class = msg.getReceiverInterface(); @@ -856,30 +773,26 @@ public: /// getMethodSummary - This version of getMethodSummary is used to query /// the summary for the current method being analyzed. - RetainSummary *getMethodSummary(const ObjCMethodDecl *MD) { + const RetainSummary *getMethodSummary(const ObjCMethodDecl *MD) { // FIXME: Eventually this should be unneeded. const ObjCInterfaceDecl *ID = MD->getClassInterface(); Selector S = MD->getSelector(); IdentifierInfo *ClsName = ID->getIdentifier(); QualType ResultTy = MD->getResultType(); - // Resolve the method decl last. - if (const ObjCMethodDecl *InterfaceMD = ResolveToInterfaceMethodDecl(MD)) - MD = InterfaceMD; - if (MD->isInstanceMethod()) return getInstanceMethodSummary(S, ClsName, ID, MD, ResultTy); else return getClassMethodSummary(S, ClsName, ID, MD, ResultTy); } - RetainSummary* getCommonMethodSummary(const ObjCMethodDecl* MD, - Selector S, QualType RetTy); + const RetainSummary * getCommonMethodSummary(const ObjCMethodDecl *MD, + Selector S, QualType RetTy); - void updateSummaryFromAnnotations(RetainSummary &Summ, + void updateSummaryFromAnnotations(const RetainSummary *&Summ, const ObjCMethodDecl *MD); - void updateSummaryFromAnnotations(RetainSummary &Summ, + void updateSummaryFromAnnotations(const RetainSummary *&Summ, const FunctionDecl *FD); bool isGCEnabled() const { return GCEnabled; } @@ -888,35 +801,69 @@ public: bool isARCorGCEnabled() const { return GCEnabled || ARCEnabled; } - RetainSummary *copySummary(RetainSummary *OldSumm) { - RetainSummary *Summ = (RetainSummary*) BPAlloc.Allocate<RetainSummary>(); + const RetainSummary *copySummary(const RetainSummary *OldSumm) { + RetainSummary *Summ = (RetainSummary *) BPAlloc.Allocate<RetainSummary>(); new (Summ) RetainSummary(*OldSumm); return Summ; } }; +// Used to avoid allocating long-term (BPAlloc'd) memory for default retain +// summaries. If a function or method looks like it has a default summary, but +// it has annotations, the annotations are added to the stack-based template +// and then copied into managed memory. +class RetainSummaryTemplate { + RetainSummaryManager &Manager; + const RetainSummary *&RealSummary; + const RetainSummary *BaseSummary; + RetainSummary ScratchSummary; + bool Accessed; +public: + RetainSummaryTemplate(const RetainSummary *&real, const RetainSummary &base, + RetainSummaryManager &manager) + : Manager(manager), + RealSummary(real), + BaseSummary(&base), + ScratchSummary(base), + Accessed(false) {} + + ~RetainSummaryTemplate() { + if (Accessed) + RealSummary = Manager.copySummary(&ScratchSummary); + else if (!RealSummary) + RealSummary = BaseSummary; + } + + RetainSummary &operator*() { + Accessed = true; + return ScratchSummary; + } + + RetainSummary *operator->() { + Accessed = true; + return &ScratchSummary; + } +}; + } // end anonymous namespace //===----------------------------------------------------------------------===// // Implementation of checker data structures. //===----------------------------------------------------------------------===// -RetainSummaryManager::~RetainSummaryManager() {} - ArgEffects RetainSummaryManager::getArgEffects() { ArgEffects AE = ScratchArgs; ScratchArgs = AF.getEmptyMap(); return AE; } -RetainSummary* +const RetainSummary * RetainSummaryManager::getPersistentSummary(ArgEffects AE, RetEffect RetEff, ArgEffect ReceiverEff, - ArgEffect DefaultEff, - bool isEndPath) { + ArgEffect DefaultEff) { // Create the summary and return it. - RetainSummary *Summ = (RetainSummary*) BPAlloc.Allocate<RetainSummary>(); - new (Summ) RetainSummary(AE, RetEff, DefaultEff, ReceiverEff, isEndPath); + RetainSummary *Summ = (RetainSummary *) BPAlloc.Allocate<RetainSummary>(); + new (Summ) RetainSummary(AE, RetEff, DefaultEff, ReceiverEff); return Summ; } @@ -924,22 +871,28 @@ RetainSummaryManager::getPersistentSummary(ArgEffects AE, RetEffect RetEff, // Summary creation for functions (largely uses of Core Foundation). //===----------------------------------------------------------------------===// -static bool isRetain(const FunctionDecl* FD, StringRef FName) { +static bool isRetain(const FunctionDecl *FD, StringRef FName) { return FName.endswith("Retain"); } -static bool isRelease(const FunctionDecl* FD, StringRef FName) { +static bool isRelease(const FunctionDecl *FD, StringRef FName) { return FName.endswith("Release"); } -RetainSummary* RetainSummaryManager::getSummary(const FunctionDecl* FD) { +static bool isMakeCollectable(const FunctionDecl *FD, StringRef FName) { + // FIXME: Remove FunctionDecl parameter. + // FIXME: Is it really okay if MakeCollectable isn't a suffix? + return FName.find("MakeCollectable") != StringRef::npos; +} + +const RetainSummary * RetainSummaryManager::getSummary(const FunctionDecl *FD) { // Look up a summary in our cache of FunctionDecls -> Summaries. FuncSummariesTy::iterator I = FuncSummaries.find(FD); if (I != FuncSummaries.end()) return I->second; // No summary? Generate one. - RetainSummary *S = 0; + const RetainSummary *S = 0; do { // We generate "stop" summaries for implicitly defined functions. @@ -1054,10 +1007,10 @@ RetainSummary* RetainSummaryManager::getSummary(const FunctionDecl* FD) { if (cocoa::isRefType(RetTy, "CF", FName)) { if (isRetain(FD, FName)) S = getUnarySummary(FT, cfretain); - else if (FName.find("MakeCollectable") != StringRef::npos) + else if (isMakeCollectable(FD, FName)) S = getUnarySummary(FT, cfmakecollectable); else - S = getCFCreateGetRuleSummary(FD, FName); + S = getCFCreateGetRuleSummary(FD); break; } @@ -1067,7 +1020,7 @@ RetainSummary* RetainSummaryManager::getSummary(const FunctionDecl* FD) { if (isRetain(FD, FName)) S = getUnarySummary(FT, cfretain); else - S = getCFCreateGetRuleSummary(FD, FName); + S = getCFCreateGetRuleSummary(FD); break; } @@ -1076,7 +1029,7 @@ RetainSummary* RetainSummaryManager::getSummary(const FunctionDecl* FD) { if (cocoa::isRefType(RetTy, "DADisk") || cocoa::isRefType(RetTy, "DADissenter") || cocoa::isRefType(RetTy, "DASessionRef")) { - S = getCFCreateGetRuleSummary(FD, FName); + S = getCFCreateGetRuleSummary(FD); break; } @@ -1121,27 +1074,22 @@ RetainSummary* RetainSummaryManager::getSummary(const FunctionDecl* FD) { } while (0); - if (!S) - S = getDefaultSummary(); - // Annotations override defaults. - assert(S); - updateSummaryFromAnnotations(*S, FD); + updateSummaryFromAnnotations(S, FD); FuncSummaries[FD] = S; return S; } -RetainSummary* -RetainSummaryManager::getCFCreateGetRuleSummary(const FunctionDecl* FD, - StringRef FName) { - if (coreFoundation::followsCreateRule(FName)) +const RetainSummary * +RetainSummaryManager::getCFCreateGetRuleSummary(const FunctionDecl *FD) { + if (coreFoundation::followsCreateRule(FD)) return getCFSummaryCreateRule(FD); return getCFSummaryGetRule(FD); } -RetainSummary* +const RetainSummary * RetainSummaryManager::getUnarySummary(const FunctionType* FT, UnaryFuncKind func) { @@ -1153,44 +1101,27 @@ RetainSummaryManager::getUnarySummary(const FunctionType* FT, assert (ScratchArgs.isEmpty()); + ArgEffect Effect; switch (func) { - case cfretain: { - ScratchArgs = AF.add(ScratchArgs, 0, IncRef); - return getPersistentSummary(RetEffect::MakeAlias(0), - DoNothing, DoNothing); - } - - case cfrelease: { - ScratchArgs = AF.add(ScratchArgs, 0, DecRef); - return getPersistentSummary(RetEffect::MakeNoRet(), - DoNothing, DoNothing); - } - - case cfmakecollectable: { - ScratchArgs = AF.add(ScratchArgs, 0, MakeCollectable); - return getPersistentSummary(RetEffect::MakeAlias(0),DoNothing, DoNothing); - } - - default: - assert (false && "Not a supported unary function."); - return getDefaultSummary(); + case cfretain: Effect = IncRef; break; + case cfrelease: Effect = DecRef; break; + case cfmakecollectable: Effect = MakeCollectable; break; + default: llvm_unreachable("Not a supported unary function."); } + + ScratchArgs = AF.add(ScratchArgs, 0, Effect); + return getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, DoNothing); } -RetainSummary* -RetainSummaryManager::getCFSummaryCreateRule(const FunctionDecl* FD) { +const RetainSummary * +RetainSummaryManager::getCFSummaryCreateRule(const FunctionDecl *FD) { assert (ScratchArgs.isEmpty()); - if (FD->getIdentifier() == CFDictionaryCreateII) { - ScratchArgs = AF.add(ScratchArgs, 1, DoNothingByRef); - ScratchArgs = AF.add(ScratchArgs, 2, DoNothingByRef); - } - return getPersistentSummary(RetEffect::MakeOwned(RetEffect::CF, true)); } -RetainSummary* -RetainSummaryManager::getCFSummaryGetRule(const FunctionDecl* FD) { +const RetainSummary * +RetainSummaryManager::getCFSummaryGetRule(const FunctionDecl *FD) { assert (ScratchArgs.isEmpty()); return getPersistentSummary(RetEffect::MakeNotOwned(RetEffect::CF), DoNothing, DoNothing); @@ -1200,7 +1131,7 @@ RetainSummaryManager::getCFSummaryGetRule(const FunctionDecl* FD) { // Summary creation for Selectors. //===----------------------------------------------------------------------===// -RetainSummary* +const RetainSummary * RetainSummaryManager::getInitMethodSummary(QualType RetTy) { assert(ScratchArgs.isEmpty()); // 'init' methods conceptually return a newly allocated object and claim @@ -1213,22 +1144,24 @@ RetainSummaryManager::getInitMethodSummary(QualType RetTy) { } void -RetainSummaryManager::updateSummaryFromAnnotations(RetainSummary &Summ, +RetainSummaryManager::updateSummaryFromAnnotations(const RetainSummary *&Summ, const FunctionDecl *FD) { if (!FD) return; + RetainSummaryTemplate Template(Summ, DefaultSummary, *this); + // Effects on the parameters. unsigned parm_idx = 0; for (FunctionDecl::param_const_iterator pi = FD->param_begin(), pe = FD->param_end(); pi != pe; ++pi, ++parm_idx) { const ParmVarDecl *pd = *pi; if (pd->getAttr<NSConsumedAttr>()) { - if (!GCEnabled) - Summ.addArg(AF, parm_idx, DecRef); - } - else if(pd->getAttr<CFConsumedAttr>()) { - Summ.addArg(AF, parm_idx, DecRef); + if (!GCEnabled) { + Template->addArg(AF, parm_idx, DecRef); + } + } else if (pd->getAttr<CFConsumedAttr>()) { + Template->addArg(AF, parm_idx, DecRef); } } @@ -1237,83 +1170,84 @@ RetainSummaryManager::updateSummaryFromAnnotations(RetainSummary &Summ, // Determine if there is a special return effect for this method. if (cocoa::isCocoaObjectRef(RetTy)) { if (FD->getAttr<NSReturnsRetainedAttr>()) { - Summ.setRetEffect(ObjCAllocRetE); + Template->setRetEffect(ObjCAllocRetE); } else if (FD->getAttr<CFReturnsRetainedAttr>()) { - Summ.setRetEffect(RetEffect::MakeOwned(RetEffect::CF, true)); + Template->setRetEffect(RetEffect::MakeOwned(RetEffect::CF, true)); } else if (FD->getAttr<NSReturnsNotRetainedAttr>()) { - Summ.setRetEffect(RetEffect::MakeNotOwned(RetEffect::ObjC)); + Template->setRetEffect(RetEffect::MakeNotOwned(RetEffect::ObjC)); } else if (FD->getAttr<CFReturnsNotRetainedAttr>()) { - Summ.setRetEffect(RetEffect::MakeNotOwned(RetEffect::CF)); + Template->setRetEffect(RetEffect::MakeNotOwned(RetEffect::CF)); } - } - else if (RetTy->getAs<PointerType>()) { + } else if (RetTy->getAs<PointerType>()) { if (FD->getAttr<CFReturnsRetainedAttr>()) { - Summ.setRetEffect(RetEffect::MakeOwned(RetEffect::CF, true)); + Template->setRetEffect(RetEffect::MakeOwned(RetEffect::CF, true)); } else if (FD->getAttr<CFReturnsNotRetainedAttr>()) { - Summ.setRetEffect(RetEffect::MakeNotOwned(RetEffect::CF)); + Template->setRetEffect(RetEffect::MakeNotOwned(RetEffect::CF)); } } } void -RetainSummaryManager::updateSummaryFromAnnotations(RetainSummary &Summ, - const ObjCMethodDecl *MD) { +RetainSummaryManager::updateSummaryFromAnnotations(const RetainSummary *&Summ, + const ObjCMethodDecl *MD) { if (!MD) return; + RetainSummaryTemplate Template(Summ, DefaultSummary, *this); + bool isTrackedLoc = false; // Effects on the receiver. if (MD->getAttr<NSConsumesSelfAttr>()) { if (!GCEnabled) - Summ.setReceiverEffect(DecRefMsg); + Template->setReceiverEffect(DecRefMsg); } // Effects on the parameters. unsigned parm_idx = 0; - for (ObjCMethodDecl::param_iterator pi=MD->param_begin(), pe=MD->param_end(); + for (ObjCMethodDecl::param_const_iterator + pi=MD->param_begin(), pe=MD->param_end(); pi != pe; ++pi, ++parm_idx) { const ParmVarDecl *pd = *pi; if (pd->getAttr<NSConsumedAttr>()) { if (!GCEnabled) - Summ.addArg(AF, parm_idx, DecRef); + Template->addArg(AF, parm_idx, DecRef); } else if(pd->getAttr<CFConsumedAttr>()) { - Summ.addArg(AF, parm_idx, DecRef); + Template->addArg(AF, parm_idx, DecRef); } } // Determine if there is a special return effect for this method. if (cocoa::isCocoaObjectRef(MD->getResultType())) { if (MD->getAttr<NSReturnsRetainedAttr>()) { - Summ.setRetEffect(ObjCAllocRetE); + Template->setRetEffect(ObjCAllocRetE); return; } if (MD->getAttr<NSReturnsNotRetainedAttr>()) { - Summ.setRetEffect(RetEffect::MakeNotOwned(RetEffect::ObjC)); + Template->setRetEffect(RetEffect::MakeNotOwned(RetEffect::ObjC)); return; } isTrackedLoc = true; - } - - if (!isTrackedLoc) + } else { isTrackedLoc = MD->getResultType()->getAs<PointerType>() != NULL; + } if (isTrackedLoc) { if (MD->getAttr<CFReturnsRetainedAttr>()) - Summ.setRetEffect(RetEffect::MakeOwned(RetEffect::CF, true)); + Template->setRetEffect(RetEffect::MakeOwned(RetEffect::CF, true)); else if (MD->getAttr<CFReturnsNotRetainedAttr>()) - Summ.setRetEffect(RetEffect::MakeNotOwned(RetEffect::CF)); + Template->setRetEffect(RetEffect::MakeNotOwned(RetEffect::CF)); } } -RetainSummary* -RetainSummaryManager::getCommonMethodSummary(const ObjCMethodDecl* MD, +const RetainSummary * +RetainSummaryManager::getCommonMethodSummary(const ObjCMethodDecl *MD, Selector S, QualType RetTy) { if (MD) { @@ -1322,9 +1256,9 @@ RetainSummaryManager::getCommonMethodSummary(const ObjCMethodDecl* MD, // Delegates are a frequent form of false positives with the retain // count checker. unsigned i = 0; - for (ObjCMethodDecl::param_iterator I = MD->param_begin(), + for (ObjCMethodDecl::param_const_iterator I = MD->param_begin(), E = MD->param_end(); I != E; ++I, ++i) - if (ParmVarDecl *PD = *I) { + if (const ParmVarDecl *PD = *I) { QualType Ty = Ctx.getCanonicalType(PD->getType()); if (Ty.getLocalUnqualifiedType() == Ctx.VoidPtrTy) ScratchArgs = AF.add(ScratchArgs, i, StopTracking); @@ -1370,15 +1304,15 @@ RetainSummaryManager::getCommonMethodSummary(const ObjCMethodDecl* MD, return getPersistentSummary(RetEffect::MakeNoRet(), ReceiverEff, MayEscape); } -RetainSummary* +const RetainSummary * RetainSummaryManager::getInstanceMethodSummary(const ObjCMessage &msg, - const GRState *state, + const ProgramState *state, const LocationContext *LC) { // We need the type-information of the tracked receiver object // Retrieve it from the state. const Expr *Receiver = msg.getInstanceReceiver(); - const ObjCInterfaceDecl* ID = 0; + const ObjCInterfaceDecl *ID = 0; // FIXME: Is this really working as expected? There are cases where // we just use the 'ID' from the message expression. @@ -1410,19 +1344,18 @@ RetainSummaryManager::getInstanceMethodSummary(const ObjCMessage &msg, // FIXME: The receiver could be a reference to a class, meaning that // we should use the class method. - RetainSummary *Summ = getInstanceMethodSummary(msg, ID); - return Summ ? Summ : getDefaultSummary(); + return getInstanceMethodSummary(msg, ID); } -RetainSummary* +const RetainSummary * RetainSummaryManager::getInstanceMethodSummary(Selector S, IdentifierInfo *ClsName, - const ObjCInterfaceDecl* ID, + const ObjCInterfaceDecl *ID, const ObjCMethodDecl *MD, QualType RetTy) { // Look up a summary in our summary cache. - RetainSummary *Summ = ObjCMethodSummaries.find(ID, ClsName, S); + const RetainSummary *Summ = ObjCMethodSummaries.find(ID, ClsName, S); if (!Summ) { assert(ScratchArgs.isEmpty()); @@ -1434,7 +1367,7 @@ RetainSummaryManager::getInstanceMethodSummary(Selector S, Summ = getCommonMethodSummary(MD, S, RetTy); // Annotations override defaults. - updateSummaryFromAnnotations(*Summ, MD); + updateSummaryFromAnnotations(Summ, MD); // Memoize the summary. ObjCMethodSummaries[ObjCSummaryKey(ID, ClsName, S)] = Summ; @@ -1443,19 +1376,21 @@ RetainSummaryManager::getInstanceMethodSummary(Selector S, return Summ; } -RetainSummary* +const RetainSummary * RetainSummaryManager::getClassMethodSummary(Selector S, IdentifierInfo *ClsName, const ObjCInterfaceDecl *ID, const ObjCMethodDecl *MD, QualType RetTy) { assert(ClsName && "Class name must be specified."); - RetainSummary *Summ = ObjCClassMethodSummaries.find(ID, ClsName, S); + const RetainSummary *Summ = ObjCClassMethodSummaries.find(ID, ClsName, S); if (!Summ) { Summ = getCommonMethodSummary(MD, S, RetTy); + // Annotations override defaults. - updateSummaryFromAnnotations(*Summ, MD); + updateSummaryFromAnnotations(Summ, MD); + // Memoize the summary. ObjCClassMethodSummaries[ObjCSummaryKey(ID, ClsName, S)] = Summ; } @@ -1465,8 +1400,6 @@ RetainSummaryManager::getClassMethodSummary(Selector S, IdentifierInfo *ClsName, void RetainSummaryManager::InitializeClassMethodSummaries() { assert(ScratchArgs.isEmpty()); - RetainSummary* Summ = getPersistentSummary(ObjCAllocRetE); - // Create the [NSAssertionHandler currentHander] summary. addClassMethSummary("NSAssertionHandler", "currentHandler", getPersistentSummary(RetEffect::MakeNotOwned(RetEffect::ObjC))); @@ -1482,7 +1415,8 @@ void RetainSummaryManager::InitializeClassMethodSummaries() { // used for delegates that can release the object. When we have better // inter-procedural analysis we can potentially do something better. This // workaround is to remove false positives. - Summ = getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, StopTracking); + const RetainSummary *Summ = + getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, StopTracking); IdentifierInfo *NSObjectII = &Ctx.Idents.get("NSObject"); addClsMethSummary(NSObjectII, Summ, "performSelector", "withObject", "afterDelay", NULL); @@ -1506,7 +1440,7 @@ void RetainSummaryManager::InitializeMethodSummaries() { // Create the "init" selector. It just acts as a pass-through for the // receiver. - RetainSummary *InitSumm = getPersistentSummary(ObjCInitRetE, DecRefMsg); + const RetainSummary *InitSumm = getPersistentSummary(ObjCInitRetE, DecRefMsg); addNSObjectMethSummary(GetNullarySelector("init", Ctx), InitSumm); // awakeAfterUsingCoder: behaves basically like an 'init' method. It @@ -1515,35 +1449,34 @@ void RetainSummaryManager::InitializeMethodSummaries() { InitSumm); // The next methods are allocators. - RetainSummary *AllocSumm = getPersistentSummary(ObjCAllocRetE); - RetainSummary *CFAllocSumm = + const RetainSummary *AllocSumm = getPersistentSummary(ObjCAllocRetE); + const RetainSummary *CFAllocSumm = getPersistentSummary(RetEffect::MakeOwned(RetEffect::CF, true)); // Create the "retain" selector. - RetEffect E = RetEffect::MakeReceiverAlias(); - RetainSummary *Summ = getPersistentSummary(E, IncRefMsg); + RetEffect NoRet = RetEffect::MakeNoRet(); + const RetainSummary *Summ = getPersistentSummary(NoRet, IncRefMsg); addNSObjectMethSummary(GetNullarySelector("retain", Ctx), Summ); // Create the "release" selector. - Summ = getPersistentSummary(E, DecRefMsg); + Summ = getPersistentSummary(NoRet, DecRefMsg); addNSObjectMethSummary(GetNullarySelector("release", Ctx), Summ); // Create the "drain" selector. - Summ = getPersistentSummary(E, isGCEnabled() ? DoNothing : DecRef); + Summ = getPersistentSummary(NoRet, isGCEnabled() ? DoNothing : DecRef); addNSObjectMethSummary(GetNullarySelector("drain", Ctx), Summ); // Create the -dealloc summary. - Summ = getPersistentSummary(RetEffect::MakeNoRet(), Dealloc); + Summ = getPersistentSummary(NoRet, Dealloc); addNSObjectMethSummary(GetNullarySelector("dealloc", Ctx), Summ); // Create the "autorelease" selector. - Summ = getPersistentSummary(E, Autorelease); + Summ = getPersistentSummary(NoRet, Autorelease); addNSObjectMethSummary(GetNullarySelector("autorelease", Ctx), Summ); // Specially handle NSAutoreleasePool. addInstMethSummary("NSAutoreleasePool", "init", - getPersistentSummary(RetEffect::MakeReceiverAlias(), - NewAutoreleasePool)); + getPersistentSummary(NoRet, NewAutoreleasePool)); // For NSWindow, allocated objects are (initially) self-owned. // FIXME: For now we opt for false negatives with NSWindow, as these objects @@ -1551,7 +1484,7 @@ void RetainSummaryManager::InitializeMethodSummaries() { // Thus, we need to track an NSWindow's display status. // This is tracked in <rdar://problem/6062711>. // See also http://llvm.org/bugs/show_bug.cgi?id=3714. - RetainSummary *NoTrackYet = getPersistentSummary(RetEffect::MakeNoRet(), + const RetainSummary *NoTrackYet = getPersistentSummary(RetEffect::MakeNoRet(), StopTracking, StopTracking); @@ -1583,13 +1516,6 @@ void RetainSummaryManager::InitializeMethodSummaries() { // exit a method. addClassMethSummary("NSAutoreleasePool", "alloc", NoTrackYet); - // Create NSAssertionHandler summaries. - addPanicSummary("NSAssertionHandler", "handleFailureInFunction", "file", - "lineNumber", "description", NULL); - - addPanicSummary("NSAssertionHandler", "handleFailureInMethod", "object", - "file", "lineNumber", "description", NULL); - // Create summaries QCRenderer/QCView -createSnapShotImageOfType: addInstMethSummary("QCRenderer", AllocSumm, "createSnapshotImageOfType", NULL); @@ -1623,26 +1549,27 @@ namespace { class AutoreleaseStack {}; } namespace clang { namespace ento { -template<> struct GRStateTrait<AutoreleaseStack> - : public GRStatePartialTrait<ARStack> { - static inline void* GDMIndex() { return &AutoRBIndex; } +template<> struct ProgramStateTrait<AutoreleaseStack> + : public ProgramStatePartialTrait<ARStack> { + static inline void *GDMIndex() { return &AutoRBIndex; } }; -template<> struct GRStateTrait<AutoreleasePoolContents> - : public GRStatePartialTrait<ARPoolContents> { - static inline void* GDMIndex() { return &AutoRCIndex; } +template<> struct ProgramStateTrait<AutoreleasePoolContents> + : public ProgramStatePartialTrait<ARPoolContents> { + static inline void *GDMIndex() { return &AutoRCIndex; } }; } // end GR namespace } // end clang namespace -static SymbolRef GetCurrentAutoreleasePool(const GRState* state) { +static SymbolRef GetCurrentAutoreleasePool(const ProgramState *state) { ARStack stack = state->get<AutoreleaseStack>(); return stack.isEmpty() ? SymbolRef() : stack.getHead(); } -static const GRState * SendAutorelease(const GRState *state, - ARCounts::Factory &F, SymbolRef sym) { - +static const ProgramState * +SendAutorelease(const ProgramState *state, + ARCounts::Factory &F, + SymbolRef sym) { SymbolRef pool = GetCurrentAutoreleasePool(state); const ARCounts *cnts = state->get<AutoreleasePoolContents>(pool); ARCounts newCnts(0); @@ -1658,196 +1585,11 @@ static const GRState * SendAutorelease(const GRState *state, } //===----------------------------------------------------------------------===// -// Transfer functions. -//===----------------------------------------------------------------------===// - -namespace { - -class CFRefCount : public TransferFuncs { -public: - class BindingsPrinter : public GRState::Printer { - public: - virtual void Print(llvm::raw_ostream& Out, const GRState* state, - const char* nl, const char* sep); - }; - - typedef llvm::DenseMap<const ExplodedNode*, const RetainSummary*> - SummaryLogTy; - - RetainSummaryManager Summaries; - SummaryLogTy SummaryLog; - const LangOptions& LOpts; - ARCounts::Factory ARCountFactory; - - BugType *useAfterRelease, *releaseNotOwned; - BugType *deallocGC, *deallocNotOwned; - BugType *leakWithinFunction, *leakAtReturn; - BugType *overAutorelease; - BugType *returnNotOwnedForOwned; - BugReporter *BR; - - const GRState * Update(const GRState * state, SymbolRef sym, RefVal V, ArgEffect E, - RefVal::Kind& hasErr); - - void ProcessNonLeakError(ExplodedNodeSet& Dst, - StmtNodeBuilder& Builder, - const Expr* NodeExpr, SourceRange ErrorRange, - ExplodedNode* Pred, - const GRState* St, - RefVal::Kind hasErr, SymbolRef Sym); - - const GRState * HandleSymbolDeath(const GRState * state, SymbolRef sid, RefVal V, - llvm::SmallVectorImpl<SymbolRef> &Leaked); - - ExplodedNode* ProcessLeaks(const GRState * state, - llvm::SmallVectorImpl<SymbolRef> &Leaked, - GenericNodeBuilderRefCount &Builder, - ExprEngine &Eng, - ExplodedNode *Pred = 0); - -public: - CFRefCount(ASTContext& Ctx, bool gcenabled, const LangOptions& lopts) - : Summaries(Ctx, gcenabled, (bool)lopts.ObjCAutoRefCount), - LOpts(lopts), useAfterRelease(0), releaseNotOwned(0), - deallocGC(0), deallocNotOwned(0), - leakWithinFunction(0), leakAtReturn(0), overAutorelease(0), - returnNotOwnedForOwned(0), BR(0) {} - - virtual ~CFRefCount() {} - - void RegisterChecks(ExprEngine &Eng); - - virtual void RegisterPrinters(std::vector<GRState::Printer*>& Printers) { - Printers.push_back(new BindingsPrinter()); - } - - bool isGCEnabled() const { return Summaries.isGCEnabled(); } - bool isARCorGCEnabled() const { return Summaries.isARCorGCEnabled(); } - - const LangOptions& getLangOptions() const { return LOpts; } - - const RetainSummary *getSummaryOfNode(const ExplodedNode *N) const { - SummaryLogTy::const_iterator I = SummaryLog.find(N); - return I == SummaryLog.end() ? 0 : I->second; - } - - // Calls. - - void evalSummary(ExplodedNodeSet& Dst, - ExprEngine& Eng, - StmtNodeBuilder& Builder, - const Expr* Ex, - const CallOrObjCMessage &callOrMsg, - InstanceReceiver Receiver, - const RetainSummary& Summ, - const MemRegion *Callee, - ExplodedNode* Pred, const GRState *state); - - virtual void evalCall(ExplodedNodeSet& Dst, - ExprEngine& Eng, - StmtNodeBuilder& Builder, - const CallExpr* CE, SVal L, - ExplodedNode* Pred); - - - virtual void evalObjCMessage(ExplodedNodeSet& Dst, - ExprEngine& Engine, - StmtNodeBuilder& Builder, - ObjCMessage msg, - ExplodedNode* Pred, - const GRState *state); - // Stores. - virtual void evalBind(StmtNodeBuilderRef& B, SVal location, SVal val); - - // End-of-path. - - virtual void evalEndPath(ExprEngine& Engine, - EndOfFunctionNodeBuilder& Builder); - - virtual void evalDeadSymbols(ExplodedNodeSet& Dst, - ExprEngine& Engine, - StmtNodeBuilder& Builder, - ExplodedNode* Pred, - const GRState* state, - SymbolReaper& SymReaper); - - std::pair<ExplodedNode*, const GRState *> - HandleAutoreleaseCounts(const GRState * state, GenericNodeBuilderRefCount Bd, - ExplodedNode* Pred, ExprEngine &Eng, - SymbolRef Sym, RefVal V, bool &stop); - // Return statements. - - virtual void evalReturn(ExplodedNodeSet& Dst, - ExprEngine& Engine, - StmtNodeBuilder& Builder, - const ReturnStmt* S, - ExplodedNode* Pred); - - void evalReturnWithRetEffect(ExplodedNodeSet& Dst, - ExprEngine& Engine, - StmtNodeBuilder& Builder, - const ReturnStmt* S, - ExplodedNode* Pred, - RetEffect RE, RefVal X, - SymbolRef Sym, const GRState *state); - - - // Assumptions. - - virtual const GRState *evalAssume(const GRState* state, SVal condition, - bool assumption); -}; - -} // end anonymous namespace - -static void PrintPool(llvm::raw_ostream &Out, SymbolRef Sym, - const GRState *state) { - Out << ' '; - if (Sym) - Out << Sym->getSymbolID(); - else - Out << "<pool>"; - Out << ":{"; - - // Get the contents of the pool. - if (const ARCounts *cnts = state->get<AutoreleasePoolContents>(Sym)) - for (ARCounts::iterator J=cnts->begin(), EJ=cnts->end(); J != EJ; ++J) - Out << '(' << J.getKey() << ',' << J.getData() << ')'; - - Out << '}'; -} - -void CFRefCount::BindingsPrinter::Print(llvm::raw_ostream& Out, - const GRState* state, - const char* nl, const char* sep) { - - RefBindings B = state->get<RefBindings>(); - - if (!B.isEmpty()) - Out << sep << nl; - - for (RefBindings::iterator I=B.begin(), E=B.end(); I!=E; ++I) { - Out << (*I).first << " : "; - (*I).second.print(Out); - Out << nl; - } - - // Print the autorelease stack. - Out << sep << nl << "AR pool stack:"; - ARStack stack = state->get<AutoreleaseStack>(); - - PrintPool(Out, SymbolRef(), state); // Print the caller's pool. - for (ARStack::iterator I=stack.begin(), E=stack.end(); I!=E; ++I) - PrintPool(Out, *I, state); - - Out << nl; -} - -//===----------------------------------------------------------------------===// // Error reporting. //===----------------------------------------------------------------------===// - namespace { + typedef llvm::DenseMap<const ExplodedNode *, const RetainSummary *> + SummaryLogTy; //===-------------===// // Bug Descriptions. // @@ -1855,35 +1597,30 @@ namespace { class CFRefBug : public BugType { protected: - CFRefCount& TF; - - CFRefBug(CFRefCount* tf, llvm::StringRef name) - : BugType(name, "Memory (Core Foundation/Objective-C)"), TF(*tf) {} + CFRefBug(StringRef name) + : BugType(name, "Memory (Core Foundation/Objective-C)") {} public: - CFRefCount& getTF() { return TF; } - // FIXME: Eventually remove. - virtual const char* getDescription() const = 0; + virtual const char *getDescription() const = 0; virtual bool isLeak() const { return false; } }; class UseAfterRelease : public CFRefBug { public: - UseAfterRelease(CFRefCount* tf) - : CFRefBug(tf, "Use-after-release") {} + UseAfterRelease() : CFRefBug("Use-after-release") {} - const char* getDescription() const { + const char *getDescription() const { return "Reference-counted object is used after it is released"; } }; class BadRelease : public CFRefBug { public: - BadRelease(CFRefCount* tf) : CFRefBug(tf, "Bad release") {} + BadRelease() : CFRefBug("Bad release") {} - const char* getDescription() const { + const char *getDescription() const { return "Incorrect decrement of the reference count of an object that is " "not owned at this point by the caller"; } @@ -1891,8 +1628,8 @@ namespace { class DeallocGC : public CFRefBug { public: - DeallocGC(CFRefCount *tf) - : CFRefBug(tf, "-dealloc called while using garbage collection") {} + DeallocGC() + : CFRefBug("-dealloc called while using garbage collection") {} const char *getDescription() const { return "-dealloc called while using garbage collection"; @@ -1901,8 +1638,8 @@ namespace { class DeallocNotOwned : public CFRefBug { public: - DeallocNotOwned(CFRefCount *tf) - : CFRefBug(tf, "-dealloc sent to non-exclusively owned object") {} + DeallocNotOwned() + : CFRefBug("-dealloc sent to non-exclusively owned object") {} const char *getDescription() const { return "-dealloc sent to object that may be referenced elsewhere"; @@ -1911,8 +1648,8 @@ namespace { class OverAutorelease : public CFRefBug { public: - OverAutorelease(CFRefCount *tf) : - CFRefBug(tf, "Object sent -autorelease too many times") {} + OverAutorelease() + : CFRefBug("Object sent -autorelease too many times") {} const char *getDescription() const { return "Object sent -autorelease too many times"; @@ -1921,8 +1658,8 @@ namespace { class ReturnedNotOwnedForOwned : public CFRefBug { public: - ReturnedNotOwnedForOwned(CFRefCount *tf) : - CFRefBug(tf, "Method should return an owned object") {} + ReturnedNotOwnedForOwned() + : CFRefBug("Method should return an owned object") {} const char *getDescription() const { return "Object with a +0 retain count returned to caller where a +1 " @@ -1933,141 +1670,170 @@ namespace { class Leak : public CFRefBug { const bool isReturn; protected: - Leak(CFRefCount* tf, llvm::StringRef name, bool isRet) - : CFRefBug(tf, name), isReturn(isRet) {} + Leak(StringRef name, bool isRet) + : CFRefBug(name), isReturn(isRet) { + // Leaks should not be reported if they are post-dominated by a sink. + setSuppressOnSink(true); + } public: - const char* getDescription() const { return ""; } + const char *getDescription() const { return ""; } bool isLeak() const { return true; } }; class LeakAtReturn : public Leak { public: - LeakAtReturn(CFRefCount* tf, llvm::StringRef name) - : Leak(tf, name, true) {} + LeakAtReturn(StringRef name) + : Leak(name, true) {} }; class LeakWithinFunction : public Leak { public: - LeakWithinFunction(CFRefCount* tf, llvm::StringRef name) - : Leak(tf, name, false) {} + LeakWithinFunction(StringRef name) + : Leak(name, false) {} }; //===---------===// // Bug Reports. // //===---------===// - class CFRefReport : public RangedBugReport { + class CFRefReportVisitor : public BugReporterVisitor { protected: SymbolRef Sym; - const CFRefCount &TF; + const SummaryLogTy &SummaryLog; + bool GCEnabled; + public: - CFRefReport(CFRefBug& D, const CFRefCount &tf, - ExplodedNode *n, SymbolRef sym) - : RangedBugReport(D, D.getDescription(), n), Sym(sym), TF(tf) {} + CFRefReportVisitor(SymbolRef sym, bool gcEnabled, const SummaryLogTy &log) + : Sym(sym), SummaryLog(log), GCEnabled(gcEnabled) {} - CFRefReport(CFRefBug& D, const CFRefCount &tf, - ExplodedNode *n, SymbolRef sym, llvm::StringRef endText) - : RangedBugReport(D, D.getDescription(), endText, n), Sym(sym), TF(tf) {} - - virtual ~CFRefReport() {} - - CFRefBug& getBugType() const { - return (CFRefBug&) RangedBugReport::getBugType(); + virtual void Profile(llvm::FoldingSetNodeID &ID) const { + static int x = 0; + ID.AddPointer(&x); + ID.AddPointer(Sym); } - virtual std::pair<ranges_iterator, ranges_iterator> getRanges() const { - if (!getBugType().isLeak()) - return RangedBugReport::getRanges(); - else - return std::make_pair(ranges_iterator(), ranges_iterator()); - } + virtual PathDiagnosticPiece *VisitNode(const ExplodedNode *N, + const ExplodedNode *PrevN, + BugReporterContext &BRC, + BugReport &BR); - SymbolRef getSymbol() const { return Sym; } + virtual PathDiagnosticPiece *getEndPath(BugReporterContext &BRC, + const ExplodedNode *N, + BugReport &BR); + }; - PathDiagnosticPiece* getEndPath(BugReporterContext& BRC, - const ExplodedNode* N); + class CFRefLeakReportVisitor : public CFRefReportVisitor { + public: + CFRefLeakReportVisitor(SymbolRef sym, bool GCEnabled, + const SummaryLogTy &log) + : CFRefReportVisitor(sym, GCEnabled, log) {} - std::pair<const char**,const char**> getExtraDescriptiveText(); + PathDiagnosticPiece *getEndPath(BugReporterContext &BRC, + const ExplodedNode *N, + BugReport &BR); + }; + + class CFRefReport : public BugReport { + void addGCModeDescription(const LangOptions &LOpts, bool GCEnabled); - PathDiagnosticPiece* VisitNode(const ExplodedNode* N, - const ExplodedNode* PrevN, - BugReporterContext& BRC); + public: + CFRefReport(CFRefBug &D, const LangOptions &LOpts, bool GCEnabled, + const SummaryLogTy &Log, ExplodedNode *n, SymbolRef sym, + bool registerVisitor = true) + : BugReport(D, D.getDescription(), n) { + if (registerVisitor) + addVisitor(new CFRefReportVisitor(sym, GCEnabled, Log)); + addGCModeDescription(LOpts, GCEnabled); + } + + CFRefReport(CFRefBug &D, const LangOptions &LOpts, bool GCEnabled, + const SummaryLogTy &Log, ExplodedNode *n, SymbolRef sym, + StringRef endText) + : BugReport(D, D.getDescription(), endText, n) { + addVisitor(new CFRefReportVisitor(sym, GCEnabled, Log)); + addGCModeDescription(LOpts, GCEnabled); + } + + virtual std::pair<ranges_iterator, ranges_iterator> getRanges() { + const CFRefBug& BugTy = static_cast<CFRefBug&>(getBugType()); + if (!BugTy.isLeak()) + return BugReport::getRanges(); + else + return std::make_pair(ranges_iterator(), ranges_iterator()); + } }; class CFRefLeakReport : public CFRefReport { - SourceLocation AllocSite; const MemRegion* AllocBinding; - public: - CFRefLeakReport(CFRefBug& D, const CFRefCount &tf, - ExplodedNode *n, SymbolRef sym, - ExprEngine& Eng); - PathDiagnosticPiece* getEndPath(BugReporterContext& BRC, - const ExplodedNode* N); + public: + CFRefLeakReport(CFRefBug &D, const LangOptions &LOpts, bool GCEnabled, + const SummaryLogTy &Log, ExplodedNode *n, SymbolRef sym, + ExprEngine &Eng); - SourceLocation getLocation() const { return AllocSite; } + PathDiagnosticLocation getLocation(const SourceManager &SM) const { + assert(Location.isValid()); + return Location; + } }; } // end anonymous namespace - - -static const char* Msgs[] = { - // GC only - "Code is compiled to only use garbage collection", - // No GC. - "Code is compiled to use reference counts", - // Hybrid, with GC. - "Code is compiled to use either garbage collection (GC) or reference counts" - " (non-GC). The bug occurs with GC enabled", - // Hybrid, without GC - "Code is compiled to use either garbage collection (GC) or reference counts" - " (non-GC). The bug occurs in non-GC mode" -}; - -std::pair<const char**,const char**> CFRefReport::getExtraDescriptiveText() { - CFRefCount& TF = static_cast<CFRefBug&>(getBugType()).getTF(); - - switch (TF.getLangOptions().getGCMode()) { - default: - assert(false); - - case LangOptions::GCOnly: - assert (TF.isGCEnabled()); - return std::make_pair(&Msgs[0], &Msgs[0]+1); - - case LangOptions::NonGC: - assert (!TF.isGCEnabled()); - return std::make_pair(&Msgs[1], &Msgs[1]+1); - - case LangOptions::HybridGC: - if (TF.isGCEnabled()) - return std::make_pair(&Msgs[2], &Msgs[2]+1); - else - return std::make_pair(&Msgs[3], &Msgs[3]+1); +void CFRefReport::addGCModeDescription(const LangOptions &LOpts, + bool GCEnabled) { + const char *GCModeDescription = 0; + + switch (LOpts.getGC()) { + case LangOptions::GCOnly: + assert(GCEnabled); + GCModeDescription = "Code is compiled to only use garbage collection"; + break; + + case LangOptions::NonGC: + assert(!GCEnabled); + GCModeDescription = "Code is compiled to use reference counts"; + break; + + case LangOptions::HybridGC: + if (GCEnabled) { + GCModeDescription = "Code is compiled to use either garbage collection " + "(GC) or reference counts (non-GC). The bug occurs " + "with GC enabled"; + break; + } else { + GCModeDescription = "Code is compiled to use either garbage collection " + "(GC) or reference counts (non-GC). The bug occurs " + "in non-GC mode"; + break; + } } + + assert(GCModeDescription && "invalid/unknown GC mode"); + addExtraText(GCModeDescription); } -static inline bool contains(const llvm::SmallVectorImpl<ArgEffect>& V, +// FIXME: This should be a method on SmallVector. +static inline bool contains(const SmallVectorImpl<ArgEffect>& V, ArgEffect X) { - for (llvm::SmallVectorImpl<ArgEffect>::const_iterator I=V.begin(), E=V.end(); + for (SmallVectorImpl<ArgEffect>::const_iterator I=V.begin(), E=V.end(); I!=E; ++I) if (*I == X) return true; return false; } -PathDiagnosticPiece* CFRefReport::VisitNode(const ExplodedNode* N, - const ExplodedNode* PrevN, - BugReporterContext& BRC) { +PathDiagnosticPiece *CFRefReportVisitor::VisitNode(const ExplodedNode *N, + const ExplodedNode *PrevN, + BugReporterContext &BRC, + BugReport &BR) { - if (!isa<PostStmt>(N->getLocation())) + if (!isa<StmtPoint>(N->getLocation())) return NULL; // Check if the type state has changed. - const GRState *PrevSt = PrevN->getState(); - const GRState *CurrSt = N->getState(); + const ProgramState *PrevSt = PrevN->getState(); + const ProgramState *CurrSt = N->getState(); const RefVal* CurrT = CurrSt->get<RefBindings>(Sym); if (!CurrT) return NULL; @@ -2083,13 +1849,13 @@ PathDiagnosticPiece* CFRefReport::VisitNode(const ExplodedNode* N, // This is the allocation site since the previous node had no bindings // for this symbol. if (!PrevT) { - const Stmt* S = cast<PostStmt>(N->getLocation()).getStmt(); + const Stmt *S = cast<StmtPoint>(N->getLocation()).getStmt(); if (const CallExpr *CE = dyn_cast<CallExpr>(S)) { // Get the name of the callee (if it is available). SVal X = CurrSt->getSValAsScalarOrLoc(CE->getCallee()); - if (const FunctionDecl* FD = X.getAsFunctionDecl()) - os << "Call to function '" << FD << '\''; + if (const FunctionDecl *FD = X.getAsFunctionDecl()) + os << "Call to function '" << *FD << '\''; else os << "function call"; } @@ -2110,7 +1876,7 @@ PathDiagnosticPiece* CFRefReport::VisitNode(const ExplodedNode* N, if (CurrV.isOwned()) { os << "+1 retain count"; - if (static_cast<CFRefBug&>(getBugType()).getTF().isGCEnabled()) { + if (GCEnabled) { assert(CurrV.getObjKind() == RetEffect::CF); os << ". " "Core Foundation objects are not automatically garbage collected."; @@ -2121,19 +1887,20 @@ PathDiagnosticPiece* CFRefReport::VisitNode(const ExplodedNode* N, os << "+0 retain count"; } - PathDiagnosticLocation Pos(S, BRC.getSourceManager()); + PathDiagnosticLocation Pos(S, BRC.getSourceManager(), + N->getLocationContext()); return new PathDiagnosticEventPiece(Pos, os.str()); } // Gather up the effects that were performed on the object at this // program point - llvm::SmallVector<ArgEffect, 2> AEffects; + SmallVector<ArgEffect, 2> AEffects; - if (const RetainSummary *Summ = - TF.getSummaryOfNode(BRC.getNodeResolver().getOriginalNode(N))) { + const ExplodedNode *OrigNode = BRC.getNodeResolver().getOriginalNode(N); + if (const RetainSummary *Summ = SummaryLog.lookup(OrigNode)) { // We only have summaries attached to nodes after evaluating CallExpr and // ObjCMessageExprs. - const Stmt* S = cast<PostStmt>(N->getLocation()).getStmt(); + const Stmt *S = cast<StmtPoint>(N->getLocation()).getStmt(); if (const CallExpr *CE = dyn_cast<CallExpr>(S)) { // Iterate through the parameter expressions and see if the symbol @@ -2166,7 +1933,7 @@ PathDiagnosticPiece* CFRefReport::VisitNode(const ExplodedNode* N, RefVal PrevV = *PrevT; // Specially handle -dealloc. - if (!TF.isGCEnabled() && contains(AEffects, Dealloc)) { + if (!GCEnabled && contains(AEffects, Dealloc)) { // Determine if the object's reference count was pushed to zero. assert(!(PrevV == CurrV) && "The typestate *must* have changed."); // We may not have transitioned to 'release' if we hit an error. @@ -2181,16 +1948,15 @@ PathDiagnosticPiece* CFRefReport::VisitNode(const ExplodedNode* N, // Specially handle CFMakeCollectable and friends. if (contains(AEffects, MakeCollectable)) { // Get the name of the function. - const Stmt* S = cast<PostStmt>(N->getLocation()).getStmt(); + const Stmt *S = cast<StmtPoint>(N->getLocation()).getStmt(); SVal X = CurrSt->getSValAsScalarOrLoc(cast<CallExpr>(S)->getCallee()); - const FunctionDecl* FD = X.getAsFunctionDecl(); - const std::string& FName = FD->getNameAsString(); + const FunctionDecl *FD = X.getAsFunctionDecl(); - if (TF.isGCEnabled()) { + if (GCEnabled) { // Determine if the object's reference count was pushed to zero. assert(!(PrevV == CurrV) && "The typestate *must* have changed."); - os << "In GC mode a call to '" << FName + os << "In GC mode a call to '" << *FD << "' decrements an object's retain count and registers the " "object with the garbage collector. "; @@ -2205,7 +1971,7 @@ PathDiagnosticPiece* CFRefReport::VisitNode(const ExplodedNode* N, << '.'; } else - os << "When GC is not enabled a call to '" << FName + os << "When GC is not enabled a call to '" << *FD << "' has no effect on its argument."; // Nothing more to say. @@ -2237,7 +2003,7 @@ PathDiagnosticPiece* CFRefReport::VisitNode(const ExplodedNode* N, os << " The object now has a +" << Count << " retain count."; if (PrevV.getKind() == RefVal::Released) { - assert(TF.isGCEnabled() && CurrV.getCount() > 0); + assert(GCEnabled && CurrV.getCount() > 0); os << " The object is not eligible for garbage collection until the " "retain count reaches 0 again."; } @@ -2262,11 +2028,11 @@ PathDiagnosticPiece* CFRefReport::VisitNode(const ExplodedNode* N, } // Emit any remaining diagnostics for the argument effects (if any). - for (llvm::SmallVectorImpl<ArgEffect>::iterator I=AEffects.begin(), + for (SmallVectorImpl<ArgEffect>::iterator I=AEffects.begin(), E=AEffects.end(); I != E; ++I) { // A bunch of things have alternate behavior under GC. - if (TF.isGCEnabled()) + if (GCEnabled) switch (*I) { default: break; case Autorelease: @@ -2285,15 +2051,16 @@ PathDiagnosticPiece* CFRefReport::VisitNode(const ExplodedNode* N, if (os.str().empty()) return 0; // We have nothing to say! - const Stmt* S = cast<PostStmt>(N->getLocation()).getStmt(); - PathDiagnosticLocation Pos(S, BRC.getSourceManager()); - PathDiagnosticPiece* P = new PathDiagnosticEventPiece(Pos, os.str()); + const Stmt *S = cast<StmtPoint>(N->getLocation()).getStmt(); + PathDiagnosticLocation Pos(S, BRC.getSourceManager(), + N->getLocationContext()); + PathDiagnosticPiece *P = new PathDiagnosticEventPiece(Pos, os.str()); // Add the range by scanning the children of the statement for any bindings // to Sym. for (Stmt::const_child_iterator I = S->child_begin(), E = S->child_end(); I!=E; ++I) - if (const Expr* Exp = dyn_cast_or_null<Expr>(*I)) + if (const Expr *Exp = dyn_cast_or_null<Expr>(*I)) if (CurrSt->getSValAsScalarOrLoc(Exp).getAsLocSymbol() == Sym) { P->addRange(Exp->getSourceRange()); break; @@ -2335,16 +2102,16 @@ namespace { } static std::pair<const ExplodedNode*,const MemRegion*> -GetAllocationSite(GRStateManager& StateMgr, const ExplodedNode* N, +GetAllocationSite(ProgramStateManager& StateMgr, const ExplodedNode *N, SymbolRef Sym) { // Find both first node that referred to the tracked symbol and the // memory location that value was store to. - const ExplodedNode* Last = N; + const ExplodedNode *Last = N; const MemRegion* FirstBinding = 0; while (N) { - const GRState* St = N->getState(); + const ProgramState *St = N->getState(); RefBindings B = St->get<RefBindings>(); if (!B.lookup(Sym)) @@ -2362,17 +2129,19 @@ GetAllocationSite(GRStateManager& StateMgr, const ExplodedNode* N, } PathDiagnosticPiece* -CFRefReport::getEndPath(BugReporterContext& BRC, - const ExplodedNode* EndN) { +CFRefReportVisitor::getEndPath(BugReporterContext &BRC, + const ExplodedNode *EndN, + BugReport &BR) { // Tell the BugReporterContext to report cases when the tracked symbol is // assigned to different variables, etc. BRC.addNotableSymbol(Sym); - return RangedBugReport::getEndPath(BRC, EndN); + return BugReporterVisitor::getDefaultEndPath(BRC, EndN, BR); } PathDiagnosticPiece* -CFRefLeakReport::getEndPath(BugReporterContext& BRC, - const ExplodedNode* EndN){ +CFRefLeakReportVisitor::getEndPath(BugReporterContext &BRC, + const ExplodedNode *EndN, + BugReport &BR) { // Tell the BugReporterContext to report cases when the tracked symbol is // assigned to different variables, etc. @@ -2381,41 +2150,19 @@ CFRefLeakReport::getEndPath(BugReporterContext& BRC, // We are reporting a leak. Walk up the graph to get to the first node where // the symbol appeared, and also get the first VarDecl that tracked object // is stored to. - const ExplodedNode* AllocNode = 0; + const ExplodedNode *AllocNode = 0; const MemRegion* FirstBinding = 0; llvm::tie(AllocNode, FirstBinding) = GetAllocationSite(BRC.getStateManager(), EndN, Sym); - SourceManager& SMgr = BRC.getSourceManager(); + SourceManager& SM = BRC.getSourceManager(); // Compute an actual location for the leak. Sometimes a leak doesn't // occur at an actual statement (e.g., transition between blocks; end // of function) so we need to walk the graph and compute a real location. - const ExplodedNode* LeakN = EndN; - PathDiagnosticLocation L; - - while (LeakN) { - ProgramPoint P = LeakN->getLocation(); - - if (const PostStmt *PS = dyn_cast<PostStmt>(&P)) { - L = PathDiagnosticLocation(PS->getStmt()->getLocStart(), SMgr); - break; - } - else if (const BlockEdge *BE = dyn_cast<BlockEdge>(&P)) { - if (const Stmt* Term = BE->getSrc()->getTerminator()) { - L = PathDiagnosticLocation(Term->getLocStart(), SMgr); - break; - } - } - - LeakN = LeakN->succ_empty() ? 0 : *(LeakN->succ_begin()); - } - - if (!L.isValid()) { - const Decl &D = EndN->getCodeDecl(); - L = PathDiagnosticLocation(D.getBodyRBrace(), SMgr); - } + const ExplodedNode *LeakN = EndN; + PathDiagnosticLocation L = PathDiagnosticLocation::createEndOfPath(LeakN, SM); std::string sbuf; llvm::raw_string_ostream os(sbuf); @@ -2454,7 +2201,7 @@ CFRefLeakReport::getEndPath(BugReporterContext& BRC, } } else if (RV->getKind() == RefVal::ErrorGCLeakReturned) { - ObjCMethodDecl& MD = cast<ObjCMethodDecl>(EndN->getCodeDecl()); + ObjCMethodDecl &MD = cast<ObjCMethodDecl>(EndN->getCodeDecl()); os << " and returned from method '" << MD.getSelector().getAsString() << "' is potentially leaked when using garbage collection. Callers " "of this method do not expect a returned object with a +1 retain " @@ -2468,10 +2215,11 @@ CFRefLeakReport::getEndPath(BugReporterContext& BRC, return new PathDiagnosticEventPiece(L, os.str()); } -CFRefLeakReport::CFRefLeakReport(CFRefBug& D, const CFRefCount &tf, - ExplodedNode *n, - SymbolRef sym, ExprEngine& Eng) -: CFRefReport(D, tf, n, sym) { +CFRefLeakReport::CFRefLeakReport(CFRefBug &D, const LangOptions &LOpts, + bool GCEnabled, const SummaryLogTy &Log, + ExplodedNode *n, SymbolRef sym, + ExprEngine &Eng) +: CFRefReport(D, LOpts, GCEnabled, Log, n, sym, false) { // Most bug reports are cached at the location where they occurred. // With leaks, we want to unique them by the location where they were @@ -2481,41 +2229,418 @@ CFRefLeakReport::CFRefLeakReport(CFRefBug& D, const CFRefCount &tf, // Note that this is *not* the trimmed graph; we are guaranteed, however, // that all ancestor nodes that represent the allocation site have the // same SourceLocation. - const ExplodedNode* AllocNode = 0; + const ExplodedNode *AllocNode = 0; + + const SourceManager& SMgr = Eng.getContext().getSourceManager(); llvm::tie(AllocNode, AllocBinding) = // Set AllocBinding. - GetAllocationSite(Eng.getStateManager(), getErrorNode(), getSymbol()); + GetAllocationSite(Eng.getStateManager(), getErrorNode(), sym); // Get the SourceLocation for the allocation site. ProgramPoint P = AllocNode->getLocation(); - AllocSite = cast<PostStmt>(P).getStmt()->getLocStart(); - + const Stmt *AllocStmt = cast<PostStmt>(P).getStmt(); + Location = PathDiagnosticLocation::createBegin(AllocStmt, SMgr, + n->getLocationContext()); // Fill in the description of the bug. Description.clear(); llvm::raw_string_ostream os(Description); - SourceManager& SMgr = Eng.getContext().getSourceManager(); - unsigned AllocLine = SMgr.getInstantiationLineNumber(AllocSite); + unsigned AllocLine = SMgr.getExpansionLineNumber(AllocStmt->getLocStart()); os << "Potential leak "; - if (tf.isGCEnabled()) { + if (GCEnabled) os << "(when using garbage collection) "; - } os << "of an object allocated on line " << AllocLine; // FIXME: AllocBinding doesn't get populated for RegionStore yet. if (AllocBinding) os << " and stored into '" << AllocBinding->getString() << '\''; + + addVisitor(new CFRefLeakReportVisitor(sym, GCEnabled, Log)); } //===----------------------------------------------------------------------===// // Main checker logic. //===----------------------------------------------------------------------===// +namespace { +class RetainCountChecker + : public Checker< check::Bind, + check::DeadSymbols, + check::EndAnalysis, + check::EndPath, + check::PostStmt<BlockExpr>, + check::PostStmt<CastExpr>, + check::PostStmt<CallExpr>, + check::PostStmt<CXXConstructExpr>, + check::PostObjCMessage, + check::PreStmt<ReturnStmt>, + check::RegionChanges, + eval::Assume, + eval::Call > { + mutable llvm::OwningPtr<CFRefBug> useAfterRelease, releaseNotOwned; + mutable llvm::OwningPtr<CFRefBug> deallocGC, deallocNotOwned; + mutable llvm::OwningPtr<CFRefBug> overAutorelease, returnNotOwnedForOwned; + mutable llvm::OwningPtr<CFRefBug> leakWithinFunction, leakAtReturn; + mutable llvm::OwningPtr<CFRefBug> leakWithinFunctionGC, leakAtReturnGC; + + typedef llvm::DenseMap<SymbolRef, const SimpleProgramPointTag *> SymbolTagMap; + + // This map is only used to ensure proper deletion of any allocated tags. + mutable SymbolTagMap DeadSymbolTags; + + mutable llvm::OwningPtr<RetainSummaryManager> Summaries; + mutable llvm::OwningPtr<RetainSummaryManager> SummariesGC; + + mutable ARCounts::Factory ARCountFactory; + + mutable SummaryLogTy SummaryLog; + mutable bool ShouldResetSummaryLog; + +public: + RetainCountChecker() : ShouldResetSummaryLog(false) {} + + virtual ~RetainCountChecker() { + DeleteContainerSeconds(DeadSymbolTags); + } + + void checkEndAnalysis(ExplodedGraph &G, BugReporter &BR, + ExprEngine &Eng) const { + // FIXME: This is a hack to make sure the summary log gets cleared between + // analyses of different code bodies. + // + // Why is this necessary? Because a checker's lifetime is tied to a + // translation unit, but an ExplodedGraph's lifetime is just a code body. + // Once in a blue moon, a new ExplodedNode will have the same address as an + // old one with an associated summary, and the bug report visitor gets very + // confused. (To make things worse, the summary lifetime is currently also + // tied to a code body, so we get a crash instead of incorrect results.) + // + // Why is this a bad solution? Because if the lifetime of the ExplodedGraph + // changes, things will start going wrong again. Really the lifetime of this + // log needs to be tied to either the specific nodes in it or the entire + // ExplodedGraph, not to a specific part of the code being analyzed. + // + // (Also, having stateful local data means that the same checker can't be + // used from multiple threads, but a lot of checkers have incorrect + // assumptions about that anyway. So that wasn't a priority at the time of + // this fix.) + // + // This happens at the end of analysis, but bug reports are emitted /after/ + // this point. So we can't just clear the summary log now. Instead, we mark + // that the next time we access the summary log, it should be cleared. + + // If we never reset the summary log during /this/ code body analysis, + // there were no new summaries. There might still have been summaries from + // the /last/ analysis, so clear them out to make sure the bug report + // visitors don't get confused. + if (ShouldResetSummaryLog) + SummaryLog.clear(); + + ShouldResetSummaryLog = !SummaryLog.empty(); + } + + CFRefBug *getLeakWithinFunctionBug(const LangOptions &LOpts, + bool GCEnabled) const { + if (GCEnabled) { + if (!leakWithinFunctionGC) + leakWithinFunctionGC.reset(new LeakWithinFunction("Leak of object when " + "using garbage " + "collection")); + return leakWithinFunctionGC.get(); + } else { + if (!leakWithinFunction) { + if (LOpts.getGC() == LangOptions::HybridGC) { + leakWithinFunction.reset(new LeakWithinFunction("Leak of object when " + "not using garbage " + "collection (GC) in " + "dual GC/non-GC " + "code")); + } else { + leakWithinFunction.reset(new LeakWithinFunction("Leak")); + } + } + return leakWithinFunction.get(); + } + } + + CFRefBug *getLeakAtReturnBug(const LangOptions &LOpts, bool GCEnabled) const { + if (GCEnabled) { + if (!leakAtReturnGC) + leakAtReturnGC.reset(new LeakAtReturn("Leak of returned object when " + "using garbage collection")); + return leakAtReturnGC.get(); + } else { + if (!leakAtReturn) { + if (LOpts.getGC() == LangOptions::HybridGC) { + leakAtReturn.reset(new LeakAtReturn("Leak of returned object when " + "not using garbage collection " + "(GC) in dual GC/non-GC code")); + } else { + leakAtReturn.reset(new LeakAtReturn("Leak of returned object")); + } + } + return leakAtReturn.get(); + } + } + + RetainSummaryManager &getSummaryManager(ASTContext &Ctx, + bool GCEnabled) const { + // FIXME: We don't support ARC being turned on and off during one analysis. + // (nor, for that matter, do we support changing ASTContexts) + bool ARCEnabled = (bool)Ctx.getLangOptions().ObjCAutoRefCount; + if (GCEnabled) { + if (!SummariesGC) + SummariesGC.reset(new RetainSummaryManager(Ctx, true, ARCEnabled)); + else + assert(SummariesGC->isARCEnabled() == ARCEnabled); + return *SummariesGC; + } else { + if (!Summaries) + Summaries.reset(new RetainSummaryManager(Ctx, false, ARCEnabled)); + else + assert(Summaries->isARCEnabled() == ARCEnabled); + return *Summaries; + } + } + + RetainSummaryManager &getSummaryManager(CheckerContext &C) const { + return getSummaryManager(C.getASTContext(), C.isObjCGCEnabled()); + } + + void printState(raw_ostream &Out, const ProgramState *State, + const char *NL, const char *Sep) const; + + void checkBind(SVal loc, SVal val, const Stmt *S, CheckerContext &C) const; + void checkPostStmt(const BlockExpr *BE, CheckerContext &C) const; + void checkPostStmt(const CastExpr *CE, CheckerContext &C) const; + + void checkPostStmt(const CallExpr *CE, CheckerContext &C) const; + void checkPostStmt(const CXXConstructExpr *CE, CheckerContext &C) const; + void checkPostObjCMessage(const ObjCMessage &Msg, CheckerContext &C) const; + void checkSummary(const RetainSummary &Summ, const CallOrObjCMessage &Call, + CheckerContext &C) const; + + bool evalCall(const CallExpr *CE, CheckerContext &C) const; + + const ProgramState *evalAssume(const ProgramState *state, SVal Cond, + bool Assumption) const; + + const ProgramState * + checkRegionChanges(const ProgramState *state, + const StoreManager::InvalidatedSymbols *invalidated, + ArrayRef<const MemRegion *> ExplicitRegions, + ArrayRef<const MemRegion *> Regions) const; + + bool wantsRegionChangeUpdate(const ProgramState *state) const { + return true; + } + + void checkPreStmt(const ReturnStmt *S, CheckerContext &C) const; + void checkReturnWithRetEffect(const ReturnStmt *S, CheckerContext &C, + ExplodedNode *Pred, RetEffect RE, RefVal X, + SymbolRef Sym, const ProgramState *state) const; + + void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const; + void checkEndPath(EndOfFunctionNodeBuilder &Builder, ExprEngine &Eng) const; + + const ProgramState *updateSymbol(const ProgramState *state, SymbolRef sym, + RefVal V, ArgEffect E, RefVal::Kind &hasErr, + CheckerContext &C) const; + + void processNonLeakError(const ProgramState *St, SourceRange ErrorRange, + RefVal::Kind ErrorKind, SymbolRef Sym, + CheckerContext &C) const; + + const ProgramPointTag *getDeadSymbolTag(SymbolRef sym) const; + + const ProgramState *handleSymbolDeath(const ProgramState *state, + SymbolRef sid, RefVal V, + SmallVectorImpl<SymbolRef> &Leaked) const; + + std::pair<ExplodedNode *, const ProgramState *> + handleAutoreleaseCounts(const ProgramState *state, + GenericNodeBuilderRefCount Bd, ExplodedNode *Pred, + ExprEngine &Eng, SymbolRef Sym, RefVal V) const; + + ExplodedNode *processLeaks(const ProgramState *state, + SmallVectorImpl<SymbolRef> &Leaked, + GenericNodeBuilderRefCount &Builder, + ExprEngine &Eng, + ExplodedNode *Pred = 0) const; +}; +} // end anonymous namespace + +namespace { +class StopTrackingCallback : public SymbolVisitor { + const ProgramState *state; +public: + StopTrackingCallback(const ProgramState *st) : state(st) {} + const ProgramState *getState() const { return state; } + + bool VisitSymbol(SymbolRef sym) { + state = state->remove<RefBindings>(sym); + return true; + } +}; +} // end anonymous namespace + +//===----------------------------------------------------------------------===// +// Handle statements that may have an effect on refcounts. +//===----------------------------------------------------------------------===// + +void RetainCountChecker::checkPostStmt(const BlockExpr *BE, + CheckerContext &C) const { + + // Scan the BlockDecRefExprs for any object the retain count checker + // may be tracking. + if (!BE->getBlockDecl()->hasCaptures()) + return; + + const ProgramState *state = C.getState(); + const BlockDataRegion *R = + cast<BlockDataRegion>(state->getSVal(BE).getAsRegion()); + + BlockDataRegion::referenced_vars_iterator I = R->referenced_vars_begin(), + E = R->referenced_vars_end(); + + if (I == E) + return; + + // FIXME: For now we invalidate the tracking of all symbols passed to blocks + // via captured variables, even though captured variables result in a copy + // and in implicit increment/decrement of a retain count. + SmallVector<const MemRegion*, 10> Regions; + const LocationContext *LC = C.getPredecessor()->getLocationContext(); + MemRegionManager &MemMgr = C.getSValBuilder().getRegionManager(); + + for ( ; I != E; ++I) { + const VarRegion *VR = *I; + if (VR->getSuperRegion() == R) { + VR = MemMgr.getVarRegion(VR->getDecl(), LC); + } + Regions.push_back(VR); + } + + state = + state->scanReachableSymbols<StopTrackingCallback>(Regions.data(), + Regions.data() + Regions.size()).getState(); + C.addTransition(state); +} + +void RetainCountChecker::checkPostStmt(const CastExpr *CE, + CheckerContext &C) const { + const ObjCBridgedCastExpr *BE = dyn_cast<ObjCBridgedCastExpr>(CE); + if (!BE) + return; + + ArgEffect AE = IncRef; + + switch (BE->getBridgeKind()) { + case clang::OBC_Bridge: + // Do nothing. + return; + case clang::OBC_BridgeRetained: + AE = IncRef; + break; + case clang::OBC_BridgeTransfer: + AE = DecRefBridgedTransfered; + break; + } + + const ProgramState *state = C.getState(); + SymbolRef Sym = state->getSVal(CE).getAsLocSymbol(); + if (!Sym) + return; + const RefVal* T = state->get<RefBindings>(Sym); + if (!T) + return; + + RefVal::Kind hasErr = (RefVal::Kind) 0; + state = updateSymbol(state, Sym, *T, AE, hasErr, C); + + if (hasErr) { + // FIXME: If we get an error during a bridge cast, should we report it? + // Should we assert that there is no error? + return; + } + + C.generateNode(state); +} + +void RetainCountChecker::checkPostStmt(const CallExpr *CE, + CheckerContext &C) const { + // Get the callee. + const ProgramState *state = C.getState(); + const Expr *Callee = CE->getCallee(); + SVal L = state->getSVal(Callee); + + RetainSummaryManager &Summaries = getSummaryManager(C); + const RetainSummary *Summ = 0; + + // FIXME: Better support for blocks. For now we stop tracking anything + // that is passed to blocks. + // FIXME: Need to handle variables that are "captured" by the block. + if (dyn_cast_or_null<BlockDataRegion>(L.getAsRegion())) { + Summ = Summaries.getPersistentStopSummary(); + } else if (const FunctionDecl *FD = L.getAsFunctionDecl()) { + Summ = Summaries.getSummary(FD); + } else if (const CXXMemberCallExpr *me = dyn_cast<CXXMemberCallExpr>(CE)) { + if (const CXXMethodDecl *MD = me->getMethodDecl()) + Summ = Summaries.getSummary(MD); + } + + if (!Summ) + Summ = Summaries.getDefaultSummary(); + + checkSummary(*Summ, CallOrObjCMessage(CE, state), C); +} + +void RetainCountChecker::checkPostStmt(const CXXConstructExpr *CE, + CheckerContext &C) const { + const CXXConstructorDecl *Ctor = CE->getConstructor(); + if (!Ctor) + return; + + RetainSummaryManager &Summaries = getSummaryManager(C); + const RetainSummary *Summ = Summaries.getSummary(Ctor); + + // If we didn't get a summary, this constructor doesn't affect retain counts. + if (!Summ) + return; + + const ProgramState *state = C.getState(); + checkSummary(*Summ, CallOrObjCMessage(CE, state), C); +} + +void RetainCountChecker::checkPostObjCMessage(const ObjCMessage &Msg, + CheckerContext &C) const { + const ProgramState *state = C.getState(); + ExplodedNode *Pred = C.getPredecessor(); + + RetainSummaryManager &Summaries = getSummaryManager(C); + + const RetainSummary *Summ; + if (Msg.isInstanceMessage()) { + const LocationContext *LC = Pred->getLocationContext(); + Summ = Summaries.getInstanceMethodSummary(Msg, state, LC); + } else { + Summ = Summaries.getClassMethodSummary(Msg); + } + + // If we didn't get a summary, this message doesn't affect retain counts. + if (!Summ) + return; + + checkSummary(*Summ, CallOrObjCMessage(Msg, state), C); +} + /// GetReturnType - Used to get the return type of a message expression or /// function call with the intention of affixing that type to a tracked symbol. /// While the the return type can be queried directly from RetEx, when /// invoking class methods we augment to the return type to be that of /// a pointer to the class (as opposed it just being id). -static QualType GetReturnType(const Expr* RetE, ASTContext& Ctx) { +// FIXME: We may be able to do this with related result types instead. +// This function is probably overestimating. +static QualType GetReturnType(const Expr *RetE, ASTContext &Ctx) { QualType RetTy = RetE->getType(); // If RetE is not a message expression just return its type. // If RetE is a message expression, return its types if it is something @@ -2536,167 +2661,43 @@ static QualType GetReturnType(const Expr* RetE, ASTContext& Ctx) { return RetTy; } - -// HACK: Symbols that have ref-count state that are referenced directly -// (not as structure or array elements, or via bindings) by an argument -// should not have their ref-count state stripped after we have -// done an invalidation pass. -// -// FIXME: This is a global to currently share between CFRefCount and -// RetainReleaseChecker. Eventually all functionality in CFRefCount should -// be migrated to RetainReleaseChecker, and we can make this a non-global. -llvm::DenseSet<SymbolRef> WhitelistedSymbols; -namespace { -struct ResetWhiteList { - ResetWhiteList() {} - ~ResetWhiteList() { WhitelistedSymbols.clear(); } -}; -} - -void CFRefCount::evalSummary(ExplodedNodeSet& Dst, - ExprEngine& Eng, - StmtNodeBuilder& Builder, - const Expr* Ex, - const CallOrObjCMessage &callOrMsg, - InstanceReceiver Receiver, - const RetainSummary& Summ, - const MemRegion *Callee, - ExplodedNode* Pred, const GRState *state) { +void RetainCountChecker::checkSummary(const RetainSummary &Summ, + const CallOrObjCMessage &CallOrMsg, + CheckerContext &C) const { + const ProgramState *state = C.getState(); // Evaluate the effect of the arguments. RefVal::Kind hasErr = (RefVal::Kind) 0; SourceRange ErrorRange; SymbolRef ErrorSym = 0; - llvm::SmallVector<const MemRegion*, 10> RegionsToInvalidate; - - // Use RAII to make sure the whitelist is properly cleared. - ResetWhiteList resetWhiteList; + for (unsigned idx = 0, e = CallOrMsg.getNumArgs(); idx != e; ++idx) { + SVal V = CallOrMsg.getArgSVal(idx); - // Invalidate all instance variables of the receiver of a message. - // FIXME: We should be able to do better with inter-procedural analysis. - if (Receiver) { - SVal V = Receiver.getSValAsScalarOrLoc(state); if (SymbolRef Sym = V.getAsLocSymbol()) { - if (state->get<RefBindings>(Sym)) - WhitelistedSymbols.insert(Sym); - } - if (const MemRegion *region = V.getAsRegion()) - RegionsToInvalidate.push_back(region); - } - - // Invalidate all instance variables for the callee of a C++ method call. - // FIXME: We should be able to do better with inter-procedural analysis. - // FIXME: we can probably do better for const versus non-const methods. - if (callOrMsg.isCXXCall()) { - if (const MemRegion *callee = callOrMsg.getCXXCallee().getAsRegion()) - RegionsToInvalidate.push_back(callee); - } - - for (unsigned idx = 0, e = callOrMsg.getNumArgs(); idx != e; ++idx) { - SVal V = callOrMsg.getArgSValAsScalarOrLoc(idx); - SymbolRef Sym = V.getAsLocSymbol(); - - if (Sym) - if (RefBindings::data_type* T = state->get<RefBindings>(Sym)) { - WhitelistedSymbols.insert(Sym); - state = Update(state, Sym, *T, Summ.getArg(idx), hasErr); + if (RefBindings::data_type *T = state->get<RefBindings>(Sym)) { + state = updateSymbol(state, Sym, *T, Summ.getArg(idx), hasErr, C); if (hasErr) { - ErrorRange = callOrMsg.getArgSourceRange(idx); + ErrorRange = CallOrMsg.getArgSourceRange(idx); ErrorSym = Sym; break; } } - - tryAgain: - if (isa<Loc>(V)) { - if (loc::MemRegionVal* MR = dyn_cast<loc::MemRegionVal>(&V)) { - if (Summ.getArg(idx) == DoNothingByRef) - continue; - - // Invalidate the value of the variable passed by reference. - const MemRegion *R = MR->getRegion(); - - // Are we dealing with an ElementRegion? If the element type is - // a basic integer type (e.g., char, int) and the underying region - // is a variable region then strip off the ElementRegion. - // FIXME: We really need to think about this for the general case - // as sometimes we are reasoning about arrays and other times - // about (char*), etc., is just a form of passing raw bytes. - // e.g., void *p = alloca(); foo((char*)p); - if (const ElementRegion *ER = dyn_cast<ElementRegion>(R)) { - // Checking for 'integral type' is probably too promiscuous, but - // we'll leave it in for now until we have a systematic way of - // handling all of these cases. Eventually we need to come up - // with an interface to StoreManager so that this logic can be - // approriately delegated to the respective StoreManagers while - // still allowing us to do checker-specific logic (e.g., - // invalidating reference counts), probably via callbacks. - if (ER->getElementType()->isIntegralOrEnumerationType()) { - const MemRegion *superReg = ER->getSuperRegion(); - if (isa<VarRegion>(superReg) || isa<FieldRegion>(superReg) || - isa<ObjCIvarRegion>(superReg)) - R = cast<TypedRegion>(superReg); - } - // FIXME: What about layers of ElementRegions? - } - - // Mark this region for invalidation. We batch invalidate regions - // below for efficiency. - RegionsToInvalidate.push_back(R); - continue; - } - else { - // Nuke all other arguments passed by reference. - // FIXME: is this necessary or correct? This handles the non-Region - // cases. Is it ever valid to store to these? - state = state->unbindLoc(cast<Loc>(V)); - } } - else if (isa<nonloc::LocAsInteger>(V)) { - // If we are passing a location wrapped as an integer, unwrap it and - // invalidate the values referred by the location. - V = cast<nonloc::LocAsInteger>(V).getLoc(); - goto tryAgain; - } - } - - // Block calls result in all captured values passed-via-reference to be - // invalidated. - if (const BlockDataRegion *BR = dyn_cast_or_null<BlockDataRegion>(Callee)) { - RegionsToInvalidate.push_back(BR); } - // Invalidate regions we designed for invalidation use the batch invalidation - // API. - - // FIXME: We can have collisions on the conjured symbol if the - // expression *I also creates conjured symbols. We probably want - // to identify conjured symbols by an expression pair: the enclosing - // expression (the context) and the expression itself. This should - // disambiguate conjured symbols. - unsigned Count = Builder.getCurrentBlockCount(); - StoreManager::InvalidatedSymbols IS; - - // NOTE: Even if RegionsToInvalidate is empty, we must still invalidate - // global variables. - // NOTE: RetainReleaseChecker handles the actual invalidation of symbols. - state = - state->invalidateRegions(RegionsToInvalidate.data(), - RegionsToInvalidate.data() + - RegionsToInvalidate.size(), - Ex, Count, &IS, - /* invalidateGlobals = */ - Eng.doesInvalidateGlobals(callOrMsg)); - // Evaluate the effect on the message receiver. - if (!ErrorRange.isValid() && Receiver) { - SymbolRef Sym = Receiver.getSValAsScalarOrLoc(state).getAsLocSymbol(); - if (Sym) { - if (const RefVal* T = state->get<RefBindings>(Sym)) { - state = Update(state, Sym, *T, Summ.getReceiverEffect(), hasErr); + bool ReceiverIsTracked = false; + if (!hasErr && CallOrMsg.isObjCMessage()) { + const LocationContext *LC = C.getPredecessor()->getLocationContext(); + SVal Receiver = CallOrMsg.getInstanceMessageReceiver(LC); + if (SymbolRef Sym = Receiver.getAsLocSymbol()) { + if (const RefVal *T = state->get<RefBindings>(Sym)) { + ReceiverIsTracked = true; + state = updateSymbol(state, Sym, *T, Summ.getReceiverEffect(), + hasErr, C); if (hasErr) { - ErrorRange = Receiver.getSourceRange(); + ErrorRange = CallOrMsg.getReceiverSourceRange(); ErrorSym = Sym; } } @@ -2705,8 +2706,7 @@ void CFRefCount::evalSummary(ExplodedNodeSet& Dst, // Process any errors. if (hasErr) { - ProcessNonLeakError(Dst, Builder, Ex, ErrorRange, Pred, state, - hasErr, ErrorSym); + processNonLeakError(state, ErrorRange, hasErr, ErrorSym, C); return; } @@ -2714,75 +2714,34 @@ void CFRefCount::evalSummary(ExplodedNodeSet& Dst, RetEffect RE = Summ.getRetEffect(); if (RE.getKind() == RetEffect::OwnedWhenTrackedReceiver) { - bool found = false; - if (Receiver) { - SVal V = Receiver.getSValAsScalarOrLoc(state); - if (SymbolRef Sym = V.getAsLocSymbol()) - if (state->get<RefBindings>(Sym)) { - found = true; - RE = Summaries.getObjAllocRetEffect(); - } - } // FIXME: Otherwise, this is a send-to-super instance message. - if (!found) + if (ReceiverIsTracked) + RE = getSummaryManager(C).getObjAllocRetEffect(); + else RE = RetEffect::MakeNoRet(); } switch (RE.getKind()) { default: - assert (false && "Unhandled RetEffect."); break; - - case RetEffect::NoRet: { - // Make up a symbol for the return value (not reference counted). - // FIXME: Most of this logic is not specific to the retain/release - // checker. - - // FIXME: We eventually should handle structs and other compound types - // that are returned by value. - - // Use the result type from callOrMsg as it automatically adjusts - // for methods/functions that return references. - QualType resultTy = callOrMsg.getResultType(Eng.getContext()); - if (Loc::isLocType(resultTy) || - (resultTy->isIntegerType() && resultTy->isScalarType())) { - unsigned Count = Builder.getCurrentBlockCount(); - SValBuilder &svalBuilder = Eng.getSValBuilder(); - SVal X = svalBuilder.getConjuredSymbolVal(NULL, Ex, resultTy, Count); - state = state->BindExpr(Ex, X, false); - } - - break; - } - - case RetEffect::Alias: { - unsigned idx = RE.getIndex(); - assert (idx < callOrMsg.getNumArgs()); - SVal V = callOrMsg.getArgSValAsScalarOrLoc(idx); - state = state->BindExpr(Ex, V, false); - break; - } + llvm_unreachable("Unhandled RetEffect."); break; - case RetEffect::ReceiverAlias: { - assert(Receiver); - SVal V = Receiver.getSValAsScalarOrLoc(state); - state = state->BindExpr(Ex, V, false); + case RetEffect::NoRet: + // No work necessary. break; - } case RetEffect::OwnedAllocatedSymbol: case RetEffect::OwnedSymbol: { - unsigned Count = Builder.getCurrentBlockCount(); - SValBuilder &svalBuilder = Eng.getSValBuilder(); - SymbolRef Sym = svalBuilder.getConjuredSymbol(Ex, Count); + SymbolRef Sym = state->getSVal(CallOrMsg.getOriginExpr()).getAsSymbol(); + if (!Sym) + break; // Use the result type from callOrMsg as it automatically adjusts - // for methods/functions that return references. - QualType resultTy = callOrMsg.getResultType(Eng.getContext()); + // for methods/functions that return references. + QualType ResultTy = CallOrMsg.getResultType(C.getASTContext()); state = state->set<RefBindings>(Sym, RefVal::makeOwned(RE.getObjKind(), - resultTy)); - state = state->BindExpr(Ex, svalBuilder.makeLoc(Sym), false); + ResultTy)); // FIXME: Add a flag to the checker where allocations are assumed to - // *not fail. + // *not* fail. (The code below is out-of-date, though.) #if 0 if (RE.getKind() == RetEffect::OwnedAllocatedSymbol) { bool isFeasible; @@ -2797,151 +2756,324 @@ void CFRefCount::evalSummary(ExplodedNodeSet& Dst, case RetEffect::GCNotOwnedSymbol: case RetEffect::ARCNotOwnedSymbol: case RetEffect::NotOwnedSymbol: { - unsigned Count = Builder.getCurrentBlockCount(); - SValBuilder &svalBuilder = Eng.getSValBuilder(); - SymbolRef Sym = svalBuilder.getConjuredSymbol(Ex, Count); - QualType RetT = GetReturnType(Ex, svalBuilder.getContext()); + const Expr *Ex = CallOrMsg.getOriginExpr(); + SymbolRef Sym = state->getSVal(Ex).getAsSymbol(); + if (!Sym) + break; + + // Use GetReturnType in order to give [NSFoo alloc] the type NSFoo *. + QualType ResultTy = GetReturnType(Ex, C.getASTContext()); state = state->set<RefBindings>(Sym, RefVal::makeNotOwned(RE.getObjKind(), - RetT)); - state = state->BindExpr(Ex, svalBuilder.makeLoc(Sym), false); + ResultTy)); break; } } - // Generate a sink node if we are at the end of a path. - ExplodedNode *NewNode = - Summ.isEndPath() ? Builder.MakeSinkNode(Dst, Ex, Pred, state) - : Builder.MakeNode(Dst, Ex, Pred, state); + // This check is actually necessary; otherwise the statement builder thinks + // we've hit a previously-found path. + // Normally addTransition takes care of this, but we want the node pointer. + ExplodedNode *NewNode; + if (state == C.getState()) { + NewNode = C.getPredecessor(); + } else { + NewNode = C.generateNode(state); + } - // Annotate the edge with summary we used. - if (NewNode) SummaryLog[NewNode] = &Summ; + // Annotate the node with summary we used. + if (NewNode) { + // FIXME: This is ugly. See checkEndAnalysis for why it's necessary. + if (ShouldResetSummaryLog) { + SummaryLog.clear(); + ShouldResetSummaryLog = false; + } + SummaryLog[NewNode] = &Summ; + } } -void CFRefCount::evalCall(ExplodedNodeSet& Dst, - ExprEngine& Eng, - StmtNodeBuilder& Builder, - const CallExpr* CE, SVal L, - ExplodedNode* Pred) { - - RetainSummary *Summ = 0; +const ProgramState * +RetainCountChecker::updateSymbol(const ProgramState *state, SymbolRef sym, + RefVal V, ArgEffect E, RefVal::Kind &hasErr, + CheckerContext &C) const { + // In GC mode [... release] and [... retain] do nothing. + // In ARC mode they shouldn't exist at all, but we just ignore them. + bool IgnoreRetainMsg = C.isObjCGCEnabled(); + if (!IgnoreRetainMsg) + IgnoreRetainMsg = (bool)C.getASTContext().getLangOptions().ObjCAutoRefCount; - // FIXME: Better support for blocks. For now we stop tracking anything - // that is passed to blocks. - // FIXME: Need to handle variables that are "captured" by the block. - if (dyn_cast_or_null<BlockDataRegion>(L.getAsRegion())) { - Summ = Summaries.getPersistentStopSummary(); - } - else if (const FunctionDecl* FD = L.getAsFunctionDecl()) { - Summ = Summaries.getSummary(FD); + switch (E) { + default: break; + case IncRefMsg: E = IgnoreRetainMsg ? DoNothing : IncRef; break; + case DecRefMsg: E = IgnoreRetainMsg ? DoNothing : DecRef; break; + case MakeCollectable: E = C.isObjCGCEnabled() ? DecRef : DoNothing; break; + case NewAutoreleasePool: E = C.isObjCGCEnabled() ? DoNothing : + NewAutoreleasePool; break; } - else if (const CXXMemberCallExpr *me = dyn_cast<CXXMemberCallExpr>(CE)) { - if (const CXXMethodDecl *MD = me->getMethodDecl()) - Summ = Summaries.getSummary(MD); - else - Summ = Summaries.getDefaultSummary(); + + // Handle all use-after-releases. + if (!C.isObjCGCEnabled() && V.getKind() == RefVal::Released) { + V = V ^ RefVal::ErrorUseAfterRelease; + hasErr = V.getKind(); + return state->set<RefBindings>(sym, V); } - else - Summ = Summaries.getDefaultSummary(); - assert(Summ); - evalSummary(Dst, Eng, Builder, CE, - CallOrObjCMessage(CE, Builder.GetState(Pred)), - InstanceReceiver(), *Summ,L.getAsRegion(), - Pred, Builder.GetState(Pred)); -} + switch (E) { + case DecRefMsg: + case IncRefMsg: + case MakeCollectable: + llvm_unreachable("DecRefMsg/IncRefMsg/MakeCollectable already converted"); + return state; -void CFRefCount::evalObjCMessage(ExplodedNodeSet& Dst, - ExprEngine& Eng, - StmtNodeBuilder& Builder, - ObjCMessage msg, - ExplodedNode* Pred, - const GRState *state) { - RetainSummary *Summ = - msg.isInstanceMessage() - ? Summaries.getInstanceMethodSummary(msg, state,Pred->getLocationContext()) - : Summaries.getClassMethodSummary(msg); - - assert(Summ && "RetainSummary is null"); - evalSummary(Dst, Eng, Builder, msg.getOriginExpr(), - CallOrObjCMessage(msg, Builder.GetState(Pred)), - InstanceReceiver(msg, Pred->getLocationContext()), *Summ, NULL, - Pred, state); -} + case Dealloc: + // Any use of -dealloc in GC is *bad*. + if (C.isObjCGCEnabled()) { + V = V ^ RefVal::ErrorDeallocGC; + hasErr = V.getKind(); + break; + } -namespace { -class StopTrackingCallback : public SymbolVisitor { - const GRState *state; -public: - StopTrackingCallback(const GRState *st) : state(st) {} - const GRState *getState() const { return state; } + switch (V.getKind()) { + default: + llvm_unreachable("Invalid RefVal state for an explicit dealloc."); + break; + case RefVal::Owned: + // The object immediately transitions to the released state. + V = V ^ RefVal::Released; + V.clearCounts(); + return state->set<RefBindings>(sym, V); + case RefVal::NotOwned: + V = V ^ RefVal::ErrorDeallocNotOwned; + hasErr = V.getKind(); + break; + } + break; - bool VisitSymbol(SymbolRef sym) { - state = state->remove<RefBindings>(sym); - return true; - } -}; -} // end anonymous namespace + case NewAutoreleasePool: + assert(!C.isObjCGCEnabled()); + return state->add<AutoreleaseStack>(sym); + case MayEscape: + if (V.getKind() == RefVal::Owned) { + V = V ^ RefVal::NotOwned; + break; + } -void CFRefCount::evalBind(StmtNodeBuilderRef& B, SVal location, SVal val) { - // Are we storing to something that causes the value to "escape"? - bool escapes = false; + // Fall-through. - // A value escapes in three possible cases (this may change): - // - // (1) we are binding to something that is not a memory region. - // (2) we are binding to a memregion that does not have stack storage - // (3) we are binding to a memregion with stack storage that the store - // does not understand. - const GRState *state = B.getState(); + case DoNothing: + return state; - if (!isa<loc::MemRegionVal>(location)) - escapes = true; - else { - const MemRegion* R = cast<loc::MemRegionVal>(location).getRegion(); - escapes = !R->hasStackStorage(); + case Autorelease: + if (C.isObjCGCEnabled()) + return state; - if (!escapes) { - // To test (3), generate a new state with the binding removed. If it is - // the same state, then it escapes (since the store cannot represent - // the binding). - escapes = (state == (state->bindLoc(cast<Loc>(location), UnknownVal()))); - } + // Update the autorelease counts. + state = SendAutorelease(state, ARCountFactory, sym); + V = V.autorelease(); + break; + + case StopTracking: + return state->remove<RefBindings>(sym); + + case IncRef: + switch (V.getKind()) { + default: + llvm_unreachable("Invalid RefVal state for a retain."); + break; + case RefVal::Owned: + case RefVal::NotOwned: + V = V + 1; + break; + case RefVal::Released: + // Non-GC cases are handled above. + assert(C.isObjCGCEnabled()); + V = (V ^ RefVal::Owned) + 1; + break; + } + break; + + case SelfOwn: + V = V ^ RefVal::NotOwned; + // Fall-through. + case DecRef: + case DecRefBridgedTransfered: + switch (V.getKind()) { + default: + // case 'RefVal::Released' handled above. + llvm_unreachable("Invalid RefVal state for a release."); + break; + + case RefVal::Owned: + assert(V.getCount() > 0); + if (V.getCount() == 1) + V = V ^ (E == DecRefBridgedTransfered ? + RefVal::NotOwned : RefVal::Released); + V = V - 1; + break; + + case RefVal::NotOwned: + if (V.getCount() > 0) + V = V - 1; + else { + V = V ^ RefVal::ErrorReleaseNotOwned; + hasErr = V.getKind(); + } + break; + + case RefVal::Released: + // Non-GC cases are handled above. + assert(C.isObjCGCEnabled()); + V = V ^ RefVal::ErrorUseAfterRelease; + hasErr = V.getKind(); + break; + } + break; } + return state->set<RefBindings>(sym, V); +} - // If our store can represent the binding and we aren't storing to something - // that doesn't have local storage then just return and have the simulation - // state continue as is. - if (!escapes) +void RetainCountChecker::processNonLeakError(const ProgramState *St, + SourceRange ErrorRange, + RefVal::Kind ErrorKind, + SymbolRef Sym, + CheckerContext &C) const { + ExplodedNode *N = C.generateSink(St); + if (!N) + return; + + CFRefBug *BT; + switch (ErrorKind) { + default: + llvm_unreachable("Unhandled error."); return; + case RefVal::ErrorUseAfterRelease: + if (!useAfterRelease) + useAfterRelease.reset(new UseAfterRelease()); + BT = &*useAfterRelease; + break; + case RefVal::ErrorReleaseNotOwned: + if (!releaseNotOwned) + releaseNotOwned.reset(new BadRelease()); + BT = &*releaseNotOwned; + break; + case RefVal::ErrorDeallocGC: + if (!deallocGC) + deallocGC.reset(new DeallocGC()); + BT = &*deallocGC; + break; + case RefVal::ErrorDeallocNotOwned: + if (!deallocNotOwned) + deallocNotOwned.reset(new DeallocNotOwned()); + BT = &*deallocNotOwned; + break; + } - // Otherwise, find all symbols referenced by 'val' that we are tracking - // and stop tracking them. - B.MakeNode(state->scanReachableSymbols<StopTrackingCallback>(val).getState()); + assert(BT); + CFRefReport *report = new CFRefReport(*BT, C.getASTContext().getLangOptions(), + C.isObjCGCEnabled(), SummaryLog, + N, Sym); + report->addRange(ErrorRange); + C.EmitReport(report); } - // Return statements. +//===----------------------------------------------------------------------===// +// Handle the return values of retain-count-related functions. +//===----------------------------------------------------------------------===// + +bool RetainCountChecker::evalCall(const CallExpr *CE, CheckerContext &C) const { + // Get the callee. We're only interested in simple C functions. + const ProgramState *state = C.getState(); + const Expr *Callee = CE->getCallee(); + SVal L = state->getSVal(Callee); -void CFRefCount::evalReturn(ExplodedNodeSet& Dst, - ExprEngine& Eng, - StmtNodeBuilder& Builder, - const ReturnStmt* S, - ExplodedNode* Pred) { + const FunctionDecl *FD = L.getAsFunctionDecl(); + if (!FD) + return false; + + IdentifierInfo *II = FD->getIdentifier(); + if (!II) + return false; + + // For now, we're only handling the functions that return aliases of their + // arguments: CFRetain and CFMakeCollectable (and their families). + // Eventually we should add other functions we can model entirely, + // such as CFRelease, which don't invalidate their arguments or globals. + if (CE->getNumArgs() != 1) + return false; + + // Get the name of the function. + StringRef FName = II->getName(); + FName = FName.substr(FName.find_first_not_of('_')); + + // See if it's one of the specific functions we know how to eval. + bool canEval = false; + + QualType ResultTy = FD->getResultType(); + if (ResultTy->isObjCIdType()) { + // Handle: id NSMakeCollectable(CFTypeRef) + canEval = II->isStr("NSMakeCollectable"); + } else if (ResultTy->isPointerType()) { + // Handle: (CF|CG)Retain + // CFMakeCollectable + // It's okay to be a little sloppy here (CGMakeCollectable doesn't exist). + if (cocoa::isRefType(ResultTy, "CF", FName) || + cocoa::isRefType(ResultTy, "CG", FName)) { + canEval = isRetain(FD, FName) || isMakeCollectable(FD, FName); + } + } + + if (!canEval) + return false; + + // Bind the return value. + SVal RetVal = state->getSVal(CE->getArg(0)); + if (RetVal.isUnknown()) { + // If the receiver is unknown, conjure a return value. + SValBuilder &SVB = C.getSValBuilder(); + unsigned Count = C.getCurrentBlockCount(); + SVal RetVal = SVB.getConjuredSymbolVal(0, CE, ResultTy, Count); + } + state = state->BindExpr(CE, RetVal, false); + + // FIXME: This should not be necessary, but otherwise the argument seems to be + // considered alive during the next statement. + if (const MemRegion *ArgRegion = RetVal.getAsRegion()) { + // Save the refcount status of the argument. + SymbolRef Sym = RetVal.getAsLocSymbol(); + RefBindings::data_type *Binding = 0; + if (Sym) + Binding = state->get<RefBindings>(Sym); + + // Invalidate the argument region. + unsigned Count = C.getCurrentBlockCount(); + state = state->invalidateRegions(ArgRegion, CE, Count); + + // Restore the refcount status of the argument. + if (Binding) + state = state->set<RefBindings>(Sym, *Binding); + } + + C.addTransition(state); + return true; +} + +//===----------------------------------------------------------------------===// +// Handle return statements. +//===----------------------------------------------------------------------===// - const Expr* RetE = S->getRetValue(); +void RetainCountChecker::checkPreStmt(const ReturnStmt *S, + CheckerContext &C) const { + const Expr *RetE = S->getRetValue(); if (!RetE) return; - const GRState *state = Builder.GetState(Pred); + const ProgramState *state = C.getState(); SymbolRef Sym = state->getSValAsScalarOrLoc(RetE).getAsLocSymbol(); - if (!Sym) return; // Get the reference count binding (if any). - const RefVal* T = state->get<RefBindings>(Sym); - + const RefVal *T = state->get<RefBindings>(Sym); if (!T) return; @@ -2951,7 +3083,7 @@ void CFRefCount::evalReturn(ExplodedNodeSet& Dst, switch (X.getKind()) { case RefVal::Owned: { unsigned cnt = X.getCount(); - assert (cnt > 0); + assert(cnt > 0); X.setCount(cnt - 1); X = X ^ RefVal::ReturnedOwned; break; @@ -2975,21 +3107,25 @@ void CFRefCount::evalReturn(ExplodedNodeSet& Dst, // Update the binding. state = state->set<RefBindings>(Sym, X); - Pred = Builder.MakeNode(Dst, S, Pred, state); + ExplodedNode *Pred = C.generateNode(state); + + // At this point we have updated the state properly. + // Everything after this is merely checking to see if the return value has + // been over- or under-retained. // Did we cache out? if (!Pred) return; // Update the autorelease counts. - static unsigned autoreleasetag = 0; - GenericNodeBuilderRefCount Bd(Builder, S, &autoreleasetag); - bool stop = false; - llvm::tie(Pred, state) = HandleAutoreleaseCounts(state , Bd, Pred, Eng, Sym, - X, stop); + static SimpleProgramPointTag + AutoreleaseTag("RetainCountChecker : Autorelease"); + GenericNodeBuilderRefCount Bd(C, &AutoreleaseTag); + llvm::tie(Pred, state) = handleAutoreleaseCounts(state, Bd, Pred, + C.getEngine(), Sym, X); // Did we cache out? - if (!Pred || stop) + if (!Pred) return; // Get the updated binding. @@ -2998,36 +3134,37 @@ void CFRefCount::evalReturn(ExplodedNodeSet& Dst, X = *T; // Consult the summary of the enclosing method. - Decl const *CD = &Pred->getCodeDecl(); + RetainSummaryManager &Summaries = getSummaryManager(C); + const Decl *CD = &Pred->getCodeDecl(); - if (const ObjCMethodDecl* MD = dyn_cast<ObjCMethodDecl>(CD)) { - const RetainSummary &Summ = *Summaries.getMethodSummary(MD); - return evalReturnWithRetEffect(Dst, Eng, Builder, S, - Pred, Summ.getRetEffect(), X, - Sym, state); + if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(CD)) { + // Unlike regular functions, /all/ ObjC methods are assumed to always + // follow Cocoa retain-count conventions, not just those with special + // names or attributes. + const RetainSummary *Summ = Summaries.getMethodSummary(MD); + RetEffect RE = Summ ? Summ->getRetEffect() : RetEffect::MakeNoRet(); + checkReturnWithRetEffect(S, C, Pred, RE, X, Sym, state); } if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(CD)) { if (!isa<CXXMethodDecl>(FD)) if (const RetainSummary *Summ = Summaries.getSummary(FD)) - return evalReturnWithRetEffect(Dst, Eng, Builder, S, - Pred, Summ->getRetEffect(), X, - Sym, state); + checkReturnWithRetEffect(S, C, Pred, Summ->getRetEffect(), X, + Sym, state); } } -void CFRefCount::evalReturnWithRetEffect(ExplodedNodeSet &Dst, - ExprEngine &Eng, - StmtNodeBuilder &Builder, - const ReturnStmt *S, - ExplodedNode *Pred, - RetEffect RE, RefVal X, - SymbolRef Sym, const GRState *state) { +void RetainCountChecker::checkReturnWithRetEffect(const ReturnStmt *S, + CheckerContext &C, + ExplodedNode *Pred, + RetEffect RE, RefVal X, + SymbolRef Sym, + const ProgramState *state) const { // Any leaks or other errors? if (X.isReturnedOwned() && X.getCount() == 0) { if (RE.getKind() != RetEffect::NoRet) { bool hasError = false; - if (isGCEnabled() && RE.getObjKind() == RetEffect::ObjC) { + if (C.isObjCGCEnabled() && RE.getObjKind() == RetEffect::ObjC) { // Things are more complicated with garbage collection. If the // returned object is suppose to be an Objective-C object, we have // a leak (as the caller expects a GC'ed object) because no @@ -3045,46 +3182,88 @@ void CFRefCount::evalReturnWithRetEffect(ExplodedNodeSet &Dst, if (hasError) { // Generate an error node. - static int ReturnOwnLeakTag = 0; state = state->set<RefBindings>(Sym, X); - ExplodedNode *N = - Builder.generateNode(PostStmt(S, Pred->getLocationContext(), - &ReturnOwnLeakTag), state, Pred); + + static SimpleProgramPointTag + ReturnOwnLeakTag("RetainCountChecker : ReturnsOwnLeak"); + ExplodedNode *N = C.generateNode(state, Pred, &ReturnOwnLeakTag); if (N) { + const LangOptions &LOpts = C.getASTContext().getLangOptions(); + bool GCEnabled = C.isObjCGCEnabled(); CFRefReport *report = - new CFRefLeakReport(*static_cast<CFRefBug*>(leakAtReturn), *this, - N, Sym, Eng); - BR->EmitReport(report); + new CFRefLeakReport(*getLeakAtReturnBug(LOpts, GCEnabled), + LOpts, GCEnabled, SummaryLog, + N, Sym, C.getEngine()); + C.EmitReport(report); } } } - return; - } - - if (X.isReturnedNotOwned()) { + } else if (X.isReturnedNotOwned()) { if (RE.isOwned()) { // Trying to return a not owned object to a caller expecting an // owned object. - - static int ReturnNotOwnedForOwnedTag = 0; state = state->set<RefBindings>(Sym, X ^ RefVal::ErrorReturnedNotOwned); - if (ExplodedNode *N = - Builder.generateNode(PostStmt(S, Pred->getLocationContext(), - &ReturnNotOwnedForOwnedTag), - state, Pred)) { - CFRefReport *report = - new CFRefReport(*static_cast<CFRefBug*>(returnNotOwnedForOwned), - *this, N, Sym); - BR->EmitReport(report); + + static SimpleProgramPointTag + ReturnNotOwnedTag("RetainCountChecker : ReturnNotOwnedForOwned"); + ExplodedNode *N = C.generateNode(state, Pred, &ReturnNotOwnedTag); + if (N) { + if (!returnNotOwnedForOwned) + returnNotOwnedForOwned.reset(new ReturnedNotOwnedForOwned()); + + CFRefReport *report = + new CFRefReport(*returnNotOwnedForOwned, + C.getASTContext().getLangOptions(), + C.isObjCGCEnabled(), SummaryLog, N, Sym); + C.EmitReport(report); } } } } -// Assumptions. +//===----------------------------------------------------------------------===// +// Check various ways a symbol can be invalidated. +//===----------------------------------------------------------------------===// + +void RetainCountChecker::checkBind(SVal loc, SVal val, const Stmt *S, + CheckerContext &C) const { + // Are we storing to something that causes the value to "escape"? + bool escapes = true; + + // A value escapes in three possible cases (this may change): + // + // (1) we are binding to something that is not a memory region. + // (2) we are binding to a memregion that does not have stack storage + // (3) we are binding to a memregion with stack storage that the store + // does not understand. + const ProgramState *state = C.getState(); + + if (loc::MemRegionVal *regionLoc = dyn_cast<loc::MemRegionVal>(&loc)) { + escapes = !regionLoc->getRegion()->hasStackStorage(); + + if (!escapes) { + // To test (3), generate a new state with the binding added. If it is + // the same state, then it escapes (since the store cannot represent + // the binding). + escapes = (state == (state->bindLoc(*regionLoc, val))); + } + } + + // If our store can represent the binding and we aren't storing to something + // that doesn't have local storage then just return and have the simulation + // state continue as is. + if (!escapes) + return; + + // Otherwise, find all symbols referenced by 'val' that we are tracking + // and stop tracking them. + state = state->scanReachableSymbols<StopTrackingCallback>(val).getState(); + C.addTransition(state); +} -const GRState* CFRefCount::evalAssume(const GRState *state, - SVal Cond, bool Assumption) { +const ProgramState *RetainCountChecker::evalAssume(const ProgramState *state, + SVal Cond, + bool Assumption) const { // FIXME: We may add to the interface of evalAssume the list of symbols // whose assumptions have changed. For now we just iterate through the @@ -3098,9 +3277,9 @@ const GRState* CFRefCount::evalAssume(const GRState *state, return state; bool changed = false; - RefBindings::Factory& RefBFactory = state->get_context<RefBindings>(); + RefBindings::Factory &RefBFactory = state->get_context<RefBindings>(); - for (RefBindings::iterator I=B.begin(), E=B.end(); I!=E; ++I) { + for (RefBindings::iterator I = B.begin(), E = B.end(); I != E; ++I) { // Check if the symbol is null (or equal to any constant). // If this is the case, stop tracking the symbol. if (state->getSymVal(I.getKey())) { @@ -3115,161 +3294,48 @@ const GRState* CFRefCount::evalAssume(const GRState *state, return state; } -const GRState * CFRefCount::Update(const GRState * state, SymbolRef sym, - RefVal V, ArgEffect E, - RefVal::Kind& hasErr) { - - // In GC mode [... release] and [... retain] do nothing. - switch (E) { - default: break; - case IncRefMsg: E = isARCorGCEnabled() ? DoNothing : IncRef; break; - case DecRefMsg: E = isARCorGCEnabled() ? DoNothing : DecRef; break; - case MakeCollectable: E = isGCEnabled() ? DecRef : DoNothing; break; - case NewAutoreleasePool: E = isGCEnabled() ? DoNothing : - NewAutoreleasePool; break; - } +const ProgramState * +RetainCountChecker::checkRegionChanges(const ProgramState *state, + const StoreManager::InvalidatedSymbols *invalidated, + ArrayRef<const MemRegion *> ExplicitRegions, + ArrayRef<const MemRegion *> Regions) const { + if (!invalidated) + return state; - // Handle all use-after-releases. - if (!isGCEnabled() && V.getKind() == RefVal::Released) { - V = V ^ RefVal::ErrorUseAfterRelease; - hasErr = V.getKind(); - return state->set<RefBindings>(sym, V); + llvm::SmallPtrSet<SymbolRef, 8> WhitelistedSymbols; + for (ArrayRef<const MemRegion *>::iterator I = ExplicitRegions.begin(), + E = ExplicitRegions.end(); I != E; ++I) { + if (const SymbolicRegion *SR = (*I)->StripCasts()->getAs<SymbolicRegion>()) + WhitelistedSymbols.insert(SR->getSymbol()); } - switch (E) { - case DecRefMsg: - case IncRefMsg: - case MakeCollectable: - assert(false && - "DecRefMsg/IncRefMsg/MakeCollectable already transformed"); - return state; - - case Dealloc: - // Any use of -dealloc in GC is *bad*. - if (isGCEnabled()) { - V = V ^ RefVal::ErrorDeallocGC; - hasErr = V.getKind(); - break; - } - - switch (V.getKind()) { - default: - assert(false && "Invalid case."); - case RefVal::Owned: - // The object immediately transitions to the released state. - V = V ^ RefVal::Released; - V.clearCounts(); - return state->set<RefBindings>(sym, V); - case RefVal::NotOwned: - V = V ^ RefVal::ErrorDeallocNotOwned; - hasErr = V.getKind(); - break; - } - break; - - case NewAutoreleasePool: - assert(!isGCEnabled()); - return state->add<AutoreleaseStack>(sym); - - case MayEscape: - if (V.getKind() == RefVal::Owned) { - V = V ^ RefVal::NotOwned; - break; - } - - // Fall-through. - - case DoNothingByRef: - case DoNothing: - return state; - - case Autorelease: - if (isGCEnabled()) - return state; - - // Update the autorelease counts. - state = SendAutorelease(state, ARCountFactory, sym); - V = V.autorelease(); - break; - - case StopTracking: - return state->remove<RefBindings>(sym); - - case IncRef: - switch (V.getKind()) { - default: - assert(false); - - case RefVal::Owned: - case RefVal::NotOwned: - V = V + 1; - break; - case RefVal::Released: - // Non-GC cases are handled above. - assert(isGCEnabled()); - V = (V ^ RefVal::Owned) + 1; - break; - } - break; - - case SelfOwn: - V = V ^ RefVal::NotOwned; - // Fall-through. - case DecRef: - case DecRefBridgedTransfered: - switch (V.getKind()) { - default: - // case 'RefVal::Released' handled above. - assert (false); - - case RefVal::Owned: - assert(V.getCount() > 0); - if (V.getCount() == 1) - V = V ^ (E == DecRefBridgedTransfered ? - RefVal::NotOwned : RefVal::Released); - V = V - 1; - break; - - case RefVal::NotOwned: - if (V.getCount() > 0) - V = V - 1; - else { - V = V ^ RefVal::ErrorReleaseNotOwned; - hasErr = V.getKind(); - } - break; - - case RefVal::Released: - // Non-GC cases are handled above. - assert(isGCEnabled()); - V = V ^ RefVal::ErrorUseAfterRelease; - hasErr = V.getKind(); - break; - } - break; + for (StoreManager::InvalidatedSymbols::const_iterator I=invalidated->begin(), + E = invalidated->end(); I!=E; ++I) { + SymbolRef sym = *I; + if (WhitelistedSymbols.count(sym)) + continue; + // Remove any existing reference-count binding. + state = state->remove<RefBindings>(sym); } - return state->set<RefBindings>(sym, V); + return state; } //===----------------------------------------------------------------------===// // Handle dead symbols and end-of-path. //===----------------------------------------------------------------------===// -std::pair<ExplodedNode*, const GRState *> -CFRefCount::HandleAutoreleaseCounts(const GRState * state, - GenericNodeBuilderRefCount Bd, - ExplodedNode* Pred, - ExprEngine &Eng, - SymbolRef Sym, RefVal V, bool &stop) { - +std::pair<ExplodedNode *, const ProgramState *> +RetainCountChecker::handleAutoreleaseCounts(const ProgramState *state, + GenericNodeBuilderRefCount Bd, + ExplodedNode *Pred, ExprEngine &Eng, + SymbolRef Sym, RefVal V) const { unsigned ACnt = V.getAutoreleaseCount(); - stop = false; // No autorelease counts? Nothing to be done. if (!ACnt) return std::make_pair(Pred, state); - assert(!isGCEnabled() && "Autorelease counts in GC mode?"); + assert(!Eng.isObjCGCEnabled() && "Autorelease counts in GC mode?"); unsigned Cnt = V.getCount(); // FIXME: Handle sending 'autorelease' to already released object. @@ -3284,48 +3350,54 @@ CFRefCount::HandleAutoreleaseCounts(const GRState * state, V = V ^ RefVal::ReturnedNotOwned; else V = V ^ RefVal::NotOwned; - } - else { + } else { V.setCount(Cnt - ACnt); V.setAutoreleaseCount(0); } state = state->set<RefBindings>(Sym, V); ExplodedNode *N = Bd.MakeNode(state, Pred); - stop = (N == 0); + if (N == 0) + state = 0; return std::make_pair(N, state); } // Woah! More autorelease counts then retain counts left. // Emit hard error. - stop = true; V = V ^ RefVal::ErrorOverAutorelease; state = state->set<RefBindings>(Sym, V); if (ExplodedNode *N = Bd.MakeNode(state, Pred)) { N->markAsSink(); - std::string sbuf; - llvm::raw_string_ostream os(sbuf); + llvm::SmallString<128> sbuf; + llvm::raw_svector_ostream os(sbuf); os << "Object over-autoreleased: object was sent -autorelease "; if (V.getAutoreleaseCount() > 1) os << V.getAutoreleaseCount() << " times "; os << "but the object has a +" << V.getCount() << " retain count"; + if (!overAutorelease) + overAutorelease.reset(new OverAutorelease()); + + const LangOptions &LOpts = Eng.getContext().getLangOptions(); CFRefReport *report = - new CFRefReport(*static_cast<CFRefBug*>(overAutorelease), - *this, N, Sym, os.str()); - BR->EmitReport(report); + new CFRefReport(*overAutorelease, LOpts, /* GCEnabled = */ false, + SummaryLog, N, Sym, os.str()); + Eng.getBugReporter().EmitReport(report); } - return std::make_pair((ExplodedNode*)0, state); + return std::make_pair((ExplodedNode *)0, (const ProgramState *)0); } -const GRState * -CFRefCount::HandleSymbolDeath(const GRState * state, SymbolRef sid, RefVal V, - llvm::SmallVectorImpl<SymbolRef> &Leaked) { - - bool hasLeak = V.isOwned() || - ((V.isNotOwned() || V.isReturnedOwned()) && V.getCount() > 0); +const ProgramState * +RetainCountChecker::handleSymbolDeath(const ProgramState *state, + SymbolRef sid, RefVal V, + SmallVectorImpl<SymbolRef> &Leaked) const { + bool hasLeak = false; + if (V.isOwned()) + hasLeak = true; + else if (V.isNotOwned() || V.isReturnedOwned()) + hasLeak = (V.getCount() > 0); if (!hasLeak) return state->remove<RefBindings>(sid); @@ -3334,13 +3406,11 @@ CFRefCount::HandleSymbolDeath(const GRState * state, SymbolRef sid, RefVal V, return state->set<RefBindings>(sid, V ^ RefVal::ErrorLeak); } -ExplodedNode* -CFRefCount::ProcessLeaks(const GRState * state, - llvm::SmallVectorImpl<SymbolRef> &Leaked, - GenericNodeBuilderRefCount &Builder, - ExprEngine& Eng, - ExplodedNode *Pred) { - +ExplodedNode * +RetainCountChecker::processLeaks(const ProgramState *state, + SmallVectorImpl<SymbolRef> &Leaked, + GenericNodeBuilderRefCount &Builder, + ExprEngine &Eng, ExplodedNode *Pred) const { if (Leaked.empty()) return Pred; @@ -3348,85 +3418,94 @@ CFRefCount::ProcessLeaks(const GRState * state, ExplodedNode *N = Builder.MakeNode(state, Pred); if (N) { - for (llvm::SmallVectorImpl<SymbolRef>::iterator + for (SmallVectorImpl<SymbolRef>::iterator I = Leaked.begin(), E = Leaked.end(); I != E; ++I) { - CFRefBug *BT = static_cast<CFRefBug*>(Pred ? leakWithinFunction - : leakAtReturn); + const LangOptions &LOpts = Eng.getContext().getLangOptions(); + bool GCEnabled = Eng.isObjCGCEnabled(); + CFRefBug *BT = Pred ? getLeakWithinFunctionBug(LOpts, GCEnabled) + : getLeakAtReturnBug(LOpts, GCEnabled); assert(BT && "BugType not initialized."); - CFRefLeakReport* report = new CFRefLeakReport(*BT, *this, N, *I, Eng); - BR->EmitReport(report); + + CFRefLeakReport *report = new CFRefLeakReport(*BT, LOpts, GCEnabled, + SummaryLog, N, *I, Eng); + Eng.getBugReporter().EmitReport(report); } } return N; } -void CFRefCount::evalEndPath(ExprEngine& Eng, - EndOfFunctionNodeBuilder& Builder) { - - const GRState *state = Builder.getState(); +void RetainCountChecker::checkEndPath(EndOfFunctionNodeBuilder &Builder, + ExprEngine &Eng) const { + const ProgramState *state = Builder.getState(); GenericNodeBuilderRefCount Bd(Builder); RefBindings B = state->get<RefBindings>(); - ExplodedNode *Pred = 0; + ExplodedNode *Pred = Builder.getPredecessor(); for (RefBindings::iterator I = B.begin(), E = B.end(); I != E; ++I) { - bool stop = false; - llvm::tie(Pred, state) = HandleAutoreleaseCounts(state, Bd, Pred, Eng, - (*I).first, - (*I).second, stop); - - if (stop) + llvm::tie(Pred, state) = handleAutoreleaseCounts(state, Bd, Pred, Eng, + I->first, I->second); + if (!state) return; } B = state->get<RefBindings>(); - llvm::SmallVector<SymbolRef, 10> Leaked; + SmallVector<SymbolRef, 10> Leaked; for (RefBindings::iterator I = B.begin(), E = B.end(); I != E; ++I) - state = HandleSymbolDeath(state, (*I).first, (*I).second, Leaked); + state = handleSymbolDeath(state, I->first, I->second, Leaked); - ProcessLeaks(state, Leaked, Bd, Eng, Pred); + processLeaks(state, Leaked, Bd, Eng, Pred); +} + +const ProgramPointTag * +RetainCountChecker::getDeadSymbolTag(SymbolRef sym) const { + const SimpleProgramPointTag *&tag = DeadSymbolTags[sym]; + if (!tag) { + llvm::SmallString<64> buf; + llvm::raw_svector_ostream out(buf); + out << "RetainCountChecker : Dead Symbol : " << sym->getSymbolID(); + tag = new SimpleProgramPointTag(out.str()); + } + return tag; } -void CFRefCount::evalDeadSymbols(ExplodedNodeSet& Dst, - ExprEngine& Eng, - StmtNodeBuilder& Builder, - ExplodedNode* Pred, - const GRState* state, - SymbolReaper& SymReaper) { - const Stmt *S = Builder.getStmt(); +void RetainCountChecker::checkDeadSymbols(SymbolReaper &SymReaper, + CheckerContext &C) const { + ExprEngine &Eng = C.getEngine(); + ExplodedNode *Pred = C.getPredecessor(); + + const ProgramState *state = C.getState(); RefBindings B = state->get<RefBindings>(); // Update counts from autorelease pools for (SymbolReaper::dead_iterator I = SymReaper.dead_begin(), E = SymReaper.dead_end(); I != E; ++I) { SymbolRef Sym = *I; - if (const RefVal* T = B.lookup(Sym)){ + if (const RefVal *T = B.lookup(Sym)){ // Use the symbol as the tag. // FIXME: This might not be as unique as we would like. - GenericNodeBuilderRefCount Bd(Builder, S, Sym); - bool stop = false; - llvm::tie(Pred, state) = HandleAutoreleaseCounts(state, Bd, Pred, Eng, - Sym, *T, stop); - if (stop) + GenericNodeBuilderRefCount Bd(C, getDeadSymbolTag(Sym)); + llvm::tie(Pred, state) = handleAutoreleaseCounts(state, Bd, Pred, Eng, + Sym, *T); + if (!state) return; } } B = state->get<RefBindings>(); - llvm::SmallVector<SymbolRef, 10> Leaked; + SmallVector<SymbolRef, 10> Leaked; for (SymbolReaper::dead_iterator I = SymReaper.dead_begin(), E = SymReaper.dead_end(); I != E; ++I) { - if (const RefVal* T = B.lookup(*I)) - state = HandleSymbolDeath(state, *I, *T, Leaked); + if (const RefVal *T = B.lookup(*I)) + state = handleSymbolDeath(state, *I, *T, Leaked); } - static unsigned LeakPPTag = 0; { - GenericNodeBuilderRefCount Bd(Builder, S, &LeakPPTag); - Pred = ProcessLeaks(state, Leaked, Bd, Eng, Pred); + GenericNodeBuilderRefCount Bd(C, this); + Pred = processLeaks(state, Leaked, Bd, Eng, Pred); } // Did we cache out? @@ -3434,256 +3513,76 @@ void CFRefCount::evalDeadSymbols(ExplodedNodeSet& Dst, return; // Now generate a new node that nukes the old bindings. - RefBindings::Factory& F = state->get_context<RefBindings>(); + RefBindings::Factory &F = state->get_context<RefBindings>(); for (SymbolReaper::dead_iterator I = SymReaper.dead_begin(), - E = SymReaper.dead_end(); I!=E; ++I) B = F.remove(B, *I); + E = SymReaper.dead_end(); I != E; ++I) + B = F.remove(B, *I); state = state->set<RefBindings>(B); - Builder.MakeNode(Dst, S, Pred, state); -} - -void CFRefCount::ProcessNonLeakError(ExplodedNodeSet& Dst, - StmtNodeBuilder& Builder, - const Expr* NodeExpr, - SourceRange ErrorRange, - ExplodedNode* Pred, - const GRState* St, - RefVal::Kind hasErr, SymbolRef Sym) { - Builder.BuildSinks = true; - ExplodedNode *N = Builder.MakeNode(Dst, NodeExpr, Pred, St); - - if (!N) - return; - - CFRefBug *BT = 0; - - switch (hasErr) { - default: - assert(false && "Unhandled error."); - return; - case RefVal::ErrorUseAfterRelease: - BT = static_cast<CFRefBug*>(useAfterRelease); - break; - case RefVal::ErrorReleaseNotOwned: - BT = static_cast<CFRefBug*>(releaseNotOwned); - break; - case RefVal::ErrorDeallocGC: - BT = static_cast<CFRefBug*>(deallocGC); - break; - case RefVal::ErrorDeallocNotOwned: - BT = static_cast<CFRefBug*>(deallocNotOwned); - break; - } - - CFRefReport *report = new CFRefReport(*BT, *this, N, Sym); - report->addRange(ErrorRange); - BR->EmitReport(report); + C.generateNode(state, Pred); } //===----------------------------------------------------------------------===// -// Pieces of the retain/release checker implemented using a CheckerVisitor. -// More pieces of the retain/release checker will be migrated to this interface -// (ideally, all of it some day). +// Debug printing of refcount bindings and autorelease pools. //===----------------------------------------------------------------------===// -namespace { -class RetainReleaseChecker - : public Checker< check::PostStmt<BlockExpr>, - check::PostStmt<CastExpr>, - check::RegionChanges > { -public: - bool wantsRegionUpdate; - - RetainReleaseChecker() : wantsRegionUpdate(true) {} - - - void checkPostStmt(const BlockExpr *BE, CheckerContext &C) const; - - void checkPostStmt(const CastExpr *CE, CheckerContext &C) const; - - const GRState *checkRegionChanges(const GRState *state, - const StoreManager::InvalidatedSymbols *invalidated, - const MemRegion * const *begin, - const MemRegion * const *end) const; - - bool wantsRegionChangeUpdate(const GRState *state) const { - return wantsRegionUpdate; - } -}; -} // end anonymous namespace +static void PrintPool(raw_ostream &Out, SymbolRef Sym, + const ProgramState *State) { + Out << ' '; + if (Sym) + Out << Sym->getSymbolID(); + else + Out << "<pool>"; + Out << ":{"; -const GRState * -RetainReleaseChecker::checkRegionChanges(const GRState *state, - const StoreManager::InvalidatedSymbols *invalidated, - const MemRegion * const *begin, - const MemRegion * const *end) const { - if (!invalidated) - return state; + // Get the contents of the pool. + if (const ARCounts *Cnts = State->get<AutoreleasePoolContents>(Sym)) + for (ARCounts::iterator I = Cnts->begin(), E = Cnts->end(); I != E; ++I) + Out << '(' << I.getKey() << ',' << I.getData() << ')'; - for (StoreManager::InvalidatedSymbols::const_iterator I=invalidated->begin(), - E = invalidated->end(); I!=E; ++I) { - SymbolRef sym = *I; - if (WhitelistedSymbols.count(sym)) - continue; - // Remove any existing reference-count binding. - state = state->remove<RefBindings>(sym); - } - return state; + Out << '}'; } -void RetainReleaseChecker::checkPostStmt(const BlockExpr *BE, - CheckerContext &C) const { - - // Scan the BlockDecRefExprs for any object the retain/release checker - // may be tracking. - if (!BE->getBlockDecl()->hasCaptures()) - return; +static bool UsesAutorelease(const ProgramState *state) { + // A state uses autorelease if it allocated an autorelease pool or if it has + // objects in the caller's autorelease pool. + return !state->get<AutoreleaseStack>().isEmpty() || + state->get<AutoreleasePoolContents>(SymbolRef()); +} - const GRState *state = C.getState(); - const BlockDataRegion *R = - cast<BlockDataRegion>(state->getSVal(BE).getAsRegion()); +void RetainCountChecker::printState(raw_ostream &Out, const ProgramState *State, + const char *NL, const char *Sep) const { - BlockDataRegion::referenced_vars_iterator I = R->referenced_vars_begin(), - E = R->referenced_vars_end(); + RefBindings B = State->get<RefBindings>(); - if (I == E) - return; - - // FIXME: For now we invalidate the tracking of all symbols passed to blocks - // via captured variables, even though captured variables result in a copy - // and in implicit increment/decrement of a retain count. - llvm::SmallVector<const MemRegion*, 10> Regions; - const LocationContext *LC = C.getPredecessor()->getLocationContext(); - MemRegionManager &MemMgr = C.getSValBuilder().getRegionManager(); + if (!B.isEmpty()) + Out << Sep << NL; - for ( ; I != E; ++I) { - const VarRegion *VR = *I; - if (VR->getSuperRegion() == R) { - VR = MemMgr.getVarRegion(VR->getDecl(), LC); - } - Regions.push_back(VR); + for (RefBindings::iterator I = B.begin(), E = B.end(); I != E; ++I) { + Out << I->first << " : "; + I->second.print(Out); + Out << NL; } - state = - state->scanReachableSymbols<StopTrackingCallback>(Regions.data(), - Regions.data() + Regions.size()).getState(); - C.addTransition(state); -} + // Print the autorelease stack. + if (UsesAutorelease(State)) { + Out << Sep << NL << "AR pool stack:"; + ARStack Stack = State->get<AutoreleaseStack>(); -void RetainReleaseChecker::checkPostStmt(const CastExpr *CE, - CheckerContext &C) const { - const ObjCBridgedCastExpr *BE = dyn_cast<ObjCBridgedCastExpr>(CE); - if (!BE) - return; - - ArgEffect AE = IncRef; - - switch (BE->getBridgeKind()) { - case clang::OBC_Bridge: - // Do nothing. - return; - case clang::OBC_BridgeRetained: - AE = IncRef; - break; - case clang::OBC_BridgeTransfer: - AE = DecRefBridgedTransfered; - break; - } - - const GRState *state = C.getState(); - SymbolRef Sym = state->getSVal(CE).getAsLocSymbol(); - if (!Sym) - return; - const RefVal* T = state->get<RefBindings>(Sym); - if (!T) - return; + PrintPool(Out, SymbolRef(), State); // Print the caller's pool. + for (ARStack::iterator I = Stack.begin(), E = Stack.end(); I != E; ++I) + PrintPool(Out, *I, State); - // This is gross. Once the checker and CFRefCount are unified, - // this will go away. - CFRefCount &cf = static_cast<CFRefCount&>(C.getEngine().getTF()); - RefVal::Kind hasErr = (RefVal::Kind) 0; - state = cf.Update(state, Sym, *T, AE, hasErr); - - if (hasErr) { - - return; + Out << NL; } - - C.generateNode(state); } //===----------------------------------------------------------------------===// -// Transfer function creation for external clients. +// Checker registration. //===----------------------------------------------------------------------===// -void CFRefCount::RegisterChecks(ExprEngine& Eng) { - BugReporter &BR = Eng.getBugReporter(); - - useAfterRelease = new UseAfterRelease(this); - BR.Register(useAfterRelease); - - releaseNotOwned = new BadRelease(this); - BR.Register(releaseNotOwned); - - deallocGC = new DeallocGC(this); - BR.Register(deallocGC); - - deallocNotOwned = new DeallocNotOwned(this); - BR.Register(deallocNotOwned); - - overAutorelease = new OverAutorelease(this); - BR.Register(overAutorelease); - - returnNotOwnedForOwned = new ReturnedNotOwnedForOwned(this); - BR.Register(returnNotOwnedForOwned); - - // First register "return" leaks. - const char* name = 0; - - if (isGCEnabled()) - name = "Leak of returned object when using garbage collection"; - else if (getLangOptions().getGCMode() == LangOptions::HybridGC) - name = "Leak of returned object when not using garbage collection (GC) in " - "dual GC/non-GC code"; - else { - assert(getLangOptions().getGCMode() == LangOptions::NonGC); - name = "Leak of returned object"; - } - - // Leaks should not be reported if they are post-dominated by a sink. - leakAtReturn = new LeakAtReturn(this, name); - leakAtReturn->setSuppressOnSink(true); - BR.Register(leakAtReturn); - - // Second, register leaks within a function/method. - if (isGCEnabled()) - name = "Leak of object when using garbage collection"; - else if (getLangOptions().getGCMode() == LangOptions::HybridGC) - name = "Leak of object when not using garbage collection (GC) in " - "dual GC/non-GC code"; - else { - assert(getLangOptions().getGCMode() == LangOptions::NonGC); - name = "Leak"; - } - - // Leaks should not be reported if they are post-dominated by sinks. - leakWithinFunction = new LeakWithinFunction(this, name); - leakWithinFunction->setSuppressOnSink(true); - BR.Register(leakWithinFunction); - - // Save the reference to the BugReporter. - this->BR = &BR; - - // Register the RetainReleaseChecker with the ExprEngine object. - // Functionality in CFRefCount will be migrated to RetainReleaseChecker - // over time. - // FIXME: HACK! Remove TransferFuncs and turn all of CFRefCount into fully - // using the checker mechanism. - Eng.getCheckerManager().registerChecker<RetainReleaseChecker>(); +void ento::registerRetainCountChecker(CheckerManager &Mgr) { + Mgr.registerChecker<RetainCountChecker>(); } -TransferFuncs* ento::MakeCFRefCountTF(ASTContext& Ctx, bool GCEnabled, - const LangOptions& lopts) { - return new CFRefCount(Ctx, GCEnabled, lopts); -} diff --git a/lib/StaticAnalyzer/Checkers/ReturnPointerRangeChecker.cpp b/lib/StaticAnalyzer/Checkers/ReturnPointerRangeChecker.cpp index 1729b25a2979..e761bff85583 100644 --- a/lib/StaticAnalyzer/Checkers/ReturnPointerRangeChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/ReturnPointerRangeChecker.cpp @@ -33,7 +33,7 @@ public: void ReturnPointerRangeChecker::checkPreStmt(const ReturnStmt *RS, CheckerContext &C) const { - const GRState *state = C.getState(); + const ProgramState *state = C.getState(); const Expr *RetE = RS->getRetValue(); if (!RetE) @@ -58,8 +58,8 @@ void ReturnPointerRangeChecker::checkPreStmt(const ReturnStmt *RS, = C.getStoreManager().getSizeInElements(state, ER->getSuperRegion(), ER->getValueType()); - const GRState *StInBound = state->assumeInBound(Idx, NumElements, true); - const GRState *StOutBound = state->assumeInBound(Idx, NumElements, false); + const ProgramState *StInBound = state->assumeInBound(Idx, NumElements, true); + const ProgramState *StOutBound = state->assumeInBound(Idx, NumElements, false); if (StOutBound && !StInBound) { ExplodedNode *N = C.generateSink(StOutBound); @@ -78,8 +78,8 @@ void ReturnPointerRangeChecker::checkPreStmt(const ReturnStmt *RS, // reference is outside the range. // Generate a report for this bug. - RangedBugReport *report = - new RangedBugReport(*BT, BT->getDescription(), N); + BugReport *report = + new BugReport(*BT, BT->getDescription(), N); report->addRange(RetE->getSourceRange()); C.EmitReport(report); diff --git a/lib/StaticAnalyzer/Checkers/ReturnUndefChecker.cpp b/lib/StaticAnalyzer/Checkers/ReturnUndefChecker.cpp index 7c215b7cf318..e8c8d902a5e5 100644 --- a/lib/StaticAnalyzer/Checkers/ReturnUndefChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/ReturnUndefChecker.cpp @@ -50,11 +50,11 @@ void ReturnUndefChecker::checkPreStmt(const ReturnStmt *RS, BT.reset(new BuiltinBug("Garbage return value", "Undefined or garbage value returned to caller")); - EnhancedBugReport *report = - new EnhancedBugReport(*BT, BT->getDescription(), N); + BugReport *report = + new BugReport(*BT, BT->getDescription(), N); report->addRange(RetE->getSourceRange()); - report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, RetE); + report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, RetE)); C.EmitReport(report); } diff --git a/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp b/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp index 73ce359edc3e..91c4b96d695e 100644 --- a/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp @@ -17,7 +17,7 @@ #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/GRState.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" #include "clang/Basic/SourceManager.h" #include "llvm/ADT/SmallString.h" using namespace clang; @@ -35,12 +35,12 @@ public: private: void EmitStackError(CheckerContext &C, const MemRegion *R, const Expr *RetE) const; - static SourceRange GenName(llvm::raw_ostream &os, const MemRegion *R, + static SourceRange GenName(raw_ostream &os, const MemRegion *R, SourceManager &SM); }; } -SourceRange StackAddrEscapeChecker::GenName(llvm::raw_ostream &os, +SourceRange StackAddrEscapeChecker::GenName(raw_ostream &os, const MemRegion *R, SourceManager &SM) { // Get the base region, stripping away fields and elements. @@ -50,34 +50,39 @@ SourceRange StackAddrEscapeChecker::GenName(llvm::raw_ostream &os, // Check if the region is a compound literal. if (const CompoundLiteralRegion* CR = dyn_cast<CompoundLiteralRegion>(R)) { - const CompoundLiteralExpr* CL = CR->getLiteralExpr(); + const CompoundLiteralExpr *CL = CR->getLiteralExpr(); os << "stack memory associated with a compound literal " "declared on line " - << SM.getInstantiationLineNumber(CL->getLocStart()) + << SM.getExpansionLineNumber(CL->getLocStart()) << " returned to caller"; range = CL->getSourceRange(); } else if (const AllocaRegion* AR = dyn_cast<AllocaRegion>(R)) { - const Expr* ARE = AR->getExpr(); + const Expr *ARE = AR->getExpr(); SourceLocation L = ARE->getLocStart(); range = ARE->getSourceRange(); os << "stack memory allocated by call to alloca() on line " - << SM.getInstantiationLineNumber(L); + << SM.getExpansionLineNumber(L); } else if (const BlockDataRegion *BR = dyn_cast<BlockDataRegion>(R)) { const BlockDecl *BD = BR->getCodeRegion()->getDecl(); SourceLocation L = BD->getLocStart(); range = BD->getSourceRange(); os << "stack-allocated block declared on line " - << SM.getInstantiationLineNumber(L); + << SM.getExpansionLineNumber(L); } else if (const VarRegion *VR = dyn_cast<VarRegion>(R)) { os << "stack memory associated with local variable '" << VR->getString() << '\''; range = VR->getDecl()->getSourceRange(); } + else if (const CXXTempObjectRegion *TOR = dyn_cast<CXXTempObjectRegion>(R)) { + os << "stack memory associated with temporary object of type '" + << TOR->getValueType().getAsString() << '\''; + range = TOR->getExpr()->getSourceRange(); + } else { - assert(false && "Invalid region in ReturnStackAddressChecker."); + llvm_unreachable("Invalid region in ReturnStackAddressChecker."); } return range; @@ -99,7 +104,7 @@ void StackAddrEscapeChecker::EmitStackError(CheckerContext &C, const MemRegion * llvm::raw_svector_ostream os(buf); SourceRange range = GenName(os, R, C.getSourceManager()); os << " returned to caller"; - RangedBugReport *report = new RangedBugReport(*BT_returnstack, os.str(), N); + BugReport *report = new BugReport(*BT_returnstack, os.str(), N); report->addRange(RetE->getSourceRange()); if (range.isValid()) report->addRange(range); @@ -134,7 +139,7 @@ void StackAddrEscapeChecker::checkPreStmt(const ReturnStmt *RS, void StackAddrEscapeChecker::checkEndPath(EndOfFunctionNodeBuilder &B, ExprEngine &Eng) const { - const GRState *state = B.getState(); + const ProgramState *state = B.getState(); // Iterate over all bindings to global variables and see if it contains // a memory region in the stack space. @@ -143,7 +148,7 @@ void StackAddrEscapeChecker::checkEndPath(EndOfFunctionNodeBuilder &B, ExprEngine &Eng; const StackFrameContext *CurSFC; public: - llvm::SmallVector<std::pair<const MemRegion*, const MemRegion*>, 10> V; + SmallVector<std::pair<const MemRegion*, const MemRegion*>, 10> V; CallBack(ExprEngine &Eng, const LocationContext *LCtx) : Eng(Eng), CurSFC(LCtx->getCurrentStackFrame()) {} @@ -202,9 +207,9 @@ void StackAddrEscapeChecker::checkEndPath(EndOfFunctionNodeBuilder &B, Eng.getContext().getSourceManager()); os << " is still referred to by the global variable '"; const VarRegion *VR = cast<VarRegion>(cb.V[i].first->getBaseRegion()); - os << VR->getDecl()->getNameAsString() + os << *VR->getDecl() << "' upon returning to the caller. This will be a dangling reference"; - RangedBugReport *report = new RangedBugReport(*BT_stackleak, os.str(), N); + BugReport *report = new BugReport(*BT_stackleak, os.str(), N); if (range.isValid()) report->addRange(range); diff --git a/lib/StaticAnalyzer/Checkers/StreamChecker.cpp b/lib/StaticAnalyzer/Checkers/StreamChecker.cpp index 711c672c1448..1d14e9e15e42 100644 --- a/lib/StaticAnalyzer/Checkers/StreamChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/StreamChecker.cpp @@ -16,8 +16,8 @@ #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/GRState.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/GRStateTrait.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h" #include "llvm/ADT/ImmutableMap.h" @@ -96,9 +96,9 @@ private: void OpenFileAux(CheckerContext &C, const CallExpr *CE) const; - const GRState *CheckNullStream(SVal SV, const GRState *state, + const ProgramState *CheckNullStream(SVal SV, const ProgramState *state, CheckerContext &C) const; - const GRState *CheckDoubleClose(const CallExpr *CE, const GRState *state, + const ProgramState *CheckDoubleClose(const CallExpr *CE, const ProgramState *state, CheckerContext &C) const; }; @@ -107,15 +107,15 @@ private: namespace clang { namespace ento { template <> - struct GRStateTrait<StreamState> - : public GRStatePartialTrait<llvm::ImmutableMap<SymbolRef, StreamState> > { + struct ProgramStateTrait<StreamState> + : public ProgramStatePartialTrait<llvm::ImmutableMap<SymbolRef, StreamState> > { static void *GDMIndex() { static int x; return &x; } }; } } bool StreamChecker::evalCall(const CallExpr *CE, CheckerContext &C) const { - const GRState *state = C.getState(); + const ProgramState *state = C.getState(); const Expr *Callee = CE->getCallee(); SVal L = state->getSVal(Callee); const FunctionDecl *FD = L.getAsFunctionDecl(); @@ -221,8 +221,8 @@ void StreamChecker::Tmpfile(CheckerContext &C, const CallExpr *CE) const { } void StreamChecker::OpenFileAux(CheckerContext &C, const CallExpr *CE) const { - const GRState *state = C.getState(); - unsigned Count = C.getNodeBuilder().getCurrentBlockCount(); + const ProgramState *state = C.getState(); + unsigned Count = C.getCurrentBlockCount(); SValBuilder &svalBuilder = C.getSValBuilder(); DefinedSVal RetVal = cast<DefinedSVal>(svalBuilder.getConjuredSymbolVal(0, CE, Count)); @@ -231,7 +231,7 @@ void StreamChecker::OpenFileAux(CheckerContext &C, const CallExpr *CE) const { ConstraintManager &CM = C.getConstraintManager(); // Bifurcate the state into two: one with a valid FILE* pointer, the other // with a NULL. - const GRState *stateNotNull, *stateNull; + const ProgramState *stateNotNull, *stateNull; llvm::tie(stateNotNull, stateNull) = CM.assumeDual(state, RetVal); if (SymbolRef Sym = RetVal.getAsSymbol()) { @@ -247,25 +247,25 @@ void StreamChecker::OpenFileAux(CheckerContext &C, const CallExpr *CE) const { } void StreamChecker::Fclose(CheckerContext &C, const CallExpr *CE) const { - const GRState *state = CheckDoubleClose(CE, C.getState(), C); + const ProgramState *state = CheckDoubleClose(CE, C.getState(), C); if (state) C.addTransition(state); } void StreamChecker::Fread(CheckerContext &C, const CallExpr *CE) const { - const GRState *state = C.getState(); + const ProgramState *state = C.getState(); if (!CheckNullStream(state->getSVal(CE->getArg(3)), state, C)) return; } void StreamChecker::Fwrite(CheckerContext &C, const CallExpr *CE) const { - const GRState *state = C.getState(); + const ProgramState *state = C.getState(); if (!CheckNullStream(state->getSVal(CE->getArg(3)), state, C)) return; } void StreamChecker::Fseek(CheckerContext &C, const CallExpr *CE) const { - const GRState *state = C.getState(); + const ProgramState *state = C.getState(); if (!(state = CheckNullStream(state->getSVal(CE->getArg(0)), state, C))) return; // Check the legality of the 'whence' argument of 'fseek'. @@ -291,61 +291,61 @@ void StreamChecker::Fseek(CheckerContext &C, const CallExpr *CE) const { } void StreamChecker::Ftell(CheckerContext &C, const CallExpr *CE) const { - const GRState *state = C.getState(); + const ProgramState *state = C.getState(); if (!CheckNullStream(state->getSVal(CE->getArg(0)), state, C)) return; } void StreamChecker::Rewind(CheckerContext &C, const CallExpr *CE) const { - const GRState *state = C.getState(); + const ProgramState *state = C.getState(); if (!CheckNullStream(state->getSVal(CE->getArg(0)), state, C)) return; } void StreamChecker::Fgetpos(CheckerContext &C, const CallExpr *CE) const { - const GRState *state = C.getState(); + const ProgramState *state = C.getState(); if (!CheckNullStream(state->getSVal(CE->getArg(0)), state, C)) return; } void StreamChecker::Fsetpos(CheckerContext &C, const CallExpr *CE) const { - const GRState *state = C.getState(); + const ProgramState *state = C.getState(); if (!CheckNullStream(state->getSVal(CE->getArg(0)), state, C)) return; } void StreamChecker::Clearerr(CheckerContext &C, const CallExpr *CE) const { - const GRState *state = C.getState(); + const ProgramState *state = C.getState(); if (!CheckNullStream(state->getSVal(CE->getArg(0)), state, C)) return; } void StreamChecker::Feof(CheckerContext &C, const CallExpr *CE) const { - const GRState *state = C.getState(); + const ProgramState *state = C.getState(); if (!CheckNullStream(state->getSVal(CE->getArg(0)), state, C)) return; } void StreamChecker::Ferror(CheckerContext &C, const CallExpr *CE) const { - const GRState *state = C.getState(); + const ProgramState *state = C.getState(); if (!CheckNullStream(state->getSVal(CE->getArg(0)), state, C)) return; } void StreamChecker::Fileno(CheckerContext &C, const CallExpr *CE) const { - const GRState *state = C.getState(); + const ProgramState *state = C.getState(); if (!CheckNullStream(state->getSVal(CE->getArg(0)), state, C)) return; } -const GRState *StreamChecker::CheckNullStream(SVal SV, const GRState *state, +const ProgramState *StreamChecker::CheckNullStream(SVal SV, const ProgramState *state, CheckerContext &C) const { const DefinedSVal *DV = dyn_cast<DefinedSVal>(&SV); if (!DV) return 0; ConstraintManager &CM = C.getConstraintManager(); - const GRState *stateNotNull, *stateNull; + const ProgramState *stateNotNull, *stateNull; llvm::tie(stateNotNull, stateNull) = CM.assumeDual(state, *DV); if (!stateNotNull && stateNull) { @@ -361,8 +361,8 @@ const GRState *StreamChecker::CheckNullStream(SVal SV, const GRState *state, return stateNotNull; } -const GRState *StreamChecker::CheckDoubleClose(const CallExpr *CE, - const GRState *state, +const ProgramState *StreamChecker::CheckDoubleClose(const CallExpr *CE, + const ProgramState *state, CheckerContext &C) const { SymbolRef Sym = state->getSVal(CE->getArg(0)).getAsSymbol(); if (!Sym) @@ -399,7 +399,7 @@ void StreamChecker::checkDeadSymbols(SymbolReaper &SymReaper, for (SymbolReaper::dead_iterator I = SymReaper.dead_begin(), E = SymReaper.dead_end(); I != E; ++I) { SymbolRef Sym = *I; - const GRState *state = C.getState(); + const ProgramState *state = C.getState(); const StreamState *SS = state->get<StreamState>(Sym); if (!SS) return; @@ -420,7 +420,7 @@ void StreamChecker::checkDeadSymbols(SymbolReaper &SymReaper, void StreamChecker::checkEndPath(EndOfFunctionNodeBuilder &B, ExprEngine &Eng) const { - const GRState *state = B.getState(); + const ProgramState *state = B.getState(); typedef llvm::ImmutableMap<SymbolRef, StreamState> SymMap; SymMap M = state->get<StreamState>(); @@ -445,7 +445,7 @@ void StreamChecker::checkPreStmt(const ReturnStmt *S, CheckerContext &C) const { if (!RetE) return; - const GRState *state = C.getState(); + const ProgramState *state = C.getState(); SymbolRef Sym = state->getSVal(RetE).getAsSymbol(); if (!Sym) diff --git a/lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp b/lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp index 1fb181591b42..b860b34ff359 100644 --- a/lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp @@ -27,26 +27,25 @@ class UndefBranchChecker : public Checker<check::BranchCondition> { mutable llvm::OwningPtr<BuiltinBug> BT; struct FindUndefExpr { - GRStateManager& VM; - const GRState* St; + const ProgramState *St; - FindUndefExpr(GRStateManager& V, const GRState* S) : VM(V), St(S) {} + FindUndefExpr(const ProgramState *S) : St(S) {} - const Expr* FindExpr(const Expr* Ex) { + const Expr *FindExpr(const Expr *Ex) { if (!MatchesCriteria(Ex)) return 0; for (Stmt::const_child_iterator I = Ex->child_begin(), E = Ex->child_end();I!=E;++I) - if (const Expr* ExI = dyn_cast_or_null<Expr>(*I)) { - const Expr* E2 = FindExpr(ExI); + if (const Expr *ExI = dyn_cast_or_null<Expr>(*I)) { + const Expr *E2 = FindExpr(ExI); if (E2) return E2; } return Ex; } - bool MatchesCriteria(const Expr* Ex) { return St->getSVal(Ex).isUndef(); } + bool MatchesCriteria(const Expr *Ex) { return St->getSVal(Ex).isUndef(); } }; public: @@ -59,10 +58,10 @@ public: void UndefBranchChecker::checkBranchCondition(const Stmt *Condition, BranchNodeBuilder &Builder, ExprEngine &Eng) const { - const GRState *state = Builder.getState(); + const ProgramState *state = Builder.getState(); SVal X = state->getSVal(Condition); if (X.isUndef()) { - ExplodedNode *N = Builder.generateNode(state, true); + ExplodedNode *N = Builder.generateNode(Condition, state); if (N) { N->markAsSink(); if (!BT) @@ -74,33 +73,31 @@ void UndefBranchChecker::checkBranchCondition(const Stmt *Condition, // branch condition." We do a recursive walk of the condition's // subexpressions and roughly look for the most nested subexpression // that binds to Undefined. We then highlight that expression's range. - BlockEdge B = cast<BlockEdge>(N->getLocation()); - const Expr* Ex = cast<Expr>(B.getSrc()->getTerminatorCondition()); - assert (Ex && "Block must have a terminator."); // Get the predecessor node and check if is a PostStmt with the Stmt // being the terminator condition. We want to inspect the state // of that node instead because it will contain main information about // the subexpressions. - assert (!N->pred_empty()); // Note: any predecessor will do. They should have identical state, // since all the BlockEdge did was act as an error sink since the value // had to already be undefined. + assert (!N->pred_empty()); + const Expr *Ex = cast<Expr>(Condition); ExplodedNode *PrevN = *N->pred_begin(); ProgramPoint P = PrevN->getLocation(); - const GRState* St = N->getState(); + const ProgramState *St = N->getState(); - if (PostStmt* PS = dyn_cast<PostStmt>(&P)) + if (PostStmt *PS = dyn_cast<PostStmt>(&P)) if (PS->getStmt() == Ex) St = PrevN->getState(); - FindUndefExpr FindIt(Eng.getStateManager(), St); + FindUndefExpr FindIt(St); Ex = FindIt.FindExpr(Ex); // Emit the bug report. - EnhancedBugReport *R = new EnhancedBugReport(*BT, BT->getDescription(),N); - R->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, Ex); + BugReport *R = new BugReport(*BT, BT->getDescription(), N); + R->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, Ex)); R->addRange(Ex->getSourceRange()); Eng.getBugReporter().EmitReport(R); diff --git a/lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp b/lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp index 69958d11f717..2aebed9346d6 100644 --- a/lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp @@ -55,7 +55,7 @@ UndefCapturedBlockVarChecker::checkPostStmt(const BlockExpr *BE, if (!BE->getBlockDecl()->hasCaptures()) return; - const GRState *state = C.getState(); + const ProgramState *state = C.getState(); const BlockDataRegion *R = cast<BlockDataRegion>(state->getSVal(BE).getAsRegion()); @@ -74,8 +74,9 @@ UndefCapturedBlockVarChecker::checkPostStmt(const BlockExpr *BE, // Get the VarRegion associated with VD in the local stack frame. const LocationContext *LC = C.getPredecessor()->getLocationContext(); VR = C.getSValBuilder().getRegionManager().getVarRegion(VD, LC); + SVal VRVal = state->getSVal(VR); - if (state->getSVal(VR).isUndef()) + if (VRVal.isUndef()) if (ExplodedNode *N = C.generateSink()) { if (!BT) BT.reset(new BuiltinBug("uninitialized variable captured by block")); @@ -87,10 +88,10 @@ UndefCapturedBlockVarChecker::checkPostStmt(const BlockExpr *BE, os << "Variable '" << VD->getName() << "' is uninitialized when captured by block"; - EnhancedBugReport *R = new EnhancedBugReport(*BT, os.str(), N); + BugReport *R = new BugReport(*BT, os.str(), N); if (const Expr *Ex = FindBlockDeclRefExpr(BE->getBody(), VD)) R->addRange(Ex->getSourceRange()); - R->addVisitorCreator(bugreporter::registerFindLastStore, VR); + R->addVisitor(new FindLastStoreBRVisitor(VRVal, VR)); // need location of block C.EmitReport(R); } diff --git a/lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp b/lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp index 7fa3804639d6..7ae966865c86 100644 --- a/lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp @@ -35,7 +35,7 @@ public: void UndefResultChecker::checkPostStmt(const BinaryOperator *B, CheckerContext &C) const { - const GRState *state = C.getState(); + const ProgramState *state = C.getState(); if (state->getSVal(B).isUndef()) { // Generate an error node. ExplodedNode *N = C.generateSink(); @@ -71,13 +71,13 @@ void UndefResultChecker::checkPostStmt(const BinaryOperator *B, << BinaryOperator::getOpcodeStr(B->getOpcode()) << "' expression is undefined"; } - EnhancedBugReport *report = new EnhancedBugReport(*BT, OS.str(), N); + BugReport *report = new BugReport(*BT, OS.str(), N); if (Ex) { report->addRange(Ex->getSourceRange()); - report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, Ex); + report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, Ex)); } else - report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, B); + report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, B)); C.EmitReport(report); } } diff --git a/lib/StaticAnalyzer/Checkers/UndefinedArraySubscriptChecker.cpp b/lib/StaticAnalyzer/Checkers/UndefinedArraySubscriptChecker.cpp index e51ab20d2f30..bb6831b78301 100644 --- a/lib/StaticAnalyzer/Checkers/UndefinedArraySubscriptChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/UndefinedArraySubscriptChecker.cpp @@ -40,10 +40,10 @@ UndefinedArraySubscriptChecker::checkPreStmt(const ArraySubscriptExpr *A, BT.reset(new BuiltinBug("Array subscript is undefined")); // Generate a report for this bug. - EnhancedBugReport *R = new EnhancedBugReport(*BT, BT->getName(), N); + BugReport *R = new BugReport(*BT, BT->getName(), N); R->addRange(A->getIdx()->getSourceRange()); - R->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, - A->getIdx()); + R->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, + A->getIdx())); C.EmitReport(R); } } diff --git a/lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp b/lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp index 28806e3db917..5ca4a9fe46d2 100644 --- a/lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp @@ -27,11 +27,13 @@ class UndefinedAssignmentChecker mutable llvm::OwningPtr<BugType> BT; public: - void checkBind(SVal location, SVal val, CheckerContext &C) const; + void checkBind(SVal location, SVal val, const Stmt *S, + CheckerContext &C) const; }; } void UndefinedAssignmentChecker::checkBind(SVal location, SVal val, + const Stmt *StoreE, CheckerContext &C) const { if (!val.isUndef()) return; @@ -49,11 +51,10 @@ void UndefinedAssignmentChecker::checkBind(SVal location, SVal val, // Generate a report for this bug. const Expr *ex = 0; - const Stmt *StoreE = C.getStmt(); while (StoreE) { if (const BinaryOperator *B = dyn_cast<BinaryOperator>(StoreE)) { if (B->isCompoundAssignmentOp()) { - const GRState *state = C.getState(); + const ProgramState *state = C.getState(); if (state->getSVal(B->getLHS()).isUndef()) { str = "The left expression of the compound assignment is an " "uninitialized value. The computed value will also be garbage"; @@ -67,17 +68,17 @@ void UndefinedAssignmentChecker::checkBind(SVal location, SVal val, } if (const DeclStmt *DS = dyn_cast<DeclStmt>(StoreE)) { - const VarDecl* VD = dyn_cast<VarDecl>(DS->getSingleDecl()); + const VarDecl *VD = dyn_cast<VarDecl>(DS->getSingleDecl()); ex = VD->getInit(); } break; } - EnhancedBugReport *R = new EnhancedBugReport(*BT, str, N); + BugReport *R = new BugReport(*BT, str, N); if (ex) { R->addRange(ex->getSourceRange()); - R->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, ex); + R->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, ex)); } C.EmitReport(R); } diff --git a/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp b/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp index 0ecc391f3e8f..cec286d2f3e9 100644 --- a/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp @@ -62,7 +62,8 @@ void UnixAPIChecker::CheckOpen(CheckerContext &C, const CallExpr *CE) const { // The definition of O_CREAT is platform specific. We need a better way // of querying this information from the checking environment. if (!Val_O_CREAT.hasValue()) { - if (C.getASTContext().Target.getTriple().getVendor() == llvm::Triple::Apple) + if (C.getASTContext().getTargetInfo().getTriple().getVendor() + == llvm::Triple::Apple) Val_O_CREAT = 0x0200; else { // FIXME: We need a more general way of getting the O_CREAT value. @@ -73,7 +74,7 @@ void UnixAPIChecker::CheckOpen(CheckerContext &C, const CallExpr *CE) const { } // Look at the 'oflags' argument for the O_CREAT flag. - const GRState *state = C.getState(); + const ProgramState *state = C.getState(); if (CE->getNumArgs() < 2) { // The frontend should issue a warning for this case, so this is a sanity @@ -101,7 +102,7 @@ void UnixAPIChecker::CheckOpen(CheckerContext &C, const CallExpr *CE) const { DefinedSVal maskedFlags = cast<DefinedSVal>(maskedFlagsUC); // Check if maskedFlags is non-zero. - const GRState *trueState, *falseState; + const ProgramState *trueState, *falseState; llvm::tie(trueState, falseState) = state->assume(maskedFlags); // Only emit an error if the value of 'maskedFlags' is properly @@ -116,8 +117,8 @@ void UnixAPIChecker::CheckOpen(CheckerContext &C, const CallExpr *CE) const { LazyInitialize(BT_open, "Improper use of 'open'"); - RangedBugReport *report = - new RangedBugReport(*BT_open, + BugReport *report = + new BugReport(*BT_open, "Call to 'open' requires a third argument when " "the 'O_CREAT' flag is set", N); report->addRange(oflagsEx->getSourceRange()); @@ -140,7 +141,7 @@ void UnixAPIChecker::CheckPthreadOnce(CheckerContext &C, // Check if the first argument is stack allocated. If so, issue a warning // because that's likely to be bad news. - const GRState *state = C.getState(); + const ProgramState *state = C.getState(); const MemRegion *R = state->getSVal(CE->getArg(0)).getAsRegion(); if (!R || !isa<StackSpaceRegion>(R->getMemorySpace())) return; @@ -163,7 +164,7 @@ void UnixAPIChecker::CheckPthreadOnce(CheckerContext &C, LazyInitialize(BT_pthreadOnce, "Improper use of 'pthread_once'"); - RangedBugReport *report = new RangedBugReport(*BT_pthreadOnce, os.str(), N); + BugReport *report = new BugReport(*BT_pthreadOnce, os.str(), N); report->addRange(CE->getArg(0)->getSourceRange()); C.EmitReport(report); } @@ -182,13 +183,13 @@ void UnixAPIChecker::CheckMallocZero(CheckerContext &C, return; // Check if the allocation size is 0. - const GRState *state = C.getState(); + const ProgramState *state = C.getState(); SVal argVal = state->getSVal(CE->getArg(0)); if (argVal.isUnknownOrUndef()) return; - const GRState *trueState, *falseState; + const ProgramState *trueState, *falseState; llvm::tie(trueState, falseState) = state->assume(cast<DefinedSVal>(argVal)); // Is the value perfectly constrained to zero? @@ -202,12 +203,12 @@ void UnixAPIChecker::CheckMallocZero(CheckerContext &C, LazyInitialize(BT_mallocZero, "Undefined allocation of 0 bytes"); - EnhancedBugReport *report = - new EnhancedBugReport(*BT_mallocZero, "Call to 'malloc' has an allocation" + BugReport *report = + new BugReport(*BT_mallocZero, "Call to 'malloc' has an allocation" " size of 0 bytes", N); report->addRange(CE->getArg(0)->getSourceRange()); - report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, - CE->getArg(0)); + report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, + CE->getArg(0))); C.EmitReport(report); return; } @@ -225,7 +226,7 @@ void UnixAPIChecker::CheckMallocZero(CheckerContext &C, 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(); + const ProgramState *state = C.getState(); const Expr *Callee = CE->getCallee(); const FunctionDecl *Fn = state->getSVal(Callee).getAsFunctionDecl(); diff --git a/lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp b/lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp index b540bce98170..459ee65d19d9 100644 --- a/lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp @@ -60,11 +60,12 @@ void UnreachableCodeChecker::checkEndAnalysis(ExplodedGraph &G, CFG *C = 0; ParentMap *PM = 0; + const LocationContext *LC = 0; // Iterate over ExplodedGraph for (ExplodedGraph::node_iterator I = G.nodes_begin(), E = G.nodes_end(); I != E; ++I) { const ProgramPoint &P = I->getLocation(); - const LocationContext *LC = P.getLocationContext(); + LC = P.getLocationContext(); // Save the CFG if we don't have it already if (!C) @@ -111,22 +112,30 @@ void UnreachableCodeChecker::checkEndAnalysis(ExplodedGraph &G, // FIXME: This should be extended to include other unreachable markers, // such as llvm_unreachable. if (!CB->empty()) { - CFGElement First = CB->front(); - if (const CFGStmt *S = First.getAs<CFGStmt>()) { - if (const CallExpr *CE = dyn_cast<CallExpr>(S->getStmt())) { - if (CE->isBuiltinCall(Ctx) == Builtin::BI__builtin_unreachable) - continue; - } + bool foundUnreachable = false; + for (CFGBlock::const_iterator ci = CB->begin(), ce = CB->end(); + ci != ce; ++ci) { + if (const CFGStmt *S = (*ci).getAs<CFGStmt>()) + if (const CallExpr *CE = dyn_cast<CallExpr>(S->getStmt())) { + if (CE->isBuiltinCall(Ctx) == Builtin::BI__builtin_unreachable) { + foundUnreachable = true; + break; + } + } } + if (foundUnreachable) + continue; } // We found a block that wasn't covered - find the statement to report SourceRange SR; + PathDiagnosticLocation DL; SourceLocation SL; if (const Stmt *S = getUnreachableStmt(CB)) { SR = S->getSourceRange(); - SL = S->getLocStart(); - if (SR.isInvalid() || SL.isInvalid()) + DL = PathDiagnosticLocation::createBegin(S, B.getSourceManager(), LC); + SL = DL.asLocation(); + if (SR.isInvalid() || !SL.isValid()) continue; } else @@ -138,7 +147,7 @@ void UnreachableCodeChecker::checkEndAnalysis(ExplodedGraph &G, continue; B.EmitBasicReport("Unreachable code", "Dead code", "This statement is never" - " executed", SL, SR); + " executed", DL, SR); } } diff --git a/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp b/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp index 875dce2e994a..b34b97c5b301 100644 --- a/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp @@ -48,8 +48,8 @@ void VLASizeChecker::checkPreStmt(const DeclStmt *DS, CheckerContext &C) const { return; // FIXME: Handle multi-dimensional VLAs. - const Expr* SE = VLA->getSizeExpr(); - const GRState *state = C.getState(); + const Expr *SE = VLA->getSizeExpr(); + const ProgramState *state = C.getState(); SVal sizeV = state->getSVal(SE); if (sizeV.isUndef()) { @@ -62,10 +62,10 @@ void VLASizeChecker::checkPreStmt(const DeclStmt *DS, CheckerContext &C) const { BT_undef.reset(new BuiltinBug("Declared variable-length array (VLA) " "uses a garbage value as its size")); - EnhancedBugReport *report = - new EnhancedBugReport(*BT_undef, BT_undef->getName(), N); + BugReport *report = + new BugReport(*BT_undef, BT_undef->getName(), N); report->addRange(SE->getSourceRange()); - report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, SE); + report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, SE)); C.EmitReport(report); return; } @@ -78,19 +78,19 @@ void VLASizeChecker::checkPreStmt(const DeclStmt *DS, CheckerContext &C) const { // Check if the size is zero. DefinedSVal sizeD = cast<DefinedSVal>(sizeV); - const GRState *stateNotZero, *stateZero; + const ProgramState *stateNotZero, *stateZero; llvm::tie(stateNotZero, stateZero) = state->assume(sizeD); if (stateZero && !stateNotZero) { - ExplodedNode* N = C.generateSink(stateZero); + ExplodedNode *N = C.generateSink(stateZero); if (!BT_zero) BT_zero.reset(new BuiltinBug("Declared variable-length array (VLA) has " "zero size")); - EnhancedBugReport *report = - new EnhancedBugReport(*BT_zero, BT_zero->getName(), N); + BugReport *report = + new BugReport(*BT_zero, BT_zero->getName(), N); report->addRange(SE->getSourceRange()); - report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, SE); + report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, SE)); C.EmitReport(report); return; } diff --git a/lib/StaticAnalyzer/Core/AggExprVisitor.cpp b/lib/StaticAnalyzer/Core/AggExprVisitor.cpp index 901190d901dd..0936d61784ac 100644 --- a/lib/StaticAnalyzer/Core/AggExprVisitor.cpp +++ b/lib/StaticAnalyzer/Core/AggExprVisitor.cpp @@ -46,7 +46,7 @@ public: void AggExprVisitor::VisitCastExpr(CastExpr *E) { switch (E->getCastKind()) { default: - assert(0 && "Unhandled cast kind"); + llvm_unreachable("Unhandled cast kind"); case CK_NoOp: case CK_ConstructorConversion: case CK_UserDefinedConversion: diff --git a/lib/StaticAnalyzer/Core/AnalysisManager.cpp b/lib/StaticAnalyzer/Core/AnalysisManager.cpp index 5f4f83c0a8dc..17ec70d39b90 100644 --- a/lib/StaticAnalyzer/Core/AnalysisManager.cpp +++ b/lib/StaticAnalyzer/Core/AnalysisManager.cpp @@ -14,6 +14,58 @@ using namespace clang; using namespace ento; +AnalysisManager::AnalysisManager(ASTContext &ctx, DiagnosticsEngine &diags, + const LangOptions &lang, + PathDiagnosticConsumer *pd, + StoreManagerCreator storemgr, + ConstraintManagerCreator constraintmgr, + CheckerManager *checkerMgr, + idx::Indexer *idxer, + unsigned maxnodes, unsigned maxvisit, + bool vizdot, bool vizubi, + AnalysisPurgeMode purge, + bool eager, bool trim, + bool inlinecall, bool useUnoptimizedCFG, + bool addImplicitDtors, bool addInitializers, + bool eagerlyTrimEGraph) + : AnaCtxMgr(useUnoptimizedCFG, addImplicitDtors, addInitializers), + Ctx(ctx), Diags(diags), LangInfo(lang), PD(pd), + CreateStoreMgr(storemgr), CreateConstraintMgr(constraintmgr), + CheckerMgr(checkerMgr), Idxer(idxer), + AScope(ScopeDecl), MaxNodes(maxnodes), MaxVisit(maxvisit), + VisualizeEGDot(vizdot), VisualizeEGUbi(vizubi), PurgeDead(purge), + EagerlyAssume(eager), TrimGraph(trim), InlineCall(inlinecall), + EagerlyTrimEGraph(eagerlyTrimEGraph) +{ + AnaCtxMgr.getCFGBuildOptions().setAllAlwaysAdd(); +} + +AnalysisManager::AnalysisManager(ASTContext &ctx, DiagnosticsEngine &diags, + AnalysisManager &ParentAM) + : AnaCtxMgr(ParentAM.AnaCtxMgr.getUseUnoptimizedCFG(), + ParentAM.AnaCtxMgr.getCFGBuildOptions().AddImplicitDtors, + ParentAM.AnaCtxMgr.getCFGBuildOptions().AddInitializers), + Ctx(ctx), Diags(diags), + LangInfo(ParentAM.LangInfo), PD(ParentAM.getPathDiagnosticConsumer()), + CreateStoreMgr(ParentAM.CreateStoreMgr), + CreateConstraintMgr(ParentAM.CreateConstraintMgr), + CheckerMgr(ParentAM.CheckerMgr), + Idxer(ParentAM.Idxer), + AScope(ScopeDecl), + MaxNodes(ParentAM.MaxNodes), + MaxVisit(ParentAM.MaxVisit), + VisualizeEGDot(ParentAM.VisualizeEGDot), + VisualizeEGUbi(ParentAM.VisualizeEGUbi), + PurgeDead(ParentAM.PurgeDead), + EagerlyAssume(ParentAM.EagerlyAssume), + TrimGraph(ParentAM.TrimGraph), + InlineCall(ParentAM.InlineCall), + EagerlyTrimEGraph(ParentAM.EagerlyTrimEGraph) +{ + AnaCtxMgr.getCFGBuildOptions().setAllAlwaysAdd(); +} + + AnalysisContext * AnalysisManager::getAnalysisContextInAnotherTU(const Decl *D) { idx::Entity Ent = idx::Entity::get(const_cast<Decl *>(D), diff --git a/lib/StaticAnalyzer/Core/BasicConstraintManager.cpp b/lib/StaticAnalyzer/Core/BasicConstraintManager.cpp index 3050ca3ce3cc..6c748b6ad01a 100644 --- a/lib/StaticAnalyzer/Core/BasicConstraintManager.cpp +++ b/lib/StaticAnalyzer/Core/BasicConstraintManager.cpp @@ -8,14 +8,13 @@ //===----------------------------------------------------------------------===// // // This file defines BasicConstraintManager, a class that tracks simple -// equality and inequality constraints on symbolic values of GRState. +// equality and inequality constraints on symbolic values of ProgramState. // //===----------------------------------------------------------------------===// #include "SimpleConstraintManager.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/GRState.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/GRStateTrait.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/TransferFuncs.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" #include "llvm/Support/raw_ostream.h" using namespace clang; @@ -25,7 +24,7 @@ using namespace ento; namespace { class ConstNotEq {}; } namespace { class ConstEq {}; } -typedef llvm::ImmutableMap<SymbolRef,GRState::IntSetTy> ConstNotEqTy; +typedef llvm::ImmutableMap<SymbolRef,ProgramState::IntSetTy> ConstNotEqTy; typedef llvm::ImmutableMap<SymbolRef,const llvm::APSInt*> ConstEqTy; static int ConstEqIndex = 0; @@ -34,13 +33,14 @@ static int ConstNotEqIndex = 0; namespace clang { namespace ento { template<> -struct GRStateTrait<ConstNotEq> : public GRStatePartialTrait<ConstNotEqTy> { - static inline void* GDMIndex() { return &ConstNotEqIndex; } +struct ProgramStateTrait<ConstNotEq> : + public ProgramStatePartialTrait<ConstNotEqTy> { + static inline void *GDMIndex() { return &ConstNotEqIndex; } }; template<> -struct GRStateTrait<ConstEq> : public GRStatePartialTrait<ConstEqTy> { - static inline void* GDMIndex() { return &ConstEqIndex; } +struct ProgramStateTrait<ConstEq> : public ProgramStatePartialTrait<ConstEqTy> { + static inline void *GDMIndex() { return &ConstEqIndex; } }; } } @@ -50,62 +50,81 @@ namespace { // constants and integer variables. class BasicConstraintManager : public SimpleConstraintManager { - GRState::IntSetTy::Factory ISetFactory; + ProgramState::IntSetTy::Factory ISetFactory; public: - BasicConstraintManager(GRStateManager &statemgr, SubEngine &subengine) + BasicConstraintManager(ProgramStateManager &statemgr, SubEngine &subengine) : SimpleConstraintManager(subengine), ISetFactory(statemgr.getAllocator()) {} - const GRState *assumeSymNE(const GRState* state, SymbolRef sym, - const llvm::APSInt& V, - const llvm::APSInt& Adjustment); - - const GRState *assumeSymEQ(const GRState* state, SymbolRef sym, - const llvm::APSInt& V, - const llvm::APSInt& Adjustment); - - const GRState *assumeSymLT(const GRState* state, SymbolRef sym, - const llvm::APSInt& V, - const llvm::APSInt& Adjustment); - - const GRState *assumeSymGT(const GRState* state, SymbolRef sym, - const llvm::APSInt& V, - const llvm::APSInt& Adjustment); - - const GRState *assumeSymGE(const GRState* state, SymbolRef sym, - const llvm::APSInt& V, - const llvm::APSInt& Adjustment); - - const GRState *assumeSymLE(const GRState* state, SymbolRef sym, - const llvm::APSInt& V, - const llvm::APSInt& Adjustment); - - const GRState* AddEQ(const GRState* state, SymbolRef sym, const llvm::APSInt& V); - - const GRState* AddNE(const GRState* state, SymbolRef sym, const llvm::APSInt& V); - - const llvm::APSInt* getSymVal(const GRState* state, SymbolRef sym) const; - bool isNotEqual(const GRState* state, SymbolRef sym, const llvm::APSInt& V) - const; - bool isEqual(const GRState* state, SymbolRef sym, const llvm::APSInt& V) - const; - - const GRState* removeDeadBindings(const GRState* state, SymbolReaper& SymReaper); - - void print(const GRState* state, llvm::raw_ostream& Out, - const char* nl, const char *sep); + const ProgramState *assumeSymNE(const ProgramState *state, + SymbolRef sym, + const llvm::APSInt& V, + const llvm::APSInt& Adjustment); + + const ProgramState *assumeSymEQ(const ProgramState *state, + SymbolRef sym, + const llvm::APSInt& V, + const llvm::APSInt& Adjustment); + + const ProgramState *assumeSymLT(const ProgramState *state, + SymbolRef sym, + const llvm::APSInt& V, + const llvm::APSInt& Adjustment); + + const ProgramState *assumeSymGT(const ProgramState *state, + SymbolRef sym, + const llvm::APSInt& V, + const llvm::APSInt& Adjustment); + + const ProgramState *assumeSymGE(const ProgramState *state, + SymbolRef sym, + const llvm::APSInt& V, + const llvm::APSInt& Adjustment); + + const ProgramState *assumeSymLE(const ProgramState *state, + SymbolRef sym, + const llvm::APSInt& V, + const llvm::APSInt& Adjustment); + + const ProgramState *AddEQ(const ProgramState *state, + SymbolRef sym, + const llvm::APSInt& V); + + const ProgramState *AddNE(const ProgramState *state, + SymbolRef sym, + const llvm::APSInt& V); + + const llvm::APSInt* getSymVal(const ProgramState *state, + SymbolRef sym) const; + + bool isNotEqual(const ProgramState *state, + SymbolRef sym, + const llvm::APSInt& V) const; + + bool isEqual(const ProgramState *state, + SymbolRef sym, + const llvm::APSInt& V) const; + + const ProgramState *removeDeadBindings(const ProgramState *state, + SymbolReaper& SymReaper); + + void print(const ProgramState *state, + raw_ostream &Out, + const char* nl, + const char *sep); }; } // end anonymous namespace -ConstraintManager* ento::CreateBasicConstraintManager(GRStateManager& statemgr, - SubEngine &subengine) { +ConstraintManager* +ento::CreateBasicConstraintManager(ProgramStateManager& statemgr, + SubEngine &subengine) { return new BasicConstraintManager(statemgr, subengine); } - -const GRState* -BasicConstraintManager::assumeSymNE(const GRState *state, SymbolRef sym, +const ProgramState* +BasicConstraintManager::assumeSymNE(const ProgramState *state, + SymbolRef sym, const llvm::APSInt &V, const llvm::APSInt &Adjustment) { // First, determine if sym == X, where X+Adjustment != V. @@ -124,8 +143,9 @@ BasicConstraintManager::assumeSymNE(const GRState *state, SymbolRef sym, return AddNE(state, sym, Adjusted); } -const GRState* -BasicConstraintManager::assumeSymEQ(const GRState *state, SymbolRef sym, +const ProgramState* +BasicConstraintManager::assumeSymEQ(const ProgramState *state, + SymbolRef sym, const llvm::APSInt &V, const llvm::APSInt &Adjustment) { // First, determine if sym == X, where X+Adjustment != V. @@ -145,8 +165,9 @@ BasicConstraintManager::assumeSymEQ(const GRState *state, SymbolRef sym, } // The logic for these will be handled in another ConstraintManager. -const GRState* -BasicConstraintManager::assumeSymLT(const GRState *state, SymbolRef sym, +const ProgramState* +BasicConstraintManager::assumeSymLT(const ProgramState *state, + SymbolRef sym, const llvm::APSInt &V, const llvm::APSInt &Adjustment) { // Is 'V' the smallest possible value? @@ -159,8 +180,9 @@ BasicConstraintManager::assumeSymLT(const GRState *state, SymbolRef sym, return assumeSymNE(state, sym, V, Adjustment); } -const GRState* -BasicConstraintManager::assumeSymGT(const GRState *state, SymbolRef sym, +const ProgramState* +BasicConstraintManager::assumeSymGT(const ProgramState *state, + SymbolRef sym, const llvm::APSInt &V, const llvm::APSInt &Adjustment) { // Is 'V' the largest possible value? @@ -173,8 +195,9 @@ BasicConstraintManager::assumeSymGT(const GRState *state, SymbolRef sym, return assumeSymNE(state, sym, V, Adjustment); } -const GRState* -BasicConstraintManager::assumeSymGE(const GRState *state, SymbolRef sym, +const ProgramState* +BasicConstraintManager::assumeSymGE(const ProgramState *state, + SymbolRef sym, const llvm::APSInt &V, const llvm::APSInt &Adjustment) { // Reject a path if the value of sym is a constant X and !(X+Adj >= V). @@ -201,8 +224,9 @@ BasicConstraintManager::assumeSymGE(const GRState *state, SymbolRef sym, return state; } -const GRState* -BasicConstraintManager::assumeSymLE(const GRState *state, SymbolRef sym, +const ProgramState* +BasicConstraintManager::assumeSymLE(const ProgramState *state, + SymbolRef sym, const llvm::APSInt &V, const llvm::APSInt &Adjustment) { // Reject a path if the value of sym is a constant X and !(X+Adj <= V). @@ -229,18 +253,20 @@ BasicConstraintManager::assumeSymLE(const GRState *state, SymbolRef sym, return state; } -const GRState* BasicConstraintManager::AddEQ(const GRState* state, SymbolRef sym, +const ProgramState *BasicConstraintManager::AddEQ(const ProgramState *state, + SymbolRef sym, const llvm::APSInt& V) { // Create a new state with the old binding replaced. return state->set<ConstEq>(sym, &state->getBasicVals().getValue(V)); } -const GRState* BasicConstraintManager::AddNE(const GRState* state, SymbolRef sym, - const llvm::APSInt& V) { +const ProgramState *BasicConstraintManager::AddNE(const ProgramState *state, + SymbolRef sym, + const llvm::APSInt& V) { // First, retrieve the NE-set associated with the given symbol. ConstNotEqTy::data_type* T = state->get<ConstNotEq>(sym); - GRState::IntSetTy S = T ? *T : ISetFactory.getEmptySet(); + ProgramState::IntSetTy S = T ? *T : ISetFactory.getEmptySet(); // Now add V to the NE set. S = ISetFactory.add(S, &state->getBasicVals().getValue(V)); @@ -249,13 +275,14 @@ const GRState* BasicConstraintManager::AddNE(const GRState* state, SymbolRef sym return state->set<ConstNotEq>(sym, S); } -const llvm::APSInt* BasicConstraintManager::getSymVal(const GRState* state, +const llvm::APSInt* BasicConstraintManager::getSymVal(const ProgramState *state, SymbolRef sym) const { const ConstEqTy::data_type* T = state->get<ConstEq>(sym); return T ? *T : NULL; } -bool BasicConstraintManager::isNotEqual(const GRState* state, SymbolRef sym, +bool BasicConstraintManager::isNotEqual(const ProgramState *state, + SymbolRef sym, const llvm::APSInt& V) const { // Retrieve the NE-set associated with the given symbol. @@ -265,7 +292,8 @@ bool BasicConstraintManager::isNotEqual(const GRState* state, SymbolRef sym, return T ? T->contains(&state->getBasicVals().getValue(V)) : false; } -bool BasicConstraintManager::isEqual(const GRState* state, SymbolRef sym, +bool BasicConstraintManager::isEqual(const ProgramState *state, + SymbolRef sym, const llvm::APSInt& V) const { // Retrieve the EQ-set associated with the given symbol. const ConstEqTy::data_type* T = state->get<ConstEq>(sym); @@ -275,8 +303,8 @@ bool BasicConstraintManager::isEqual(const GRState* state, SymbolRef sym, /// Scan all symbols referenced by the constraints. If the symbol is not alive /// as marked in LSymbols, mark it as dead in DSymbols. -const GRState* -BasicConstraintManager::removeDeadBindings(const GRState* state, +const ProgramState* +BasicConstraintManager::removeDeadBindings(const ProgramState *state, SymbolReaper& SymReaper) { ConstEqTy CE = state->get<ConstEq>(); @@ -301,7 +329,8 @@ BasicConstraintManager::removeDeadBindings(const GRState* state, return state->set<ConstNotEq>(CNE); } -void BasicConstraintManager::print(const GRState* state, llvm::raw_ostream& Out, +void BasicConstraintManager::print(const ProgramState *state, + raw_ostream &Out, const char* nl, const char *sep) { // Print equality constraints. @@ -324,7 +353,7 @@ void BasicConstraintManager::print(const GRState* state, llvm::raw_ostream& Out, Out << nl << " $" << I.getKey() << " : "; bool isFirst = true; - GRState::IntSetTy::iterator J = I.getData().begin(), + ProgramState::IntSetTy::iterator J = I.getData().begin(), EJ = I.getData().end(); for ( ; J != EJ; ++J) { diff --git a/lib/StaticAnalyzer/Core/BasicStore.cpp b/lib/StaticAnalyzer/Core/BasicStore.cpp deleted file mode 100644 index 7c9f45a474c1..000000000000 --- a/lib/StaticAnalyzer/Core/BasicStore.cpp +++ /dev/null @@ -1,605 +0,0 @@ -//== BasicStore.cpp - Basic map from Locations to Values --------*- C++ -*--==// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file defined the BasicStore and BasicStoreManager classes. -// -//===----------------------------------------------------------------------===// - -#include "clang/AST/DeclCXX.h" -#include "clang/AST/ExprObjC.h" -#include "clang/Analysis/Analyses/LiveVariables.h" -#include "clang/Analysis/AnalysisContext.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/GRState.h" -#include "llvm/ADT/ImmutableMap.h" - -using namespace clang; -using namespace ento; - -typedef llvm::ImmutableMap<const MemRegion*,SVal> BindingsTy; - -namespace { - -class BasicStoreSubRegionMap : public SubRegionMap { -public: - BasicStoreSubRegionMap() {} - - bool iterSubRegions(const MemRegion* R, Visitor& V) const { - return true; // Do nothing. No subregions. - } -}; - -class BasicStoreManager : public StoreManager { - BindingsTy::Factory VBFactory; -public: - BasicStoreManager(GRStateManager& mgr) - : StoreManager(mgr), VBFactory(mgr.getAllocator()) {} - - ~BasicStoreManager() {} - - SubRegionMap *getSubRegionMap(Store store) { - return new BasicStoreSubRegionMap(); - } - - SVal Retrieve(Store store, Loc loc, QualType T = QualType()); - - StoreRef invalidateRegion(Store store, const MemRegion *R, const Expr *E, - unsigned Count, InvalidatedSymbols &IS); - - StoreRef invalidateRegions(Store store, const MemRegion * const *Begin, - const MemRegion * const *End, const Expr *E, - unsigned Count, InvalidatedSymbols &IS, - bool invalidateGlobals, - InvalidatedRegions *Regions); - - StoreRef scanForIvars(Stmt *B, const Decl* SelfDecl, - const MemRegion *SelfRegion, Store St); - - StoreRef Bind(Store St, Loc loc, SVal V); - StoreRef Remove(Store St, Loc loc); - StoreRef getInitialStore(const LocationContext *InitLoc); - - StoreRef BindCompoundLiteral(Store store, const CompoundLiteralExpr*, - const LocationContext*, SVal val) { - return StoreRef(store, *this); - } - - /// ArrayToPointer - Used by ExprEngine::VistCast to handle implicit - /// conversions between arrays and pointers. - SVal ArrayToPointer(Loc Array) { return Array; } - - /// removeDeadBindings - Scans a BasicStore of 'state' for dead values. - /// It updatees the GRState object in place with the values removed. - StoreRef removeDeadBindings(Store store, const StackFrameContext *LCtx, - SymbolReaper& SymReaper, - llvm::SmallVectorImpl<const MemRegion*>& RegionRoots); - - void iterBindings(Store store, BindingsHandler& f); - - StoreRef BindDecl(Store store, const VarRegion *VR, SVal InitVal) { - return BindDeclInternal(store, VR, &InitVal); - } - - StoreRef BindDeclWithNoInit(Store store, const VarRegion *VR) { - return BindDeclInternal(store, VR, 0); - } - - StoreRef BindDeclInternal(Store store, const VarRegion *VR, SVal *InitVal); - - static inline BindingsTy GetBindings(Store store) { - return BindingsTy(static_cast<const BindingsTy::TreeTy*>(store)); - } - - void print(Store store, llvm::raw_ostream& Out, const char* nl, - const char *sep); - -private: - SVal LazyRetrieve(Store store, const TypedRegion *R); -}; - -} // end anonymous namespace - - -StoreManager* ento::CreateBasicStoreManager(GRStateManager& StMgr) { - return new BasicStoreManager(StMgr); -} - -static bool isHigherOrderRawPtr(QualType T, ASTContext &C) { - bool foundPointer = false; - while (1) { - const PointerType *PT = T->getAs<PointerType>(); - if (!PT) { - if (!foundPointer) - return false; - - // intptr_t* or intptr_t**, etc? - if (T->isIntegerType() && C.getTypeSize(T) == C.getTypeSize(C.VoidPtrTy)) - return true; - - QualType X = C.getCanonicalType(T).getUnqualifiedType(); - return X == C.VoidTy; - } - - foundPointer = true; - T = PT->getPointeeType(); - } -} - -SVal BasicStoreManager::LazyRetrieve(Store store, const TypedRegion *R) { - const VarRegion *VR = dyn_cast<VarRegion>(R); - if (!VR) - return UnknownVal(); - - const VarDecl *VD = VR->getDecl(); - QualType T = VD->getType(); - - // Only handle simple types that we can symbolicate. - if (!SymbolManager::canSymbolicate(T) || !T->isScalarType()) - return UnknownVal(); - - // Globals and parameters start with symbolic values. - // Local variables initially are undefined. - - // Non-static globals may have had their values reset by invalidateRegions. - const MemSpaceRegion *MS = VR->getMemorySpace(); - if (isa<NonStaticGlobalSpaceRegion>(MS)) { - BindingsTy B = GetBindings(store); - // FIXME: Copy-and-pasted from RegionStore.cpp. - if (BindingsTy::data_type *Val = B.lookup(MS)) { - if (SymbolRef parentSym = Val->getAsSymbol()) - return svalBuilder.getDerivedRegionValueSymbolVal(parentSym, R); - - if (Val->isZeroConstant()) - return svalBuilder.makeZeroVal(T); - - if (Val->isUnknownOrUndef()) - return *Val; - - assert(0 && "Unknown default value."); - } - } - - if (VR->hasGlobalsOrParametersStorage() || - isa<UnknownSpaceRegion>(VR->getMemorySpace())) - return svalBuilder.getRegionValueSymbolVal(R); - - return UndefinedVal(); -} - -SVal BasicStoreManager::Retrieve(Store store, Loc loc, QualType T) { - if (isa<UnknownVal>(loc)) - return UnknownVal(); - - assert(!isa<UndefinedVal>(loc)); - - switch (loc.getSubKind()) { - - case loc::MemRegionKind: { - const MemRegion* R = cast<loc::MemRegionVal>(loc).getRegion(); - - if (!(isa<VarRegion>(R) || isa<ObjCIvarRegion>(R) || - isa<CXXThisRegion>(R))) - return UnknownVal(); - - BindingsTy B = GetBindings(store); - BindingsTy::data_type *Val = B.lookup(R); - const TypedRegion *TR = cast<TypedRegion>(R); - - if (Val) - return CastRetrievedVal(*Val, TR, T); - - SVal V = LazyRetrieve(store, TR); - return V.isUnknownOrUndef() ? V : CastRetrievedVal(V, TR, T); - } - - case loc::ObjCPropRefKind: - case loc::ConcreteIntKind: - // Support direct accesses to memory. It's up to individual checkers - // to flag an error. - return UnknownVal(); - - default: - assert (false && "Invalid Loc."); - break; - } - - return UnknownVal(); -} - -StoreRef BasicStoreManager::Bind(Store store, Loc loc, SVal V) { - if (isa<loc::ConcreteInt>(loc)) - return StoreRef(store, *this); - - const MemRegion* R = cast<loc::MemRegionVal>(loc).getRegion(); - - // Special case: a default symbol assigned to the NonStaticGlobalsSpaceRegion - // that is used to derive other symbols. - if (isa<NonStaticGlobalSpaceRegion>(R)) { - BindingsTy B = GetBindings(store); - return StoreRef(VBFactory.add(B, R, V).getRoot(), *this); - } - - // Special case: handle store of pointer values (Loc) to pointers via - // a cast to intXX_t*, void*, etc. This is needed to handle - // OSCompareAndSwap32Barrier/OSCompareAndSwap64Barrier. - if (isa<Loc>(V) || isa<nonloc::LocAsInteger>(V)) - if (const ElementRegion *ER = dyn_cast<ElementRegion>(R)) { - // FIXME: Should check for index 0. - QualType T = ER->getLocationType(); - - if (isHigherOrderRawPtr(T, Ctx)) - R = ER->getSuperRegion(); - } - - if (!(isa<VarRegion>(R) || isa<ObjCIvarRegion>(R) || isa<CXXThisRegion>(R))) - return StoreRef(store, *this); - - const TypedRegion *TyR = cast<TypedRegion>(R); - - // Do not bind to arrays. We need to explicitly check for this so that - // we do not encounter any weirdness of trying to load/store from arrays. - if (TyR->isBoundable() && TyR->getValueType()->isArrayType()) - return StoreRef(store, *this); - - if (nonloc::LocAsInteger *X = dyn_cast<nonloc::LocAsInteger>(&V)) { - // Only convert 'V' to a location iff the underlying region type - // is a location as well. - // FIXME: We are allowing a store of an arbitrary location to - // a pointer. We may wish to flag a type error here if the types - // are incompatible. This may also cause lots of breakage - // elsewhere. Food for thought. - if (TyR->isBoundable() && Loc::isLocType(TyR->getValueType())) - V = X->getLoc(); - } - - BindingsTy B = GetBindings(store); - return StoreRef(V.isUnknown() - ? VBFactory.remove(B, R).getRoot() - : VBFactory.add(B, R, V).getRoot(), *this); -} - -StoreRef BasicStoreManager::Remove(Store store, Loc loc) { - switch (loc.getSubKind()) { - case loc::MemRegionKind: { - const MemRegion* R = cast<loc::MemRegionVal>(loc).getRegion(); - - if (!(isa<VarRegion>(R) || isa<ObjCIvarRegion>(R) || - isa<CXXThisRegion>(R))) - return StoreRef(store, *this); - - return StoreRef(VBFactory.remove(GetBindings(store), R).getRoot(), *this); - } - default: - assert ("Remove for given Loc type not yet implemented."); - return StoreRef(store, *this); - } -} - -StoreRef BasicStoreManager::removeDeadBindings(Store store, - const StackFrameContext *LCtx, - SymbolReaper& SymReaper, - llvm::SmallVectorImpl<const MemRegion*>& RegionRoots) -{ - BindingsTy B = GetBindings(store); - typedef SVal::symbol_iterator symbol_iterator; - - // Iterate over the variable bindings. - for (BindingsTy::iterator I=B.begin(), E=B.end(); I!=E ; ++I) { - if (const VarRegion *VR = dyn_cast<VarRegion>(I.getKey())) { - if (SymReaper.isLive(VR)) - RegionRoots.push_back(VR); - else - continue; - } - else if (isa<ObjCIvarRegion>(I.getKey()) || - isa<NonStaticGlobalSpaceRegion>(I.getKey()) || - isa<CXXThisRegion>(I.getKey())) - RegionRoots.push_back(I.getKey()); - else - continue; - - // Mark the bindings in the data as live. - SVal X = I.getData(); - for (symbol_iterator SI=X.symbol_begin(), SE=X.symbol_end(); SI!=SE; ++SI) - SymReaper.markLive(*SI); - } - - // Scan for live variables and live symbols. - llvm::SmallPtrSet<const MemRegion*, 10> Marked; - - while (!RegionRoots.empty()) { - const MemRegion* MR = RegionRoots.back(); - RegionRoots.pop_back(); - - while (MR) { - if (const SymbolicRegion* SymR = dyn_cast<SymbolicRegion>(MR)) { - SymReaper.markLive(SymR->getSymbol()); - break; - } - else if (isa<VarRegion>(MR) || isa<ObjCIvarRegion>(MR) || - isa<NonStaticGlobalSpaceRegion>(MR) || isa<CXXThisRegion>(MR)) { - if (Marked.count(MR)) - break; - - Marked.insert(MR); - SVal X = Retrieve(store, loc::MemRegionVal(MR)); - - // FIXME: We need to handle symbols nested in region definitions. - for (symbol_iterator SI=X.symbol_begin(),SE=X.symbol_end();SI!=SE;++SI) - SymReaper.markLive(*SI); - - if (!isa<loc::MemRegionVal>(X)) - break; - - const loc::MemRegionVal& LVD = cast<loc::MemRegionVal>(X); - RegionRoots.push_back(LVD.getRegion()); - break; - } - else if (const SubRegion* R = dyn_cast<SubRegion>(MR)) - MR = R->getSuperRegion(); - else - break; - } - } - - // Remove dead variable bindings. - StoreRef newStore(store, *this); - for (BindingsTy::iterator I=B.begin(), E=B.end(); I!=E ; ++I) { - const MemRegion* R = I.getKey(); - - if (!Marked.count(R)) { - newStore = Remove(newStore.getStore(), svalBuilder.makeLoc(R)); - SVal X = I.getData(); - - for (symbol_iterator SI=X.symbol_begin(), SE=X.symbol_end(); SI!=SE; ++SI) - SymReaper.maybeDead(*SI); - } - } - - return newStore; -} - -StoreRef BasicStoreManager::scanForIvars(Stmt *B, const Decl* SelfDecl, - const MemRegion *SelfRegion, - Store St) { - - StoreRef newStore(St, *this); - - for (Stmt::child_iterator CI=B->child_begin(), CE=B->child_end(); - CI != CE; ++CI) { - - if (!*CI) - continue; - - // Check if the statement is an ivar reference. We only - // care about self.ivar. - if (ObjCIvarRefExpr *IV = dyn_cast<ObjCIvarRefExpr>(*CI)) { - const Expr *Base = IV->getBase()->IgnoreParenCasts(); - if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(Base)) { - if (DR->getDecl() == SelfDecl) { - const ObjCIvarRegion *IVR = MRMgr.getObjCIvarRegion(IV->getDecl(), - SelfRegion); - SVal X = svalBuilder.getRegionValueSymbolVal(IVR); - newStore = Bind(newStore.getStore(), svalBuilder.makeLoc(IVR), X); - } - } - } - else - newStore = scanForIvars(*CI, SelfDecl, SelfRegion, newStore.getStore()); - } - - return newStore; -} - -StoreRef BasicStoreManager::getInitialStore(const LocationContext *InitLoc) { - // The LiveVariables information already has a compilation of all VarDecls - // used in the function. Iterate through this set, and "symbolicate" - // any VarDecl whose value originally comes from outside the function. - typedef LiveVariables::AnalysisDataTy LVDataTy; - LVDataTy& D = InitLoc->getLiveVariables()->getAnalysisData(); - StoreRef St(VBFactory.getEmptyMap().getRoot(), *this); - - for (LVDataTy::decl_iterator I=D.begin_decl(), E=D.end_decl(); I != E; ++I) { - const NamedDecl* ND = I->first; - - // Handle implicit parameters. - if (const ImplicitParamDecl* PD = dyn_cast<ImplicitParamDecl>(ND)) { - const Decl& CD = *InitLoc->getDecl(); - if (const ObjCMethodDecl* MD = dyn_cast<ObjCMethodDecl>(&CD)) { - if (MD->getSelfDecl() == PD) { - // FIXME: Add type constraints (when they become available) to - // SelfRegion? (i.e., it implements MD->getClassInterface()). - const VarRegion *VR = MRMgr.getVarRegion(PD, InitLoc); - const MemRegion *SelfRegion = - svalBuilder.getRegionValueSymbolVal(VR).getAsRegion(); - assert(SelfRegion); - St = Bind(St.getStore(), svalBuilder.makeLoc(VR), - loc::MemRegionVal(SelfRegion)); - // Scan the method for ivar references. While this requires an - // entire AST scan, the cost should not be high in practice. - St = scanForIvars(MD->getBody(), PD, SelfRegion, St.getStore()); - } - } - } - } - - if (const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(InitLoc->getDecl())) { - // For C++ non-static member variables, add a symbolic region for 'this' in - // the initial stack frame. - if (MD->isInstance()) { - QualType ThisT = MD->getThisType(StateMgr.getContext()); - MemRegionManager &RegMgr = svalBuilder.getRegionManager(); - const CXXThisRegion *ThisR = RegMgr.getCXXThisRegion(ThisT, InitLoc); - SVal ThisV = svalBuilder.getRegionValueSymbolVal(ThisR); - St = Bind(St.getStore(), svalBuilder.makeLoc(ThisR), ThisV); - } - } - - return St; -} - -StoreRef BasicStoreManager::BindDeclInternal(Store store, const VarRegion* VR, - SVal* InitVal) { - - BasicValueFactory& BasicVals = StateMgr.getBasicVals(); - const VarDecl *VD = VR->getDecl(); - StoreRef newStore(store, *this); - - // BasicStore does not model arrays and structs. - if (VD->getType()->isArrayType() || VD->getType()->isStructureOrClassType()) - return newStore; - - if (VD->hasGlobalStorage()) { - // Handle variables with global storage: extern, static, PrivateExtern. - - // FIXME:: static variables may have an initializer, but the second time a - // function is called those values may not be current. Currently, a function - // will not be called more than once. - - // Static global variables should not be visited here. - assert(!(VD->getStorageClass() == SC_Static && - VD->isFileVarDecl())); - - // Process static variables. - if (VD->getStorageClass() == SC_Static) { - // C99: 6.7.8 Initialization - // If an object that has static storage duration is not initialized - // explicitly, then: - // -if it has pointer type, it is initialized to a null pointer; - // -if it has arithmetic type, it is initialized to (positive or - // unsigned) zero; - if (!InitVal) { - QualType T = VD->getType(); - if (Loc::isLocType(T)) - newStore = Bind(store, loc::MemRegionVal(VR), - loc::ConcreteInt(BasicVals.getValue(0, T))); - else if (T->isIntegerType() && T->isScalarType()) - newStore = Bind(store, loc::MemRegionVal(VR), - nonloc::ConcreteInt(BasicVals.getValue(0, T))); - } else { - newStore = Bind(store, loc::MemRegionVal(VR), *InitVal); - } - } - } else { - // Process local scalar variables. - QualType T = VD->getType(); - // BasicStore only supports scalars. - if ((T->isScalarType() || T->isReferenceType()) && - svalBuilder.getSymbolManager().canSymbolicate(T)) { - SVal V = InitVal ? *InitVal : UndefinedVal(); - newStore = Bind(store, loc::MemRegionVal(VR), V); - } - } - - return newStore; -} - -void BasicStoreManager::print(Store store, llvm::raw_ostream& Out, - const char* nl, const char *sep) { - - BindingsTy B = GetBindings(store); - Out << "Variables:" << nl; - - bool isFirst = true; - - for (BindingsTy::iterator I=B.begin(), E=B.end(); I != E; ++I) { - if (isFirst) - isFirst = false; - else - Out << nl; - - Out << ' ' << I.getKey() << " : " << I.getData(); - } -} - - -void BasicStoreManager::iterBindings(Store store, BindingsHandler& f) { - BindingsTy B = GetBindings(store); - - for (BindingsTy::iterator I=B.begin(), E=B.end(); I != E; ++I) - if (!f.HandleBinding(*this, store, I.getKey(), I.getData())) - return; - -} - -StoreManager::BindingsHandler::~BindingsHandler() {} - -//===----------------------------------------------------------------------===// -// Binding invalidation. -//===----------------------------------------------------------------------===// - - -StoreRef BasicStoreManager::invalidateRegions(Store store, - const MemRegion * const *I, - const MemRegion * const *End, - const Expr *E, unsigned Count, - InvalidatedSymbols &IS, - bool invalidateGlobals, - InvalidatedRegions *Regions) { - StoreRef newStore(store, *this); - - if (invalidateGlobals) { - BindingsTy B = GetBindings(store); - for (BindingsTy::iterator I=B.begin(), End=B.end(); I != End; ++I) { - const MemRegion *R = I.getKey(); - if (isa<NonStaticGlobalSpaceRegion>(R->getMemorySpace())) - newStore = invalidateRegion(newStore.getStore(), R, E, Count, IS); - } - } - - for ( ; I != End ; ++I) { - const MemRegion *R = *I; - // Don't invalidate globals twice. - if (invalidateGlobals) { - if (isa<NonStaticGlobalSpaceRegion>(R->getMemorySpace())) - continue; - } - newStore = invalidateRegion(newStore.getStore(), *I, E, Count, IS); - if (Regions) - Regions->push_back(R); - } - - // FIXME: This is copy-and-paste from RegionStore.cpp. - if (invalidateGlobals) { - // Bind the non-static globals memory space to a new symbol that we will - // use to derive the bindings for all non-static globals. - const GlobalsSpaceRegion *GS = MRMgr.getGlobalsRegion(); - SVal V = - svalBuilder.getConjuredSymbolVal(/* SymbolTag = */ (void*) GS, E, - /* symbol type, doesn't matter */ Ctx.IntTy, - Count); - - newStore = Bind(newStore.getStore(), loc::MemRegionVal(GS), V); - if (Regions) - Regions->push_back(GS); - } - - return newStore; -} - - -StoreRef BasicStoreManager::invalidateRegion(Store store, - const MemRegion *R, - const Expr *E, - unsigned Count, - InvalidatedSymbols &IS) { - R = R->StripCasts(); - - if (!(isa<VarRegion>(R) || isa<ObjCIvarRegion>(R))) - return StoreRef(store, *this); - - BindingsTy B = GetBindings(store); - if (BindingsTy::data_type *Val = B.lookup(R)) { - if (SymbolRef Sym = Val->getAsSymbol()) - IS.insert(Sym); - } - - QualType T = cast<TypedRegion>(R)->getValueType(); - SVal V = svalBuilder.getConjuredSymbolVal(R, E, T, Count); - return Bind(store, loc::MemRegionVal(R), V); -} diff --git a/lib/StaticAnalyzer/Core/BasicValueFactory.cpp b/lib/StaticAnalyzer/Core/BasicValueFactory.cpp index 0ed4ff143171..fe96700772d6 100644 --- a/lib/StaticAnalyzer/Core/BasicValueFactory.cpp +++ b/lib/StaticAnalyzer/Core/BasicValueFactory.cpp @@ -27,7 +27,7 @@ void CompoundValData::Profile(llvm::FoldingSetNodeID& ID, QualType T, void LazyCompoundValData::Profile(llvm::FoldingSetNodeID& ID, const StoreRef &store, - const TypedRegion *region) { + const TypedValueRegion *region) { ID.AddPointer(store.getStore()); ID.AddPointer(region); } @@ -70,7 +70,7 @@ BasicValueFactory::~BasicValueFactory() { const llvm::APSInt& BasicValueFactory::getValue(const llvm::APSInt& X) { llvm::FoldingSetNodeID ID; - void* InsertPos; + void *InsertPos; typedef llvm::FoldingSetNodeWrapper<llvm::APSInt> FoldNodeTy; X.Profile(ID); @@ -113,7 +113,7 @@ BasicValueFactory::getCompoundValData(QualType T, llvm::FoldingSetNodeID ID; CompoundValData::Profile(ID, T, Vals); - void* InsertPos; + void *InsertPos; CompoundValData* D = CompoundValDataSet.FindNodeOrInsertPos(ID, InsertPos); @@ -128,10 +128,10 @@ BasicValueFactory::getCompoundValData(QualType T, const LazyCompoundValData* BasicValueFactory::getLazyCompoundValData(const StoreRef &store, - const TypedRegion *region) { + const TypedValueRegion *region) { llvm::FoldingSetNodeID ID; LazyCompoundValData::Profile(ID, store, region); - void* InsertPos; + void *InsertPos; LazyCompoundValData *D = LazyCompoundValDataSet.FindNodeOrInsertPos(ID, InsertPos); @@ -243,7 +243,7 @@ BasicValueFactory::getPersistentSValWithData(const SVal& V, uintptr_t Data) { if (!PersistentSVals) PersistentSVals = new PersistentSValsTy(); llvm::FoldingSetNodeID ID; - void* InsertPos; + void *InsertPos; V.Profile(ID); ID.AddPointer((void*) Data); @@ -268,7 +268,7 @@ BasicValueFactory::getPersistentSValPair(const SVal& V1, const SVal& V2) { if (!PersistentSValPairs) PersistentSValPairs = new PersistentSValPairsTy(); llvm::FoldingSetNodeID ID; - void* InsertPos; + void *InsertPos; V1.Profile(ID); V2.Profile(ID); diff --git a/lib/StaticAnalyzer/Core/BlockCounter.cpp b/lib/StaticAnalyzer/Core/BlockCounter.cpp index ed52b6b012b7..74d761e1ecc1 100644 --- a/lib/StaticAnalyzer/Core/BlockCounter.cpp +++ b/lib/StaticAnalyzer/Core/BlockCounter.cpp @@ -48,11 +48,11 @@ public: typedef llvm::ImmutableMap<CountKey, unsigned> CountMap; -static inline CountMap GetMap(void* D) { +static inline CountMap GetMap(void *D) { return CountMap(static_cast<CountMap::TreeTy*>(D)); } -static inline CountMap::Factory& GetFactory(void* F) { +static inline CountMap::Factory& GetFactory(void *F) { return *static_cast<CountMap::Factory*>(F); } diff --git a/lib/StaticAnalyzer/Core/BugReporter.cpp b/lib/StaticAnalyzer/Core/BugReporter.cpp index 8b5d383ed07f..fbbdb040e4f2 100644 --- a/lib/StaticAnalyzer/Core/BugReporter.cpp +++ b/lib/StaticAnalyzer/Core/BugReporter.cpp @@ -33,52 +33,31 @@ using namespace clang; using namespace ento; BugReporterVisitor::~BugReporterVisitor() {} -BugReporterContext::~BugReporterContext() { - for (visitor_iterator I = visitor_begin(), E = visitor_end(); I != E; ++I) - if ((*I)->isOwnedByReporterContext()) delete *I; -} - -void BugReporterContext::addVisitor(BugReporterVisitor* visitor) { - if (!visitor) - return; - - llvm::FoldingSetNodeID ID; - visitor->Profile(ID); - void *InsertPos; - - if (CallbacksSet.FindNodeOrInsertPos(ID, InsertPos)) { - delete visitor; - return; - } - - CallbacksSet.InsertNode(visitor, InsertPos); - Callbacks = F.add(visitor, Callbacks); -} //===----------------------------------------------------------------------===// // Helper routines for walking the ExplodedGraph and fetching statements. //===----------------------------------------------------------------------===// -static inline const Stmt* GetStmt(const ProgramPoint &P) { +static inline const Stmt *GetStmt(const ProgramPoint &P) { if (const StmtPoint* SP = dyn_cast<StmtPoint>(&P)) return SP->getStmt(); - else if (const BlockEdge* BE = dyn_cast<BlockEdge>(&P)) + else if (const BlockEdge *BE = dyn_cast<BlockEdge>(&P)) return BE->getSrc()->getTerminator(); return 0; } static inline const ExplodedNode* -GetPredecessorNode(const ExplodedNode* N) { +GetPredecessorNode(const ExplodedNode *N) { return N->pred_empty() ? NULL : *(N->pred_begin()); } static inline const ExplodedNode* -GetSuccessorNode(const ExplodedNode* N) { +GetSuccessorNode(const ExplodedNode *N) { return N->succ_empty() ? NULL : *(N->succ_begin()); } -static const Stmt* GetPreviousStmt(const ExplodedNode* N) { +static const Stmt *GetPreviousStmt(const ExplodedNode *N) { for (N = GetPredecessorNode(N); N; N = GetPredecessorNode(N)) if (const Stmt *S = GetStmt(N->getLocation())) return S; @@ -86,7 +65,7 @@ static const Stmt* GetPreviousStmt(const ExplodedNode* N) { return 0; } -static const Stmt* GetNextStmt(const ExplodedNode* N) { +static const Stmt *GetNextStmt(const ExplodedNode *N) { for (N = GetSuccessorNode(N); N; N = GetSuccessorNode(N)) if (const Stmt *S = GetStmt(N->getLocation())) { // Check if the statement is '?' or '&&'/'||'. These are "merges", @@ -104,11 +83,6 @@ static const Stmt* GetNextStmt(const ExplodedNode* N) { default: break; } - - // Some expressions don't have locations. - if (S->getLocStart().isInvalid()) - continue; - return S; } @@ -116,7 +90,7 @@ static const Stmt* GetNextStmt(const ExplodedNode* N) { } static inline const Stmt* -GetCurrentOrPreviousStmt(const ExplodedNode* N) { +GetCurrentOrPreviousStmt(const ExplodedNode *N) { if (const Stmt *S = GetStmt(N->getLocation())) return S; @@ -124,7 +98,7 @@ GetCurrentOrPreviousStmt(const ExplodedNode* N) { } static inline const Stmt* -GetCurrentOrNextStmt(const ExplodedNode* N) { +GetCurrentOrNextStmt(const ExplodedNode *N) { if (const Stmt *S = GetStmt(N->getLocation())) return S; @@ -145,7 +119,7 @@ public: NodeMapClosure(NodeBackMap *m) : M(*m) {} ~NodeMapClosure() {} - const ExplodedNode* getOriginalNode(const ExplodedNode* N) { + const ExplodedNode *getOriginalNode(const ExplodedNode *N) { NodeBackMap::iterator I = M.find(N); return I == M.end() ? 0 : I->second; } @@ -153,25 +127,29 @@ public: class PathDiagnosticBuilder : public BugReporterContext { BugReport *R; - PathDiagnosticClient *PDC; + PathDiagnosticConsumer *PDC; llvm::OwningPtr<ParentMap> PM; NodeMapClosure NMC; public: PathDiagnosticBuilder(GRBugReporter &br, BugReport *r, NodeBackMap *Backmap, - PathDiagnosticClient *pdc) + PathDiagnosticConsumer *pdc) : BugReporterContext(br), - R(r), PDC(pdc), NMC(Backmap) { - addVisitor(R); - } + R(r), PDC(pdc), NMC(Backmap) {} - PathDiagnosticLocation ExecutionContinues(const ExplodedNode* N); + PathDiagnosticLocation ExecutionContinues(const ExplodedNode *N); - PathDiagnosticLocation ExecutionContinues(llvm::raw_string_ostream& os, - const ExplodedNode* N); + PathDiagnosticLocation ExecutionContinues(llvm::raw_string_ostream &os, + const ExplodedNode *N); + + BugReport *getBugReport() { return R; } Decl const &getCodeDecl() { return R->getErrorNode()->getCodeDecl(); } + const LocationContext* getLocationContext() { + return R->getErrorNode()->getLocationContext(); + } + ParentMap& getParentMap() { return R->getErrorNode()->getParentMap(); } const Stmt *getParent(const Stmt *S) { @@ -182,8 +160,8 @@ public: PathDiagnosticLocation getEnclosingStmtLocation(const Stmt *S); - PathDiagnosticClient::PathGenerationScheme getGenerationScheme() const { - return PDC ? PDC->getGenerationScheme() : PathDiagnosticClient::Extensive; + PathDiagnosticConsumer::PathGenerationScheme getGenerationScheme() const { + return PDC ? PDC->getGenerationScheme() : PathDiagnosticConsumer::Extensive; } bool supportsLogicalOpControlFlow() const { @@ -193,17 +171,17 @@ public: } // end anonymous namespace PathDiagnosticLocation -PathDiagnosticBuilder::ExecutionContinues(const ExplodedNode* N) { +PathDiagnosticBuilder::ExecutionContinues(const ExplodedNode *N) { if (const Stmt *S = GetNextStmt(N)) - return PathDiagnosticLocation(S, getSourceManager()); + return PathDiagnosticLocation(S, getSourceManager(), getLocationContext()); - return FullSourceLoc(N->getLocationContext()->getDecl()->getBodyRBrace(), - getSourceManager()); + return PathDiagnosticLocation::createDeclEnd(N->getLocationContext(), + getSourceManager()); } PathDiagnosticLocation -PathDiagnosticBuilder::ExecutionContinues(llvm::raw_string_ostream& os, - const ExplodedNode* N) { +PathDiagnosticBuilder::ExecutionContinues(llvm::raw_string_ostream &os, + const ExplodedNode *N) { // Slow, but probably doesn't matter. if (os.str().empty()) @@ -213,7 +191,7 @@ PathDiagnosticBuilder::ExecutionContinues(llvm::raw_string_ostream& os, if (Loc.asStmt()) os << "Execution continues on line " - << getSourceManager().getInstantiationLineNumber(Loc.asLocation()) + << getSourceManager().getExpansionLineNumber(Loc.asLocation()) << '.'; else { os << "Execution jumps to the end of the "; @@ -253,9 +231,10 @@ static bool IsNested(const Stmt *S, ParentMap &PM) { PathDiagnosticLocation PathDiagnosticBuilder::getEnclosingStmtLocation(const Stmt *S) { - assert(S && "Null Stmt* passed to getEnclosingStmtLocation"); + assert(S && "Null Stmt *passed to getEnclosingStmtLocation"); ParentMap &P = getParentMap(); SourceManager &SMgr = getSourceManager(); + const LocationContext *LC = getLocationContext(); while (IsNested(S, P)) { const Stmt *Parent = P.getParentIgnoreParens(S); @@ -267,44 +246,44 @@ PathDiagnosticBuilder::getEnclosingStmtLocation(const Stmt *S) { case Stmt::BinaryOperatorClass: { const BinaryOperator *B = cast<BinaryOperator>(Parent); if (B->isLogicalOp()) - return PathDiagnosticLocation(S, SMgr); + return PathDiagnosticLocation(S, SMgr, LC); break; } case Stmt::CompoundStmtClass: case Stmt::StmtExprClass: - return PathDiagnosticLocation(S, SMgr); + return PathDiagnosticLocation(S, SMgr, LC); case Stmt::ChooseExprClass: // Similar to '?' if we are referring to condition, just have the edge // point to the entire choose expression. if (cast<ChooseExpr>(Parent)->getCond() == S) - return PathDiagnosticLocation(Parent, SMgr); + return PathDiagnosticLocation(Parent, SMgr, LC); else - return PathDiagnosticLocation(S, SMgr); + return PathDiagnosticLocation(S, SMgr, LC); case Stmt::BinaryConditionalOperatorClass: case Stmt::ConditionalOperatorClass: // For '?', if we are referring to condition, just have the edge point // to the entire '?' expression. if (cast<AbstractConditionalOperator>(Parent)->getCond() == S) - return PathDiagnosticLocation(Parent, SMgr); + return PathDiagnosticLocation(Parent, SMgr, LC); else - return PathDiagnosticLocation(S, SMgr); + return PathDiagnosticLocation(S, SMgr, LC); case Stmt::DoStmtClass: - return PathDiagnosticLocation(S, SMgr); + return PathDiagnosticLocation(S, SMgr, LC); case Stmt::ForStmtClass: if (cast<ForStmt>(Parent)->getBody() == S) - return PathDiagnosticLocation(S, SMgr); + return PathDiagnosticLocation(S, SMgr, LC); break; case Stmt::IfStmtClass: if (cast<IfStmt>(Parent)->getCond() != S) - return PathDiagnosticLocation(S, SMgr); + return PathDiagnosticLocation(S, SMgr, LC); break; case Stmt::ObjCForCollectionStmtClass: if (cast<ObjCForCollectionStmt>(Parent)->getBody() == S) - return PathDiagnosticLocation(S, SMgr); + return PathDiagnosticLocation(S, SMgr, LC); break; case Stmt::WhileStmtClass: if (cast<WhileStmt>(Parent)->getCond() != S) - return PathDiagnosticLocation(S, SMgr); + return PathDiagnosticLocation(S, SMgr, LC); break; default: break; @@ -322,7 +301,7 @@ PathDiagnosticBuilder::getEnclosingStmtLocation(const Stmt *S) { switch (Parent->getStmtClass()) { case Stmt::ForStmtClass: case Stmt::ObjCForCollectionStmtClass: - return PathDiagnosticLocation(Parent, SMgr); + return PathDiagnosticLocation(Parent, SMgr, LC); default: break; } @@ -335,20 +314,20 @@ PathDiagnosticBuilder::getEnclosingStmtLocation(const Stmt *S) { if (const ForStmt *FS = dyn_cast_or_null<ForStmt>(P.getParentIgnoreParens(S))) { if (FS->getInit() == S) - return PathDiagnosticLocation(FS, SMgr); + return PathDiagnosticLocation(FS, SMgr, LC); } } - return PathDiagnosticLocation(S, SMgr); + return PathDiagnosticLocation(S, SMgr, LC); } //===----------------------------------------------------------------------===// // ScanNotableSymbols: closure-like callback for scanning Store bindings. //===----------------------------------------------------------------------===// -static const VarDecl* -GetMostRecentVarDeclBinding(const ExplodedNode* N, - GRStateManager& VMgr, SVal X) { +static const VarDecl* GetMostRecentVarDeclBinding(const ExplodedNode *N, + ProgramStateManager& VMgr, + SVal X) { for ( ; N ; N = N->pred_empty() ? 0 : *N->pred_begin()) { @@ -357,7 +336,7 @@ GetMostRecentVarDeclBinding(const ExplodedNode* N, if (!isa<PostStmt>(P)) continue; - const DeclRefExpr* DR = dyn_cast<DeclRefExpr>(cast<PostStmt>(P).getStmt()); + const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(cast<PostStmt>(P).getStmt()); if (!DR) continue; @@ -367,7 +346,7 @@ GetMostRecentVarDeclBinding(const ExplodedNode* N, if (X != Y) continue; - const VarDecl* VD = dyn_cast<VarDecl>(DR->getDecl()); + const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl()); if (!VD) continue; @@ -383,19 +362,29 @@ class NotableSymbolHandler : public StoreManager::BindingsHandler { SymbolRef Sym; - const GRState* PrevSt; - const Stmt* S; - GRStateManager& VMgr; - const ExplodedNode* Pred; + const ProgramState *PrevSt; + const Stmt *S; + ProgramStateManager& VMgr; + const ExplodedNode *Pred; PathDiagnostic& PD; BugReporter& BR; public: - NotableSymbolHandler(SymbolRef sym, const GRState* prevst, const Stmt* s, - GRStateManager& vmgr, const ExplodedNode* pred, - PathDiagnostic& pd, BugReporter& br) - : Sym(sym), PrevSt(prevst), S(s), VMgr(vmgr), Pred(pred), PD(pd), BR(br) {} + NotableSymbolHandler(SymbolRef sym, + const ProgramState *prevst, + const Stmt *s, + ProgramStateManager& vmgr, + const ExplodedNode *pred, + PathDiagnostic& pd, + BugReporter& br) + : Sym(sym), + PrevSt(prevst), + S(s), + VMgr(vmgr), + Pred(pred), + PD(pd), + BR(br) {} bool HandleBinding(StoreManager& SMgr, Store store, const MemRegion* R, SVal V) { @@ -422,14 +411,14 @@ public: return true; // What variable did we assign to? - DeclRefExpr* DR = dyn_cast<DeclRefExpr>(B->getLHS()->IgnoreParenCasts()); + DeclRefExpr *DR = dyn_cast<DeclRefExpr>(B->getLHS()->IgnoreParenCasts()); if (!DR) return true; VD = dyn_cast<VarDecl>(DR->getDecl()); } - else if (const DeclStmt* DS = dyn_cast<DeclStmt>(S)) { + else if (const DeclStmt *DS = dyn_cast<DeclStmt>(S)) { // FIXME: Eventually CFGs won't have DeclStmts. Right now we // assume that each DeclStmt has a single Decl. This invariant // holds by construction in the CFG. @@ -440,19 +429,20 @@ public: return true; // What is the most recently referenced variable with this binding? - const VarDecl* MostRecent = GetMostRecentVarDeclBinding(Pred, VMgr, V); + const VarDecl *MostRecent = GetMostRecentVarDeclBinding(Pred, VMgr, V); if (!MostRecent) return true; // Create the diagnostic. - FullSourceLoc L(S->getLocStart(), BR.getSourceManager()); - if (Loc::isLocType(VD->getType())) { - std::string msg = "'" + std::string(VD->getNameAsString()) + - "' now aliases '" + MostRecent->getNameAsString() + "'"; - - PD.push_front(new PathDiagnosticEventPiece(L, msg)); + llvm::SmallString<64> buf; + llvm::raw_svector_ostream os(buf); + os << '\'' << *VD << "' now aliases '" << *MostRecent << '\''; + PathDiagnosticLocation L = + PathDiagnosticLocation::createBegin(S, BR.getSourceManager(), + Pred->getLocationContext()); + PD.push_front(new PathDiagnosticEventPiece(L, os.str())); } return true; @@ -460,20 +450,20 @@ public: }; } -static void HandleNotableSymbol(const ExplodedNode* N, - const Stmt* S, +static void HandleNotableSymbol(const ExplodedNode *N, + const Stmt *S, SymbolRef Sym, BugReporter& BR, PathDiagnostic& PD) { - const ExplodedNode* Pred = N->pred_empty() ? 0 : *N->pred_begin(); - const GRState* PrevSt = Pred ? Pred->getState() : 0; + const ExplodedNode *Pred = N->pred_empty() ? 0 : *N->pred_begin(); + const ProgramState *PrevSt = Pred ? Pred->getState() : 0; if (!PrevSt) return; // Look at the region bindings of the current state that map to the // specified symbol. Are any of them not in the previous state? - GRStateManager& VMgr = cast<GRBugReporter>(BR).getStateManager(); + ProgramStateManager& VMgr = cast<GRBugReporter>(BR).getStateManager(); NotableSymbolHandler H(Sym, PrevSt, S, VMgr, Pred, PD, BR); cast<GRBugReporter>(BR).getStateManager().iterBindings(N->getState(), H); } @@ -483,13 +473,13 @@ class ScanNotableSymbols : public StoreManager::BindingsHandler { llvm::SmallSet<SymbolRef, 10> AlreadyProcessed; - const ExplodedNode* N; - const Stmt* S; + const ExplodedNode *N; + const Stmt *S; GRBugReporter& BR; PathDiagnostic& PD; public: - ScanNotableSymbols(const ExplodedNode* n, const Stmt* s, + ScanNotableSymbols(const ExplodedNode *n, const Stmt *s, GRBugReporter& br, PathDiagnostic& pd) : N(n), S(s), BR(br), PD(pd) {} @@ -526,7 +516,8 @@ static void GenerateMinimalPathDiagnostic(PathDiagnostic& PD, const ExplodedNode *N) { SourceManager& SMgr = PDB.getSourceManager(); - const ExplodedNode* NextNode = N->pred_empty() + const LocationContext *LC = PDB.getLocationContext(); + const ExplodedNode *NextNode = N->pred_empty() ? NULL : *(N->pred_begin()); while (NextNode) { N = NextNode; @@ -534,15 +525,17 @@ static void GenerateMinimalPathDiagnostic(PathDiagnostic& PD, ProgramPoint P = N->getLocation(); - if (const BlockEdge* BE = dyn_cast<BlockEdge>(&P)) { - const CFGBlock* Src = BE->getSrc(); - const CFGBlock* Dst = BE->getDst(); - const Stmt* T = Src->getTerminator(); + if (const BlockEdge *BE = dyn_cast<BlockEdge>(&P)) { + const CFGBlock *Src = BE->getSrc(); + const CFGBlock *Dst = BE->getDst(); + const Stmt *T = Src->getTerminator(); if (!T) continue; - FullSourceLoc Start(T->getLocStart(), SMgr); + PathDiagnosticLocation Start = + PathDiagnosticLocation::createBegin(T, SMgr, + N->getLocationContext()); switch (T->getStmtClass()) { default: @@ -550,7 +543,7 @@ static void GenerateMinimalPathDiagnostic(PathDiagnostic& PD, case Stmt::GotoStmtClass: case Stmt::IndirectGotoStmtClass: { - const Stmt* S = GetNextStmt(N); + const Stmt *S = GetNextStmt(N); if (!S) continue; @@ -560,7 +553,7 @@ static void GenerateMinimalPathDiagnostic(PathDiagnostic& PD, const PathDiagnosticLocation &End = PDB.getEnclosingStmtLocation(S); os << "Control jumps to line " - << End.asLocation().getInstantiationLineNumber(); + << End.asLocation().getExpansionLineNumber(); PD.push_front(new PathDiagnosticControlFlowPiece(Start, End, os.str())); break; @@ -571,45 +564,45 @@ static void GenerateMinimalPathDiagnostic(PathDiagnostic& PD, std::string sbuf; llvm::raw_string_ostream os(sbuf); - if (const Stmt* S = Dst->getLabel()) { - PathDiagnosticLocation End(S, SMgr); + if (const Stmt *S = Dst->getLabel()) { + PathDiagnosticLocation End(S, SMgr, LC); switch (S->getStmtClass()) { default: os << "No cases match in the switch statement. " "Control jumps to line " - << End.asLocation().getInstantiationLineNumber(); + << End.asLocation().getExpansionLineNumber(); break; case Stmt::DefaultStmtClass: os << "Control jumps to the 'default' case at line " - << End.asLocation().getInstantiationLineNumber(); + << End.asLocation().getExpansionLineNumber(); break; case Stmt::CaseStmtClass: { os << "Control jumps to 'case "; - const CaseStmt* Case = cast<CaseStmt>(S); - const Expr* LHS = Case->getLHS()->IgnoreParenCasts(); + const CaseStmt *Case = cast<CaseStmt>(S); + const Expr *LHS = Case->getLHS()->IgnoreParenCasts(); // Determine if it is an enum. bool GetRawInt = true; - if (const DeclRefExpr* DR = dyn_cast<DeclRefExpr>(LHS)) { + if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(LHS)) { // FIXME: Maybe this should be an assertion. Are there cases // were it is not an EnumConstantDecl? - const EnumConstantDecl* D = + const EnumConstantDecl *D = dyn_cast<EnumConstantDecl>(DR->getDecl()); if (D) { GetRawInt = false; - os << D; + os << *D; } } if (GetRawInt) - os << LHS->EvaluateAsInt(PDB.getASTContext()); + os << LHS->EvaluateKnownConstInt(PDB.getASTContext()); os << ":' at line " - << End.asLocation().getInstantiationLineNumber(); + << End.asLocation().getExpansionLineNumber(); break; } } @@ -673,14 +666,15 @@ static void GenerateMinimalPathDiagnostic(PathDiagnostic& PD, if (*(Src->succ_begin()+1) == Dst) { os << "false"; - PathDiagnosticLocation End(B->getLHS(), SMgr); - PathDiagnosticLocation Start(B->getOperatorLoc(), SMgr); + PathDiagnosticLocation End(B->getLHS(), SMgr, LC); + PathDiagnosticLocation Start = + PathDiagnosticLocation::createOperatorLoc(B, SMgr); PD.push_front(new PathDiagnosticControlFlowPiece(Start, End, os.str())); } else { os << "true"; - PathDiagnosticLocation Start(B->getLHS(), SMgr); + PathDiagnosticLocation Start(B->getLHS(), SMgr, LC); PathDiagnosticLocation End = PDB.ExecutionContinues(N); PD.push_front(new PathDiagnosticControlFlowPiece(Start, End, os.str())); @@ -692,15 +686,16 @@ static void GenerateMinimalPathDiagnostic(PathDiagnostic& PD, if (*(Src->succ_begin()+1) == Dst) { os << "false"; - PathDiagnosticLocation Start(B->getLHS(), SMgr); + PathDiagnosticLocation Start(B->getLHS(), SMgr, LC); PathDiagnosticLocation End = PDB.ExecutionContinues(N); PD.push_front(new PathDiagnosticControlFlowPiece(Start, End, os.str())); } else { os << "true"; - PathDiagnosticLocation End(B->getLHS(), SMgr); - PathDiagnosticLocation Start(B->getOperatorLoc(), SMgr); + PathDiagnosticLocation End(B->getLHS(), SMgr, LC); + PathDiagnosticLocation Start = + PathDiagnosticLocation::createOperatorLoc(B, SMgr); PD.push_front(new PathDiagnosticControlFlowPiece(Start, End, os.str())); } @@ -781,14 +776,16 @@ static void GenerateMinimalPathDiagnostic(PathDiagnostic& PD, } if (NextNode) { - for (BugReporterContext::visitor_iterator I = PDB.visitor_begin(), - E = PDB.visitor_end(); I!=E; ++I) { - if (PathDiagnosticPiece* p = (*I)->VisitNode(N, NextNode, PDB)) + // Add diagnostic pieces from custom visitors. + BugReport *R = PDB.getBugReport(); + for (BugReport::visitor_iterator I = R->visitor_begin(), + E = R->visitor_end(); I!=E; ++I) { + if (PathDiagnosticPiece *p = (*I)->VisitNode(N, NextNode, PDB, *R)) PD.push_front(p); } } - if (const PostStmt* PS = dyn_cast<PostStmt>(&P)) { + if (const PostStmt *PS = dyn_cast<PostStmt>(&P)) { // Scan the region bindings, and see if a "notable" symbol has a new // lval binding. ScanNotableSymbols SNS(N, PS->getStmt(), PDB.getBugReporter(), PD); @@ -882,11 +879,11 @@ class EdgeBuilder { } if (S != Original) - L = PathDiagnosticLocation(S, L.getManager()); + L = PathDiagnosticLocation(S, L.getManager(), PDB.getLocationContext()); } if (firstCharOnly) - L = PathDiagnosticLocation(L.asLocation()); + L = PathDiagnosticLocation::createSingleLocation(L); return L; } @@ -915,17 +912,14 @@ public: ~EdgeBuilder() { while (!CLocs.empty()) popLocation(); - + // Finally, add an initial edge from the start location of the first // statement (if it doesn't already exist). - // FIXME: Should handle CXXTryStmt if analyser starts supporting C++. - if (const CompoundStmt *CS = - dyn_cast_or_null<CompoundStmt>(PDB.getCodeDecl().getBody())) - if (!CS->body_empty()) { - SourceLocation Loc = (*CS->body_begin())->getLocStart(); - rawAddEdge(PathDiagnosticLocation(Loc, PDB.getSourceManager())); - } - + PathDiagnosticLocation L = PathDiagnosticLocation::createDeclBegin( + PDB.getLocationContext(), + PDB.getSourceManager()); + if (L.isValid()) + rawAddEdge(L); } void addEdge(PathDiagnosticLocation NewLoc, bool alwaysAdd = false); @@ -974,15 +968,15 @@ bool EdgeBuilder::containsLocation(const PathDiagnosticLocation &Container, SourceRange ContaineeR = Containee.asRange(); SourceManager &SM = PDB.getSourceManager(); - SourceLocation ContainerRBeg = SM.getInstantiationLoc(ContainerR.getBegin()); - SourceLocation ContainerREnd = SM.getInstantiationLoc(ContainerR.getEnd()); - SourceLocation ContaineeRBeg = SM.getInstantiationLoc(ContaineeR.getBegin()); - SourceLocation ContaineeREnd = SM.getInstantiationLoc(ContaineeR.getEnd()); + SourceLocation ContainerRBeg = SM.getExpansionLoc(ContainerR.getBegin()); + SourceLocation ContainerREnd = SM.getExpansionLoc(ContainerR.getEnd()); + SourceLocation ContaineeRBeg = SM.getExpansionLoc(ContaineeR.getBegin()); + SourceLocation ContaineeREnd = SM.getExpansionLoc(ContaineeR.getEnd()); - unsigned ContainerBegLine = SM.getInstantiationLineNumber(ContainerRBeg); - unsigned ContainerEndLine = SM.getInstantiationLineNumber(ContainerREnd); - unsigned ContaineeBegLine = SM.getInstantiationLineNumber(ContaineeRBeg); - unsigned ContaineeEndLine = SM.getInstantiationLineNumber(ContaineeREnd); + unsigned ContainerBegLine = SM.getExpansionLineNumber(ContainerRBeg); + unsigned ContainerEndLine = SM.getExpansionLineNumber(ContainerREnd); + unsigned ContaineeBegLine = SM.getExpansionLineNumber(ContaineeRBeg); + unsigned ContaineeEndLine = SM.getExpansionLineNumber(ContaineeREnd); assert(ContainerBegLine <= ContainerEndLine); assert(ContaineeBegLine <= ContaineeEndLine); @@ -990,11 +984,11 @@ bool EdgeBuilder::containsLocation(const PathDiagnosticLocation &Container, return (ContainerBegLine <= ContaineeBegLine && ContainerEndLine >= ContaineeEndLine && (ContainerBegLine != ContaineeBegLine || - SM.getInstantiationColumnNumber(ContainerRBeg) <= - SM.getInstantiationColumnNumber(ContaineeRBeg)) && + SM.getExpansionColumnNumber(ContainerRBeg) <= + SM.getExpansionColumnNumber(ContaineeRBeg)) && (ContainerEndLine != ContaineeEndLine || - SM.getInstantiationColumnNumber(ContainerREnd) >= - SM.getInstantiationColumnNumber(ContainerREnd))); + SM.getExpansionColumnNumber(ContainerREnd) >= + SM.getExpansionColumnNumber(ContainerREnd))); } void EdgeBuilder::rawAddEdge(PathDiagnosticLocation NewLoc) { @@ -1010,8 +1004,8 @@ void EdgeBuilder::rawAddEdge(PathDiagnosticLocation NewLoc) { return; // FIXME: Ignore intra-macro edges for now. - if (NewLocClean.asLocation().getInstantiationLoc() == - PrevLocClean.asLocation().getInstantiationLoc()) + if (NewLocClean.asLocation().getExpansionLoc() == + PrevLocClean.asLocation().getExpansionLoc()) return; PD.push_front(new PathDiagnosticControlFlowPiece(NewLocClean, PrevLocClean)); @@ -1099,7 +1093,7 @@ void EdgeBuilder::addContext(const Stmt *S) { if (!S) return; - PathDiagnosticLocation L(S, PDB.getSourceManager()); + PathDiagnosticLocation L(S, PDB.getSourceManager(), PDB.getLocationContext()); while (!CLocs.empty()) { const PathDiagnosticLocation &TopContextLoc = CLocs.back(); @@ -1124,8 +1118,9 @@ static void GenerateExtensivePathDiagnostic(PathDiagnostic& PD, PathDiagnosticBuilder &PDB, const ExplodedNode *N) { EdgeBuilder EB(PD, PDB); + const SourceManager& SM = PDB.getSourceManager(); - const ExplodedNode* NextNode = N->pred_empty() ? NULL : *(N->pred_begin()); + const ExplodedNode *NextNode = N->pred_empty() ? NULL : *(N->pred_begin()); while (NextNode) { N = NextNode; NextNode = GetPredecessorNode(N); @@ -1139,7 +1134,7 @@ static void GenerateExtensivePathDiagnostic(PathDiagnostic& PD, // Are we jumping to the head of a loop? Add a special diagnostic. if (const Stmt *Loop = BE->getDst()->getLoopTarget()) { - PathDiagnosticLocation L(Loop, PDB.getSourceManager()); + PathDiagnosticLocation L(Loop, SM, PDB.getLocationContext()); const CompoundStmt *CS = NULL; if (!Term) { @@ -1157,9 +1152,8 @@ static void GenerateExtensivePathDiagnostic(PathDiagnostic& PD, PD.push_front(p); if (CS) { - PathDiagnosticLocation BL(CS->getRBracLoc(), - PDB.getSourceManager()); - BL = PathDiagnosticLocation(BL.asLocation()); + PathDiagnosticLocation BL = + PathDiagnosticLocation::createEndBrace(CS, SM); EB.addEdge(BL); } } @@ -1188,9 +1182,11 @@ static void GenerateExtensivePathDiagnostic(PathDiagnostic& PD, if (!NextNode) continue; - for (BugReporterContext::visitor_iterator I = PDB.visitor_begin(), - E = PDB.visitor_end(); I!=E; ++I) { - if (PathDiagnosticPiece* p = (*I)->VisitNode(N, NextNode, PDB)) { + // Add pieces from custom visitors. + BugReport *R = PDB.getBugReport(); + for (BugReport::visitor_iterator I = R->visitor_begin(), + E = R->visitor_end(); I!=E; ++I) { + if (PathDiagnosticPiece *p = (*I)->VisitNode(N, NextNode, PDB, *R)) { const PathDiagnosticLocation &Loc = p->getLocation(); EB.addEdge(Loc, true); PD.push_front(p); @@ -1211,14 +1207,58 @@ void BugType::FlushReports(BugReporter &BR) {} //===----------------------------------------------------------------------===// // Methods for BugReport and subclasses. //===----------------------------------------------------------------------===// -BugReport::~BugReport() {} -RangedBugReport::~RangedBugReport() {} -const Stmt* BugReport::getStmt() const { +void BugReport::addVisitor(BugReporterVisitor* visitor) { + if (!visitor) + return; + + llvm::FoldingSetNodeID ID; + visitor->Profile(ID); + void *InsertPos; + + if (CallbacksSet.FindNodeOrInsertPos(ID, InsertPos)) { + delete visitor; + return; + } + + CallbacksSet.InsertNode(visitor, InsertPos); + Callbacks = F.add(visitor, Callbacks); +} + +BugReport::~BugReport() { + for (visitor_iterator I = visitor_begin(), E = visitor_end(); I != E; ++I) { + delete *I; + } +} + +void BugReport::Profile(llvm::FoldingSetNodeID& hash) const { + hash.AddPointer(&BT); + hash.AddString(Description); + if (Location.isValid()) { + Location.Profile(hash); + } else { + assert(ErrorNode); + hash.AddPointer(GetCurrentOrPreviousStmt(ErrorNode)); + } + + for (SmallVectorImpl<SourceRange>::const_iterator I = + Ranges.begin(), E = Ranges.end(); I != E; ++I) { + const SourceRange range = *I; + if (!range.isValid()) + continue; + hash.AddInteger(range.getBegin().getRawEncoding()); + hash.AddInteger(range.getEnd().getRawEncoding()); + } +} + +const Stmt *BugReport::getStmt() const { + if (!ErrorNode) + return 0; + ProgramPoint ProgP = ErrorNode->getLocation(); const Stmt *S = NULL; - if (BlockEntrance* BE = dyn_cast<BlockEntrance>(&ProgP)) { + if (BlockEntrance *BE = dyn_cast<BlockEntrance>(&ProgP)) { CFGBlock &Exit = ProgP.getLocationContext()->getCFG()->getExit(); if (BE->getBlock() == &Exit) S = GetPreviousStmt(ErrorNode); @@ -1229,61 +1269,47 @@ const Stmt* BugReport::getStmt() const { return S; } -PathDiagnosticPiece* -BugReport::getEndPath(BugReporterContext& BRC, - const ExplodedNode* EndPathNode) { - - const Stmt* S = getStmt(); - - if (!S) - return NULL; - - BugReport::ranges_iterator Beg, End; - llvm::tie(Beg, End) = getRanges(); - PathDiagnosticLocation L(S, BRC.getSourceManager()); - - // Only add the statement itself as a range if we didn't specify any - // special ranges for this report. - PathDiagnosticPiece* P = new PathDiagnosticEventPiece(L, getDescription(), - Beg == End); +std::pair<BugReport::ranges_iterator, BugReport::ranges_iterator> +BugReport::getRanges() { + // If no custom ranges, add the range of the statement corresponding to + // the error node. + if (Ranges.empty()) { + if (const Expr *E = dyn_cast_or_null<Expr>(getStmt())) + addRange(E->getSourceRange()); + else + return std::make_pair(ranges_iterator(), ranges_iterator()); + } - for (; Beg != End; ++Beg) - P->addRange(*Beg); + // User-specified absence of range info. + if (Ranges.size() == 1 && !Ranges.begin()->isValid()) + return std::make_pair(ranges_iterator(), ranges_iterator()); - return P; + return std::make_pair(Ranges.begin(), Ranges.end()); } -std::pair<BugReport::ranges_iterator, BugReport::ranges_iterator> -BugReport::getRanges() const { - if (const Expr* E = dyn_cast_or_null<Expr>(getStmt())) { - R = E->getSourceRange(); - assert(R.isValid()); - return std::make_pair(&R, &R+1); - } - else - return std::make_pair(ranges_iterator(), ranges_iterator()); -} +PathDiagnosticLocation BugReport::getLocation(const SourceManager &SM) const { + if (ErrorNode) { + assert(!Location.isValid() && + "Either Location or ErrorNode should be specified but not both."); + + if (const Stmt *S = GetCurrentOrPreviousStmt(ErrorNode)) { + const LocationContext *LC = ErrorNode->getLocationContext(); -SourceLocation BugReport::getLocation() const { - if (ErrorNode) - if (const Stmt* S = GetCurrentOrPreviousStmt(ErrorNode)) { // For member expressions, return the location of the '.' or '->'. if (const MemberExpr *ME = dyn_cast<MemberExpr>(S)) - return ME->getMemberLoc(); + return PathDiagnosticLocation::createMemberLoc(ME, SM); // For binary operators, return the location of the operator. if (const BinaryOperator *B = dyn_cast<BinaryOperator>(S)) - return B->getOperatorLoc(); + return PathDiagnosticLocation::createOperatorLoc(B, SM); - return S->getLocStart(); + return PathDiagnosticLocation::createBegin(S, SM, LC); } + } else { + assert(Location.isValid()); + return Location; + } - return FullSourceLoc(); -} - -PathDiagnosticPiece* BugReport::VisitNode(const ExplodedNode* N, - const ExplodedNode* PrevN, - BugReporterContext &BRC) { - return NULL; + return PathDiagnosticLocation(); } //===----------------------------------------------------------------------===// @@ -1299,10 +1325,19 @@ BugReporterData::~BugReporterData() {} ExplodedGraph &GRBugReporter::getGraph() { return Eng.getGraph(); } -GRStateManager& +ProgramStateManager& GRBugReporter::getStateManager() { return Eng.getStateManager(); } -BugReporter::~BugReporter() { FlushReports(); } +BugReporter::~BugReporter() { + FlushReports(); + + // Free the bug reports we are tracking. + typedef std::vector<BugReportEquivClass *> ContTy; + for (ContTy::iterator I = EQClassesVector.begin(), E = EQClassesVector.end(); + I != E; ++I) { + delete *I; + } +} void BugReporter::FlushReports() { if (BugTypes.isEmpty()) @@ -1312,10 +1347,10 @@ void BugReporter::FlushReports() { // 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; + 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 + for (SmallVector<const BugType*, 16>::iterator I = bugTypes.begin(), E = bugTypes.end(); I != E; ++I) const_cast<BugType*>(*I)->FlushReports(*this); @@ -1344,7 +1379,7 @@ void BugReporter::FlushReports() { static std::pair<std::pair<ExplodedGraph*, NodeBackMap*>, std::pair<ExplodedNode*, unsigned> > MakeReportGraph(const ExplodedGraph* G, - llvm::SmallVectorImpl<const ExplodedNode*> &nodes) { + SmallVectorImpl<const ExplodedNode*> &nodes) { // Create the trimmed graph. It will contain the shortest paths from the // error nodes to the root. In the new graph we should only have one @@ -1390,10 +1425,10 @@ MakeReportGraph(const ExplodedGraph* G, llvm::DenseMap<const void*,unsigned> Visited; unsigned cnt = 0; - const ExplodedNode* Root = 0; + const ExplodedNode *Root = 0; while (!WS.empty()) { - const ExplodedNode* Node = WS.front(); + const ExplodedNode *Node = WS.front(); WS.pop(); if (Visited.find(Node) != Visited.end()) @@ -1426,7 +1461,7 @@ MakeReportGraph(const ExplodedGraph* G, // Create the equivalent node in the new graph with the same state // and location. - ExplodedNode* NewN = GNew->getNode(N->getLocation(), N->getState()); + ExplodedNode *NewN = GNew->getNode(N->getLocation(), N->getState()); // Store the mapping to the original node. llvm::DenseMap<const void*, const void*>::iterator IMitr=InverseMap.find(N); @@ -1495,7 +1530,7 @@ static void CompactPathDiagnostic(PathDiagnostic &PD, const SourceManager& SM) { // Determine the instantiation location, which is the location we group // related PathDiagnosticPieces. SourceLocation InstantiationLoc = Loc.isMacroID() ? - SM.getInstantiationLoc(Loc) : + SM.getExpansionLoc(Loc) : SourceLocation(); if (Loc.isFileID()) { @@ -1517,7 +1552,7 @@ static void CompactPathDiagnostic(PathDiagnostic &PD, const SourceManager& SM) { PathDiagnosticMacroPiece *MacroGroup = 0; SourceLocation ParentInstantiationLoc = InstantiationLoc.isMacroID() ? - SM.getInstantiationLoc(Loc) : + SM.getExpansionLoc(Loc) : SourceLocation(); // Walk the entire macro stack. @@ -1537,7 +1572,9 @@ static void CompactPathDiagnostic(PathDiagnostic &PD, const SourceManager& SM) { if (!MacroGroup || ParentInstantiationLoc == MacroStack.back().second) { // Create a new macro group and add it to the stack. - PathDiagnosticMacroPiece *NewGroup = new PathDiagnosticMacroPiece(Loc); + PathDiagnosticMacroPiece *NewGroup = + new PathDiagnosticMacroPiece( + PathDiagnosticLocation::createSingleLocation(I->getLocation())); if (MacroGroup) MacroGroup->push_back(NewGroup); @@ -1569,11 +1606,11 @@ static void CompactPathDiagnostic(PathDiagnostic &PD, const SourceManager& SM) { } void GRBugReporter::GeneratePathDiagnostic(PathDiagnostic& PD, - llvm::SmallVectorImpl<BugReport *> &bugReports) { + SmallVectorImpl<BugReport *> &bugReports) { assert(!bugReports.empty()); - llvm::SmallVector<const ExplodedNode *, 10> errorNodes; - for (llvm::SmallVectorImpl<BugReport*>::iterator I = bugReports.begin(), + SmallVector<const ExplodedNode *, 10> errorNodes; + for (SmallVectorImpl<BugReport*>::iterator I = bugReports.begin(), E = bugReports.end(); I != E; ++I) { errorNodes.push_back((*I)->getErrorNode()); } @@ -1594,22 +1631,36 @@ void GRBugReporter::GeneratePathDiagnostic(PathDiagnostic& PD, const ExplodedNode *N = GPair.second.first; // Start building the path diagnostic... - PathDiagnosticBuilder PDB(*this, R, BackMap.get(), getPathDiagnosticClient()); - - if (PathDiagnosticPiece* Piece = R->getEndPath(PDB, N)) - PD.push_back(Piece); + PathDiagnosticBuilder PDB(*this, R, BackMap.get(), + getPathDiagnosticConsumer()); + + // Register additional node visitors. + R->addVisitor(new NilReceiverBRVisitor()); + R->addVisitor(new ConditionBRVisitor()); + + // Generate the very last diagnostic piece - the piece is visible before + // the trace is expanded. + PathDiagnosticPiece *LastPiece = 0; + for (BugReport::visitor_iterator I = R->visitor_begin(), + E = R->visitor_end(); I!=E; ++I) { + if (PathDiagnosticPiece *Piece = (*I)->getEndPath(PDB, N, *R)) { + assert (!LastPiece && + "There can only be one final piece in a diagnostic."); + LastPiece = Piece; + } + } + if (!LastPiece) + LastPiece = BugReporterVisitor::getDefaultEndPath(PDB, N, *R); + if (LastPiece) + PD.push_back(LastPiece); else return; - // Register node visitors. - R->registerInitialVisitors(PDB, N); - bugreporter::registerNilReceiverVisitor(PDB); - switch (PDB.getGenerationScheme()) { - case PathDiagnosticClient::Extensive: + case PathDiagnosticConsumer::Extensive: GenerateExtensivePathDiagnostic(PD, PDB, N); break; - case PathDiagnosticClient::Minimal: + case PathDiagnosticConsumer::Minimal: GenerateMinimalPathDiagnostic(PD, PDB, N); break; } @@ -1633,6 +1684,7 @@ void BugReporter::EmitReport(BugReport* R) { if (!EQ) { EQ = new BugReportEquivClass(R); EQClasses.InsertNode(EQ, InsertPos); + EQClassesVector.push_back(EQ); } else EQ->AddReport(R); @@ -1655,7 +1707,7 @@ struct FRIEC_WLItem { static BugReport * FindReportInEquivalenceClass(BugReportEquivClass& EQ, - llvm::SmallVectorImpl<BugReport*> &bugReports) { + SmallVectorImpl<BugReport*> &bugReports) { BugReportEquivClass::iterator I = EQ.begin(), E = EQ.end(); assert(I != E); @@ -1667,7 +1719,7 @@ FindReportInEquivalenceClass(BugReportEquivClass& EQ, // to 'Nodes'. Any of the reports will serve as a "representative" report. if (!BT.isSuppressOnSink()) { for (BugReportEquivClass::iterator I=EQ.begin(), E=EQ.end(); I!=E; ++I) { - const ExplodedNode* N = I->getErrorNode(); + const ExplodedNode *N = I->getErrorNode(); if (N) { R = *I; bugReports.push_back(R); @@ -1691,9 +1743,8 @@ FindReportInEquivalenceClass(BugReportEquivClass& EQ, if (!errorNode) continue; if (errorNode->isSink()) { - assert(false && + llvm_unreachable( "BugType::isSuppressSink() should not be 'true' for sink end nodes"); - return 0; } // No successors? By definition this nodes isn't post-dominated by a sink. if (errorNode->succ_empty()) { @@ -1706,7 +1757,7 @@ FindReportInEquivalenceClass(BugReportEquivClass& EQ, // At this point we know that 'N' is not a sink and it has at least one // successor. Use a DFS worklist to find a non-sink end-of-path node. typedef FRIEC_WLItem WLItem; - typedef llvm::SmallVector<WLItem, 10> DFSWorkList; + typedef SmallVector<WLItem, 10> DFSWorkList; llvm::DenseMap<const ExplodedNode *, unsigned> Visited; DFSWorkList WL; @@ -1763,11 +1814,8 @@ class DiagCacheItem : public llvm::FoldingSetNode { llvm::FoldingSetNodeID ID; public: DiagCacheItem(BugReport *R, PathDiagnostic *PD) { - ID.AddString(R->getBugType().getName()); - ID.AddString(R->getBugType().getCategory()); - ID.AddString(R->getDescription()); - ID.AddInteger(R->getLocation().getRawEncoding()); - PD->Profile(ID); + R->Profile(ID); + PD->Profile(ID); } void Profile(llvm::FoldingSetNodeID &id) { @@ -1798,12 +1846,12 @@ static bool IsCachedDiagnostic(BugReport *R, PathDiagnostic *PD) { } void BugReporter::FlushReport(BugReportEquivClass& EQ) { - llvm::SmallVector<BugReport*, 10> bugReports; + SmallVector<BugReport*, 10> bugReports; BugReport *exampleReport = FindReportInEquivalenceClass(EQ, bugReports); if (!exampleReport) return; - PathDiagnosticClient* PD = getPathDiagnosticClient(); + PathDiagnosticConsumer* PD = getPathDiagnosticConsumer(); // FIXME: Make sure we use the 'R' for the path that was actually used. // Probably doesn't make a difference in practice. @@ -1823,47 +1871,50 @@ void BugReporter::FlushReport(BugReportEquivClass& EQ) { return; // Get the meta data. - std::pair<const char**, const char**> Meta = - exampleReport->getExtraDescriptiveText(); - for (const char** s = Meta.first; s != Meta.second; ++s) - D->addMeta(*s); + const BugReport::ExtraTextList &Meta = + exampleReport->getExtraText(); + for (BugReport::ExtraTextList::const_iterator i = Meta.begin(), + e = Meta.end(); i != e; ++i) { + D->addMeta(*i); + } // Emit a summary diagnostic to the regular Diagnostics engine. BugReport::ranges_iterator Beg, End; llvm::tie(Beg, End) = exampleReport->getRanges(); - Diagnostic &Diag = getDiagnostic(); - FullSourceLoc L(exampleReport->getLocation(), getSourceManager()); + DiagnosticsEngine &Diag = getDiagnostic(); // Search the description for '%', as that will be interpretted as a // format character by FormatDiagnostics. - llvm::StringRef desc = exampleReport->getShortDescription(); + StringRef desc = exampleReport->getShortDescription(); unsigned ErrorDiag; { llvm::SmallString<512> TmpStr; llvm::raw_svector_ostream Out(TmpStr); - for (llvm::StringRef::iterator I=desc.begin(), E=desc.end(); I!=E; ++I) + for (StringRef::iterator I=desc.begin(), E=desc.end(); I!=E; ++I) if (*I == '%') Out << "%%"; else Out << *I; Out.flush(); - ErrorDiag = Diag.getCustomDiagID(Diagnostic::Warning, TmpStr); + ErrorDiag = Diag.getCustomDiagID(DiagnosticsEngine::Warning, TmpStr); } { - DiagnosticBuilder diagBuilder = Diag.Report(L, ErrorDiag); + DiagnosticBuilder diagBuilder = Diag.Report( + exampleReport->getLocation(getSourceManager()).asLocation(), ErrorDiag); for (BugReport::ranges_iterator I = Beg; I != End; ++I) diagBuilder << *I; } - // Emit a full diagnostic for the path if we have a PathDiagnosticClient. + // Emit a full diagnostic for the path if we have a PathDiagnosticConsumer. if (!PD) return; if (D->empty()) { - PathDiagnosticPiece* piece = - new PathDiagnosticEventPiece(L, exampleReport->getDescription()); + PathDiagnosticPiece *piece = new PathDiagnosticEventPiece( + exampleReport->getLocation(getSourceManager()), + exampleReport->getDescription()); for ( ; Beg != End; ++Beg) piece->addRange(*Beg); D->push_back(piece); @@ -1872,27 +1923,26 @@ void BugReporter::FlushReport(BugReportEquivClass& EQ) { PD->HandlePathDiagnostic(D.take()); } -void BugReporter::EmitBasicReport(llvm::StringRef name, llvm::StringRef str, - SourceLocation Loc, +void BugReporter::EmitBasicReport(StringRef name, StringRef str, + PathDiagnosticLocation Loc, SourceRange* RBeg, unsigned NumRanges) { EmitBasicReport(name, "", str, Loc, RBeg, NumRanges); } -void BugReporter::EmitBasicReport(llvm::StringRef name, - llvm::StringRef category, - llvm::StringRef str, SourceLocation Loc, +void BugReporter::EmitBasicReport(StringRef name, + StringRef category, + StringRef str, PathDiagnosticLocation Loc, SourceRange* RBeg, unsigned NumRanges) { // 'BT' is owned by BugReporter. BugType *BT = getBugTypeForName(name, category); - FullSourceLoc L = getContext().getFullLoc(Loc); - RangedBugReport *R = new DiagBugReport(*BT, str, L); + BugReport *R = new BugReport(*BT, str, Loc); for ( ; NumRanges > 0 ; --NumRanges, ++RBeg) R->addRange(*RBeg); EmitReport(R); } -BugType *BugReporter::getBugTypeForName(llvm::StringRef name, - llvm::StringRef category) { +BugType *BugReporter::getBugTypeForName(StringRef name, + StringRef category) { llvm::SmallString<136> fullDesc; llvm::raw_svector_ostream(fullDesc) << name << ":" << category; llvm::StringMapEntry<BugType *> & diff --git a/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp b/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp index 8e31adead188..1abd8baef624 100644 --- a/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp +++ b/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp @@ -11,13 +11,15 @@ // enhance the diagnostics reported for a bug. // //===----------------------------------------------------------------------===// +#include "clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitor.h" #include "clang/AST/Expr.h" #include "clang/AST/ExprObjC.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" #include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/GRState.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" using namespace clang; using namespace ento; @@ -39,8 +41,6 @@ const Stmt *bugreporter::GetDerefExpr(const ExplodedNode *N) { return ME->getBase()->IgnoreParenCasts(); } else if (const ArraySubscriptExpr *AE = dyn_cast<ArraySubscriptExpr>(S)) { - // Retrieve the base for arrays since BasicStoreManager doesn't know how - // to reason about them. return AE->getBase(); } @@ -73,248 +73,255 @@ const Stmt *bugreporter::GetRetValExpr(const ExplodedNode *N) { // Definitions for bug reporter visitors. //===----------------------------------------------------------------------===// -namespace { -class FindLastStoreBRVisitor : public BugReporterVisitor { - const MemRegion *R; - SVal V; - bool satisfied; - const ExplodedNode *StoreSite; -public: - FindLastStoreBRVisitor(SVal v, const MemRegion *r) - : R(r), V(v), satisfied(false), StoreSite(0) {} - - virtual void Profile(llvm::FoldingSetNodeID &ID) const { - static int tag = 0; - ID.AddPointer(&tag); - ID.AddPointer(R); - ID.Add(V); +PathDiagnosticPiece* +BugReporterVisitor::getEndPath(BugReporterContext &BRC, + const ExplodedNode *EndPathNode, + BugReport &BR) { + return 0; +} + +PathDiagnosticPiece* +BugReporterVisitor::getDefaultEndPath(BugReporterContext &BRC, + const ExplodedNode *EndPathNode, + BugReport &BR) { + const ProgramPoint &PP = EndPathNode->getLocation(); + PathDiagnosticLocation L; + + if (const BlockEntrance *BE = dyn_cast<BlockEntrance>(&PP)) { + const CFGBlock *block = BE->getBlock(); + if (block->getBlockID() == 0) { + L = PathDiagnosticLocation::createDeclEnd(PP.getLocationContext(), + BRC.getSourceManager()); + } } - PathDiagnosticPiece* VisitNode(const ExplodedNode *N, - const ExplodedNode *PrevN, - BugReporterContext& BRC) { + if (!L.isValid()) { + const Stmt *S = BR.getStmt(); - if (satisfied) + if (!S) return NULL; - if (!StoreSite) { - const ExplodedNode *Node = N, *Last = NULL; + L = PathDiagnosticLocation(S, BRC.getSourceManager(), + PP.getLocationContext()); + } - for ( ; Node ; Last = Node, Node = Node->getFirstPred()) { + BugReport::ranges_iterator Beg, End; + llvm::tie(Beg, End) = BR.getRanges(); - if (const VarRegion *VR = dyn_cast<VarRegion>(R)) { - if (const PostStmt *P = Node->getLocationAs<PostStmt>()) - if (const DeclStmt *DS = P->getStmtAs<DeclStmt>()) - if (DS->getSingleDecl() == VR->getDecl()) { - Last = Node; - break; - } - } + // Only add the statement itself as a range if we didn't specify any + // special ranges for this report. + PathDiagnosticPiece *P = new PathDiagnosticEventPiece(L, + BR.getDescription(), + Beg == End); + for (; Beg != End; ++Beg) + P->addRange(*Beg); - if (Node->getState()->getSVal(R) != V) - break; - } + return P; +} - if (!Node || !Last) { - satisfied = true; - return NULL; + +void FindLastStoreBRVisitor ::Profile(llvm::FoldingSetNodeID &ID) const { + static int tag = 0; + ID.AddPointer(&tag); + ID.AddPointer(R); + ID.Add(V); +} + +PathDiagnosticPiece *FindLastStoreBRVisitor::VisitNode(const ExplodedNode *N, + const ExplodedNode *PrevN, + BugReporterContext &BRC, + BugReport &BR) { + + if (satisfied) + return NULL; + + if (!StoreSite) { + const ExplodedNode *Node = N, *Last = NULL; + + for ( ; Node ; Last = Node, Node = Node->getFirstPred()) { + + if (const VarRegion *VR = dyn_cast<VarRegion>(R)) { + if (const PostStmt *P = Node->getLocationAs<PostStmt>()) + if (const DeclStmt *DS = P->getStmtAs<DeclStmt>()) + if (DS->getSingleDecl() == VR->getDecl()) { + Last = Node; + break; + } } - StoreSite = Last; + if (Node->getState()->getSVal(R) != V) + break; } - if (StoreSite != N) + if (!Node || !Last) { + satisfied = true; return NULL; + } - satisfied = true; - llvm::SmallString<256> sbuf; - llvm::raw_svector_ostream os(sbuf); + StoreSite = Last; + } - if (const PostStmt *PS = N->getLocationAs<PostStmt>()) { - if (const DeclStmt *DS = PS->getStmtAs<DeclStmt>()) { + if (StoreSite != N) + return NULL; - if (const VarRegion *VR = dyn_cast<VarRegion>(R)) { - os << "Variable '" << VR->getDecl() << "' "; - } - else - return NULL; - - if (isa<loc::ConcreteInt>(V)) { - bool b = false; - if (R->isBoundable()) { - if (const TypedRegion *TR = dyn_cast<TypedRegion>(R)) { - if (TR->getValueType()->isObjCObjectPointerType()) { - os << "initialized to nil"; - b = true; - } - } - } + satisfied = true; + llvm::SmallString<256> sbuf; + llvm::raw_svector_ostream os(sbuf); - if (!b) - os << "initialized to a null pointer value"; - } - else if (isa<nonloc::ConcreteInt>(V)) { - os << "initialized to " << cast<nonloc::ConcreteInt>(V).getValue(); - } - else if (V.isUndef()) { - if (isa<VarRegion>(R)) { - const VarDecl *VD = cast<VarDecl>(DS->getSingleDecl()); - if (VD->getInit()) - os << "initialized to a garbage value"; - else - os << "declared without an initial value"; - } - } + if (const PostStmt *PS = N->getLocationAs<PostStmt>()) { + if (const DeclStmt *DS = PS->getStmtAs<DeclStmt>()) { + + if (const VarRegion *VR = dyn_cast<VarRegion>(R)) { + os << "Variable '" << *VR->getDecl() << "' "; } - } + else + return NULL; - if (os.str().empty()) { if (isa<loc::ConcreteInt>(V)) { bool b = false; if (R->isBoundable()) { - if (const TypedRegion *TR = dyn_cast<TypedRegion>(R)) { + if (const TypedValueRegion *TR = dyn_cast<TypedValueRegion>(R)) { if (TR->getValueType()->isObjCObjectPointerType()) { - os << "nil object reference stored to "; + os << "initialized to nil"; b = true; } } } if (!b) - os << "Null pointer value stored to "; - } - else if (V.isUndef()) { - os << "Uninitialized value stored to "; + os << "initialized to a null pointer value"; } else if (isa<nonloc::ConcreteInt>(V)) { - os << "The value " << cast<nonloc::ConcreteInt>(V).getValue() - << " is assigned to "; + os << "initialized to " << cast<nonloc::ConcreteInt>(V).getValue(); } - else - return NULL; - - if (const VarRegion *VR = dyn_cast<VarRegion>(R)) { - os << '\'' << VR->getDecl() << '\''; + else if (V.isUndef()) { + if (isa<VarRegion>(R)) { + const VarDecl *VD = cast<VarDecl>(DS->getSingleDecl()); + if (VD->getInit()) + os << "initialized to a garbage value"; + else + os << "declared without an initial value"; + } } - else - return NULL; } + } - // FIXME: Refactor this into BugReporterContext. - const Stmt *S = 0; - ProgramPoint P = N->getLocation(); + if (os.str().empty()) { + if (isa<loc::ConcreteInt>(V)) { + bool b = false; + if (R->isBoundable()) { + if (const TypedValueRegion *TR = dyn_cast<TypedValueRegion>(R)) { + if (TR->getValueType()->isObjCObjectPointerType()) { + os << "nil object reference stored to "; + b = true; + } + } + } - if (BlockEdge *BE = dyn_cast<BlockEdge>(&P)) { - const CFGBlock *BSrc = BE->getSrc(); - S = BSrc->getTerminatorCondition(); + if (!b) + os << "Null pointer value stored to "; } - else if (PostStmt *PS = dyn_cast<PostStmt>(&P)) { - S = PS->getStmt(); + else if (V.isUndef()) { + os << "Uninitialized value stored to "; } - - if (!S) + else if (isa<nonloc::ConcreteInt>(V)) { + os << "The value " << cast<nonloc::ConcreteInt>(V).getValue() + << " is assigned to "; + } + else return NULL; - // Construct a new PathDiagnosticPiece. - PathDiagnosticLocation L(S, BRC.getSourceManager()); - return new PathDiagnosticEventPiece(L, os.str()); + if (const VarRegion *VR = dyn_cast<VarRegion>(R)) { + os << '\'' << *VR->getDecl() << '\''; + } + else + return NULL; } -}; - -static void registerFindLastStore(BugReporterContext& BRC, const MemRegion *R, - SVal V) { - BRC.addVisitor(new FindLastStoreBRVisitor(V, R)); + // Construct a new PathDiagnosticPiece. + ProgramPoint P = N->getLocation(); + PathDiagnosticLocation L = + PathDiagnosticLocation::create(P, BRC.getSourceManager()); + if (!L.isValid()) + return NULL; + return new PathDiagnosticEventPiece(L, os.str()); } -class TrackConstraintBRVisitor : public BugReporterVisitor { - DefinedSVal Constraint; - const bool Assumption; - bool isSatisfied; -public: - TrackConstraintBRVisitor(DefinedSVal constraint, bool assumption) - : Constraint(constraint), Assumption(assumption), isSatisfied(false) {} - - void Profile(llvm::FoldingSetNodeID &ID) const { - static int tag = 0; - ID.AddPointer(&tag); - ID.AddBoolean(Assumption); - ID.Add(Constraint); - } - - PathDiagnosticPiece* VisitNode(const ExplodedNode *N, - const ExplodedNode *PrevN, - BugReporterContext& BRC) { - if (isSatisfied) - return NULL; - - // Check if in the previous state it was feasible for this constraint - // to *not* be true. - if (PrevN->getState()->assume(Constraint, !Assumption)) { - - isSatisfied = true; - - // As a sanity check, make sure that the negation of the constraint - // was infeasible in the current state. If it is feasible, we somehow - // missed the transition point. - if (N->getState()->assume(Constraint, !Assumption)) - return NULL; - - // We found the transition point for the constraint. We now need to - // pretty-print the constraint. (work-in-progress) - std::string sbuf; - llvm::raw_string_ostream os(sbuf); +void TrackConstraintBRVisitor::Profile(llvm::FoldingSetNodeID &ID) const { + static int tag = 0; + ID.AddPointer(&tag); + ID.AddBoolean(Assumption); + ID.Add(Constraint); +} - if (isa<Loc>(Constraint)) { - os << "Assuming pointer value is "; - os << (Assumption ? "non-null" : "null"); - } +PathDiagnosticPiece * +TrackConstraintBRVisitor::VisitNode(const ExplodedNode *N, + const ExplodedNode *PrevN, + BugReporterContext &BRC, + BugReport &BR) { + if (isSatisfied) + return NULL; - if (os.str().empty()) - return NULL; + // Check if in the previous state it was feasible for this constraint + // to *not* be true. + if (PrevN->getState()->assume(Constraint, !Assumption)) { - // FIXME: Refactor this into BugReporterContext. - const Stmt *S = 0; - ProgramPoint P = N->getLocation(); + isSatisfied = true; - if (BlockEdge *BE = dyn_cast<BlockEdge>(&P)) { - const CFGBlock *BSrc = BE->getSrc(); - S = BSrc->getTerminatorCondition(); - } - else if (PostStmt *PS = dyn_cast<PostStmt>(&P)) { - S = PS->getStmt(); - } + // As a sanity check, make sure that the negation of the constraint + // was infeasible in the current state. If it is feasible, we somehow + // missed the transition point. + if (N->getState()->assume(Constraint, !Assumption)) + return NULL; - if (!S) - return NULL; + // We found the transition point for the constraint. We now need to + // pretty-print the constraint. (work-in-progress) + std::string sbuf; + llvm::raw_string_ostream os(sbuf); - // Construct a new PathDiagnosticPiece. - PathDiagnosticLocation L(S, BRC.getSourceManager()); - return new PathDiagnosticEventPiece(L, os.str()); + if (isa<Loc>(Constraint)) { + os << "Assuming pointer value is "; + os << (Assumption ? "non-null" : "null"); } - return NULL; + if (os.str().empty()) + return NULL; + + // Construct a new PathDiagnosticPiece. + ProgramPoint P = N->getLocation(); + PathDiagnosticLocation L = + PathDiagnosticLocation::create(P, BRC.getSourceManager()); + if (!L.isValid()) + return NULL; + return new PathDiagnosticEventPiece(L, os.str()); } -}; -} // end anonymous namespace -static void registerTrackConstraint(BugReporterContext& BRC, - DefinedSVal Constraint, - bool Assumption) { - BRC.addVisitor(new TrackConstraintBRVisitor(Constraint, Assumption)); + return NULL; } -void bugreporter::registerTrackNullOrUndefValue(BugReporterContext& BRC, - const void *data, - const ExplodedNode* N) { - - const Stmt *S = static_cast<const Stmt*>(data); - - if (!S) - return; +BugReporterVisitor * +bugreporter::getTrackNullOrUndefValueVisitor(const ExplodedNode *N, + const Stmt *S) { + if (!S || !N) + return 0; + + ProgramStateManager &StateMgr = N->getState()->getStateManager(); + + // Walk through nodes until we get one that matches the statement + // exactly. + while (N) { + const ProgramPoint &pp = N->getLocation(); + if (const PostStmt *ps = dyn_cast<PostStmt>(&pp)) { + if (ps->getStmt() == S) + break; + } + N = N->getFirstPred(); + } - GRStateManager &StateMgr = BRC.getStateManager(); - const GRState *state = N->getState(); + if (!N) + return 0; + + const ProgramState *state = N->getState(); // Walk through lvalue-to-rvalue conversions. if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(S)) { @@ -327,7 +334,7 @@ void bugreporter::registerTrackNullOrUndefValue(BugReporterContext& BRC, if (isa<loc::ConcreteInt>(V) || isa<nonloc::ConcreteInt>(V) || V.isUndef()) { - ::registerFindLastStore(BRC, R, V); + return new FindLastStoreBRVisitor(V, R); } } } @@ -347,94 +354,73 @@ void bugreporter::registerTrackNullOrUndefValue(BugReporterContext& BRC, if (R) { assert(isa<SymbolicRegion>(R)); - registerTrackConstraint(BRC, loc::MemRegionVal(R), false); + return new TrackConstraintBRVisitor(loc::MemRegionVal(R), false); } } -} - -void bugreporter::registerFindLastStore(BugReporterContext& BRC, - const void *data, - const ExplodedNode* N) { - const MemRegion *R = static_cast<const MemRegion*>(data); + return 0; +} - if (!R) - return; +BugReporterVisitor * +FindLastStoreBRVisitor::createVisitorObject(const ExplodedNode *N, + const MemRegion *R) { + assert(R && "The memory region is null."); - const GRState *state = N->getState(); + const ProgramState *state = N->getState(); SVal V = state->getSVal(R); - if (V.isUnknown()) - return; + return 0; - BRC.addVisitor(new FindLastStoreBRVisitor(V, R)); + return new FindLastStoreBRVisitor(V, R); } -namespace { -class NilReceiverVisitor : public BugReporterVisitor { -public: - NilReceiverVisitor() {} - - void Profile(llvm::FoldingSetNodeID &ID) const { - static int x = 0; - ID.AddPointer(&x); - } - - PathDiagnosticPiece* VisitNode(const ExplodedNode *N, - const ExplodedNode *PrevN, - BugReporterContext& BRC) { - - const PostStmt *P = N->getLocationAs<PostStmt>(); - if (!P) - return 0; - const ObjCMessageExpr *ME = P->getStmtAs<ObjCMessageExpr>(); - if (!ME) - return 0; - const Expr *Receiver = ME->getInstanceReceiver(); - if (!Receiver) - return 0; - const GRState *state = N->getState(); - const SVal &V = state->getSVal(Receiver); - const DefinedOrUnknownSVal *DV = dyn_cast<DefinedOrUnknownSVal>(&V); - if (!DV) - return 0; - state = state->assume(*DV, true); - if (state) - return 0; - - // The receiver was nil, and hence the method was skipped. - // Register a BugReporterVisitor to issue a message telling us how - // the receiver was null. - bugreporter::registerTrackNullOrUndefValue(BRC, Receiver, N); - // Issue a message saying that the method was skipped. - PathDiagnosticLocation L(Receiver, BRC.getSourceManager()); - return new PathDiagnosticEventPiece(L, "No method actually called " - "because the receiver is nil"); - } -}; -} // end anonymous namespace - -void bugreporter::registerNilReceiverVisitor(BugReporterContext &BRC) { - BRC.addVisitor(new NilReceiverVisitor()); +PathDiagnosticPiece *NilReceiverBRVisitor::VisitNode(const ExplodedNode *N, + const ExplodedNode *PrevN, + BugReporterContext &BRC, + BugReport &BR) { + const PostStmt *P = N->getLocationAs<PostStmt>(); + if (!P) + return 0; + const ObjCMessageExpr *ME = P->getStmtAs<ObjCMessageExpr>(); + if (!ME) + return 0; + const Expr *Receiver = ME->getInstanceReceiver(); + if (!Receiver) + return 0; + const ProgramState *state = N->getState(); + const SVal &V = state->getSVal(Receiver); + const DefinedOrUnknownSVal *DV = dyn_cast<DefinedOrUnknownSVal>(&V); + if (!DV) + return 0; + state = state->assume(*DV, true); + if (state) + return 0; + + // The receiver was nil, and hence the method was skipped. + // Register a BugReporterVisitor to issue a message telling us how + // the receiver was null. + BR.addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, Receiver)); + // Issue a message saying that the method was skipped. + PathDiagnosticLocation L(Receiver, BRC.getSourceManager(), + N->getLocationContext()); + return new PathDiagnosticEventPiece(L, "No method actually called " + "because the receiver is nil"); } -// Registers every VarDecl inside a Stmt with a last store vistor. -void bugreporter::registerVarDeclsLastStore(BugReporterContext &BRC, - const void *stmt, - const ExplodedNode *N) { - const Stmt *S = static_cast<const Stmt *>(stmt); - +// Registers every VarDecl inside a Stmt with a last store visitor. +void FindLastStoreBRVisitor::registerStatementVarDecls(BugReport &BR, + const Stmt *S) { + const ExplodedNode *N = BR.getErrorNode(); std::deque<const Stmt *> WorkList; - WorkList.push_back(S); while (!WorkList.empty()) { const Stmt *Head = WorkList.front(); WorkList.pop_front(); - GRStateManager &StateMgr = BRC.getStateManager(); - const GRState *state = N->getState(); + const ProgramState *state = N->getState(); + ProgramStateManager &StateMgr = state->getStateManager(); if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(Head)) { if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) { @@ -445,7 +431,8 @@ void bugreporter::registerVarDeclsLastStore(BugReporterContext &BRC, SVal V = state->getSVal(S); if (isa<loc::ConcreteInt>(V) || isa<nonloc::ConcreteInt>(V)) { - ::registerFindLastStore(BRC, R, V); + // Register a new visitor with the BugReport. + BR.addVisitor(new FindLastStoreBRVisitor(V, R)); } } } @@ -455,3 +442,248 @@ void bugreporter::registerVarDeclsLastStore(BugReporterContext &BRC, WorkList.push_back(*I); } } + +//===----------------------------------------------------------------------===// +// Visitor that tries to report interesting diagnostics from conditions. +//===----------------------------------------------------------------------===// +PathDiagnosticPiece *ConditionBRVisitor::VisitNode(const ExplodedNode *N, + const ExplodedNode *Prev, + BugReporterContext &BRC, + BugReport &BR) { + + const ProgramPoint &progPoint = N->getLocation(); + + const ProgramState *CurrentState = N->getState(); + const ProgramState *PrevState = Prev->getState(); + + // Compare the GDMs of the state, because that is where constraints + // are managed. Note that ensure that we only look at nodes that + // were generated by the analyzer engine proper, not checkers. + if (CurrentState->getGDM().getRoot() == + PrevState->getGDM().getRoot()) + return 0; + + // If an assumption was made on a branch, it should be caught + // here by looking at the state transition. + if (const BlockEdge *BE = dyn_cast<BlockEdge>(&progPoint)) { + const CFGBlock *srcBlk = BE->getSrc(); + if (const Stmt *term = srcBlk->getTerminator()) + return VisitTerminator(term, N, srcBlk, BE->getDst(), BRC); + return 0; + } + + if (const PostStmt *PS = dyn_cast<PostStmt>(&progPoint)) { + // FIXME: Assuming that BugReporter is a GRBugReporter is a layering + // violation. + const std::pair<const ProgramPointTag *, const ProgramPointTag *> &tags = + cast<GRBugReporter>(BRC.getBugReporter()). + getEngine().getEagerlyAssumeTags(); + + const ProgramPointTag *tag = PS->getTag(); + if (tag == tags.first) + return VisitTrueTest(cast<Expr>(PS->getStmt()), true, + BRC, N->getLocationContext()); + if (tag == tags.second) + return VisitTrueTest(cast<Expr>(PS->getStmt()), false, + BRC, N->getLocationContext()); + + return 0; + } + + return 0; +} + +PathDiagnosticPiece * +ConditionBRVisitor::VisitTerminator(const Stmt *Term, + const ExplodedNode *N, + const CFGBlock *srcBlk, + const CFGBlock *dstBlk, + BugReporterContext &BRC) { + const Expr *Cond = 0; + + switch (Term->getStmtClass()) { + default: + return 0; + case Stmt::IfStmtClass: + Cond = cast<IfStmt>(Term)->getCond(); + break; + case Stmt::ConditionalOperatorClass: + Cond = cast<ConditionalOperator>(Term)->getCond(); + break; + } + + assert(Cond); + assert(srcBlk->succ_size() == 2); + const bool tookTrue = *(srcBlk->succ_begin()) == dstBlk; + return VisitTrueTest(Cond->IgnoreParenNoopCasts(BRC.getASTContext()), + tookTrue, BRC, N->getLocationContext()); +} + +PathDiagnosticPiece * +ConditionBRVisitor::VisitTrueTest(const Expr *Cond, + bool tookTrue, + BugReporterContext &BRC, + const LocationContext *LC) { + + const Expr *Ex = Cond; + + while (true) { + Ex = Ex->IgnoreParens(); + switch (Ex->getStmtClass()) { + default: + return 0; + case Stmt::BinaryOperatorClass: + return VisitTrueTest(Cond, cast<BinaryOperator>(Ex), tookTrue, BRC, LC); + case Stmt::DeclRefExprClass: + return VisitTrueTest(Cond, cast<DeclRefExpr>(Ex), tookTrue, BRC, LC); + case Stmt::UnaryOperatorClass: { + const UnaryOperator *UO = cast<UnaryOperator>(Ex); + if (UO->getOpcode() == UO_LNot) { + tookTrue = !tookTrue; + Ex = UO->getSubExpr()->IgnoreParenNoopCasts(BRC.getASTContext()); + continue; + } + return 0; + } + } + } +} + +bool ConditionBRVisitor::patternMatch(const Expr *Ex, llvm::raw_ostream &Out, + BugReporterContext &BRC) { + const Expr *OriginalExpr = Ex; + Ex = Ex->IgnoreParenCasts(); + + if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(Ex)) { + const bool quotes = isa<VarDecl>(DR->getDecl()); + if (quotes) + Out << '\''; + Out << DR->getDecl()->getDeclName().getAsString(); + if (quotes) + Out << '\''; + return quotes; + } + + if (const IntegerLiteral *IL = dyn_cast<IntegerLiteral>(Ex)) { + QualType OriginalTy = OriginalExpr->getType(); + if (OriginalTy->isPointerType()) { + if (IL->getValue() == 0) { + Out << "null"; + return false; + } + } + else if (OriginalTy->isObjCObjectPointerType()) { + if (IL->getValue() == 0) { + Out << "nil"; + return false; + } + } + + Out << IL->getValue(); + return false; + } + + return false; +} + +PathDiagnosticPiece * +ConditionBRVisitor::VisitTrueTest(const Expr *Cond, + const BinaryOperator *BExpr, + const bool tookTrue, + BugReporterContext &BRC, + const LocationContext *LC) { + + bool shouldInvert = false; + + llvm::SmallString<128> LhsString, RhsString; + { + llvm::raw_svector_ostream OutLHS(LhsString), OutRHS(RhsString); + const bool isVarLHS = patternMatch(BExpr->getLHS(), OutLHS, BRC); + const bool isVarRHS = patternMatch(BExpr->getRHS(), OutRHS, BRC); + + shouldInvert = !isVarLHS && isVarRHS; + } + + if (LhsString.empty() || RhsString.empty()) + return 0; + + // Should we invert the strings if the LHS is not a variable name? + + llvm::SmallString<256> buf; + llvm::raw_svector_ostream Out(buf); + Out << "Assuming " << (shouldInvert ? RhsString : LhsString) << " is "; + + // Do we need to invert the opcode? + BinaryOperator::Opcode Op = BExpr->getOpcode(); + + if (shouldInvert) + switch (Op) { + default: break; + case BO_LT: Op = BO_GT; break; + case BO_GT: Op = BO_LT; break; + case BO_LE: Op = BO_GE; break; + case BO_GE: Op = BO_LE; break; + } + + if (!tookTrue) + switch (Op) { + case BO_EQ: Op = BO_NE; break; + case BO_NE: Op = BO_EQ; break; + case BO_LT: Op = BO_GE; break; + case BO_GT: Op = BO_LE; break; + case BO_LE: Op = BO_GT; break; + case BO_GE: Op = BO_LT; break; + default: + return 0; + } + + switch (BExpr->getOpcode()) { + case BO_EQ: + Out << "equal to "; + break; + case BO_NE: + Out << "not equal to "; + break; + default: + Out << BinaryOperator::getOpcodeStr(Op) << ' '; + break; + } + + Out << (shouldInvert ? LhsString : RhsString); + + PathDiagnosticLocation Loc(Cond, BRC.getSourceManager(), LC); + return new PathDiagnosticEventPiece(Loc, Out.str()); +} + +PathDiagnosticPiece * +ConditionBRVisitor::VisitTrueTest(const Expr *Cond, + const DeclRefExpr *DR, + const bool tookTrue, + BugReporterContext &BRC, + const LocationContext *LC) { + + const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl()); + if (!VD) + return 0; + + llvm::SmallString<256> Buf; + llvm::raw_svector_ostream Out(Buf); + + Out << "Assuming '"; + VD->getDeclName().printName(Out); + Out << "' is "; + + QualType VDTy = VD->getType(); + + if (VDTy->isPointerType()) + Out << (tookTrue ? "non-null" : "null"); + else if (VDTy->isObjCObjectPointerType()) + Out << (tookTrue ? "non-nil" : "nil"); + else if (VDTy->isScalarType()) + Out << (tookTrue ? "not equal to 0" : "0"); + else + return 0; + + PathDiagnosticLocation Loc(Cond, BRC.getSourceManager(), LC); + return new PathDiagnosticEventPiece(Loc, Out.str()); +} diff --git a/lib/StaticAnalyzer/Core/CMakeLists.txt b/lib/StaticAnalyzer/Core/CMakeLists.txt index 089a5cc39037..391a781ab09d 100644 --- a/lib/StaticAnalyzer/Core/CMakeLists.txt +++ b/lib/StaticAnalyzer/Core/CMakeLists.txt @@ -6,34 +6,36 @@ add_clang_library(clangStaticAnalyzerCore AggExprVisitor.cpp AnalysisManager.cpp BasicConstraintManager.cpp - BasicStore.cpp BasicValueFactory.cpp BlockCounter.cpp BugReporter.cpp BugReporterVisitors.cpp - CFRefCount.cpp - CXXExprEngine.cpp + Checker.cpp CheckerContext.cpp CheckerHelpers.cpp CheckerManager.cpp + CheckerRegistry.cpp CoreEngine.cpp Environment.cpp ExplodedGraph.cpp ExprEngine.cpp - FlatStore.cpp - GRState.cpp + ExprEngineC.cpp + ExprEngineCXX.cpp + ExprEngineCallAndReturn.cpp + ExprEngineObjC.cpp HTMLDiagnostics.cpp MemRegion.cpp ObjCMessage.cpp PathDiagnostic.cpp PlistDiagnostics.cpp + ProgramState.cpp RangeConstraintManager.cpp RegionStore.cpp + SValBuilder.cpp + SVals.cpp SimpleConstraintManager.cpp SimpleSValBuilder.cpp Store.cpp - SValBuilder.cpp - SVals.cpp SymbolManager.cpp TextPathDiagnostics.cpp ) diff --git a/lib/StaticAnalyzer/Core/Checker.cpp b/lib/StaticAnalyzer/Core/Checker.cpp new file mode 100644 index 000000000000..a3bf2c236f6e --- /dev/null +++ b/lib/StaticAnalyzer/Core/Checker.cpp @@ -0,0 +1,22 @@ +//== Checker.cpp - Registration mechanism for checkers -----------*- C++ -*--=// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines Checker, used to create and register checkers. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Core/Checker.h" + +using namespace clang; +using namespace ento; + +StringRef CheckerBase::getTagDescription() const { + // FIXME: We want to return the package + name of the checker here. + return "A Checker"; +} diff --git a/lib/StaticAnalyzer/Core/CheckerContext.cpp b/lib/StaticAnalyzer/Core/CheckerContext.cpp index f6fb8f256c01..5356edc752fa 100644 --- a/lib/StaticAnalyzer/Core/CheckerContext.cpp +++ b/lib/StaticAnalyzer/Core/CheckerContext.cpp @@ -23,8 +23,8 @@ CheckerContext::~CheckerContext() { // if we are building sinks or we generated a node and decided to not // add it as a transition. if (Dst.size() == size && !B.BuildSinks && !B.hasGeneratedNode) { - if (ST && ST != B.GetState(Pred)) { - static int autoTransitionTag = 0; + if (ST && ST != Pred->getState()) { + static SimpleProgramPointTag autoTransitionTag("CheckerContext : auto"); addTransition(ST, &autoTransitionTag); } else diff --git a/lib/StaticAnalyzer/Core/CheckerManager.cpp b/lib/StaticAnalyzer/Core/CheckerManager.cpp index ba7c384e5c36..acacfb0e18c6 100644 --- a/lib/StaticAnalyzer/Core/CheckerManager.cpp +++ b/lib/StaticAnalyzer/Core/CheckerManager.cpp @@ -12,8 +12,9 @@ //===----------------------------------------------------------------------===// #include "clang/StaticAnalyzer/Core/CheckerManager.h" -#include "clang/StaticAnalyzer/Core/CheckerProvider.h" +#include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h" #include "clang/Analysis/ProgramPoint.h" #include "clang/AST/DeclBase.h" @@ -33,7 +34,8 @@ bool CheckerManager::hasPathSensitiveCheckers() const { !DeadSymbolsCheckers.empty() || !RegionChangesCheckers.empty() || !EvalAssumeCheckers.empty() || - !EvalCallCheckers.empty(); + !EvalCallCheckers.empty() || + !InlineCallCheckers.empty(); } void CheckerManager::finishedCheckerRegistration() { @@ -122,7 +124,7 @@ static void expandGraphWithCheckers(CHECK_CTX checkCtx, namespace { struct CheckStmtContext { - typedef llvm::SmallVectorImpl<CheckerManager::CheckStmtFunc> CheckersTy; + typedef SmallVectorImpl<CheckerManager::CheckStmtFunc> CheckersTy; bool IsPreVisit; const CheckersTy &Checkers; const Stmt *S; @@ -138,9 +140,12 @@ namespace { 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); + ProgramPoint::Kind K = IsPreVisit ? ProgramPoint::PreStmtKind : + ProgramPoint::PostStmtKind; + const ProgramPoint &L = ProgramPoint::getProgramPoint(S, K, + Pred->getLocationContext(), checkFn.Checker); + CheckerContext C(Dst, Eng.getBuilder(), Eng, Pred, L, 0); + checkFn(S, C); } }; @@ -174,10 +179,12 @@ namespace { 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()); + ProgramPoint::Kind K = IsPreVisit ? ProgramPoint::PreStmtKind : + ProgramPoint::PostStmtKind; + const ProgramPoint &L = ProgramPoint::getProgramPoint(Msg.getOriginExpr(), + K, Pred->getLocationContext(), checkFn.Checker); + CheckerContext C(Dst, Eng.getBuilder(), Eng, Pred, L, 0); + checkFn(Msg, C); } }; @@ -214,10 +221,13 @@ namespace { 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); + ProgramPoint::Kind K = IsLoad ? ProgramPoint::PreLoadKind : + ProgramPoint::PreStoreKind; + const ProgramPoint &L = ProgramPoint::getProgramPoint(S, K, + Pred->getLocationContext(), checkFn.Checker); + CheckerContext C(Dst, Eng.getBuilder(), Eng, Pred, L, 0); + + checkFn(Loc, IsLoad, S, C); } }; } @@ -249,9 +259,12 @@ namespace { void runChecker(CheckerManager::CheckBindFunc checkFn, ExplodedNodeSet &Dst, ExplodedNode *Pred) { - CheckerContext C(Dst, Eng.getBuilder(), Eng, Pred, checkFn.Checker, - ProgramPoint::PreStmtKind, 0, S); - checkFn(Loc, Val, C); + ProgramPoint::Kind K = ProgramPoint::PreStmtKind; + const ProgramPoint &L = ProgramPoint::getProgramPoint(S, K, + Pred->getLocationContext(), checkFn.Checker); + CheckerContext C(Dst, Eng.getBuilder(), Eng, Pred, L, 0); + + checkFn(Loc, Val, S, C); } }; } @@ -293,7 +306,7 @@ void CheckerManager::runCheckersForBranchCondition(const Stmt *condition, } /// \brief Run checkers for live symbols. -void CheckerManager::runCheckersForLiveSymbols(const GRState *state, +void CheckerManager::runCheckersForLiveSymbols(const ProgramState *state, SymbolReaper &SymReaper) { for (unsigned i = 0, e = LiveSymbolsCheckers.size(); i != e; ++i) LiveSymbolsCheckers[i](state, SymReaper); @@ -316,8 +329,11 @@ namespace { void runChecker(CheckerManager::CheckDeadSymbolsFunc checkFn, ExplodedNodeSet &Dst, ExplodedNode *Pred) { - CheckerContext C(Dst, Eng.getBuilder(), Eng, Pred, checkFn.Checker, - ProgramPoint::PostPurgeDeadSymbolsKind, 0, S); + ProgramPoint::Kind K = ProgramPoint::PostPurgeDeadSymbolsKind; + const ProgramPoint &L = ProgramPoint::getProgramPoint(S, K, + Pred->getLocationContext(), checkFn.Checker); + CheckerContext C(Dst, Eng.getBuilder(), Eng, Pred, L, 0); + checkFn(SR, C); } }; @@ -334,7 +350,7 @@ void CheckerManager::runCheckersForDeadSymbols(ExplodedNodeSet &Dst, } /// \brief True if at least one checker wants to check region changes. -bool CheckerManager::wantsRegionChangeUpdate(const GRState *state) { +bool CheckerManager::wantsRegionChangeUpdate(const ProgramState *state) { for (unsigned i = 0, e = RegionChangesCheckers.size(); i != e; ++i) if (RegionChangesCheckers[i].WantUpdateFn(state)) return true; @@ -343,24 +359,25 @@ bool CheckerManager::wantsRegionChangeUpdate(const GRState *state) { } /// \brief Run checkers for region changes. -const GRState * -CheckerManager::runCheckersForRegionChanges(const GRState *state, +const ProgramState * +CheckerManager::runCheckersForRegionChanges(const ProgramState *state, const StoreManager::InvalidatedSymbols *invalidated, - const MemRegion * const *Begin, - const MemRegion * const *End) { + ArrayRef<const MemRegion *> ExplicitRegions, + ArrayRef<const MemRegion *> Regions) { 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, invalidated, Begin, End); + state = RegionChangesCheckers[i].CheckFn(state, invalidated, + ExplicitRegions, Regions); } return state; } /// \brief Run checkers for handling assumptions on symbolic values. -const GRState * -CheckerManager::runCheckersForEvalAssume(const GRState *state, +const ProgramState * +CheckerManager::runCheckersForEvalAssume(const ProgramState *state, SVal Cond, bool Assumption) { for (unsigned i = 0, e = EvalAssumeCheckers.size(); i != e; ++i) { // If any checker declares the state infeasible (or if it starts that way), @@ -379,7 +396,9 @@ void CheckerManager::runCheckersForEvalCall(ExplodedNodeSet &Dst, const CallExpr *CE, ExprEngine &Eng, GraphExpander *defaultEval) { - if (EvalCallCheckers.empty() && defaultEval == 0) { + if (EvalCallCheckers.empty() && + InlineCallCheckers.empty() && + defaultEval == 0) { Dst.insert(Src); return; } @@ -389,13 +408,50 @@ void CheckerManager::runCheckersForEvalCall(ExplodedNodeSet &Dst, ExplodedNode *Pred = *NI; bool anyEvaluated = false; + + // First, check if any of the InlineCall callbacks can evaluate the call. + assert(InlineCallCheckers.size() <= 1 && + "InlineCall is a special hacky callback to allow intrusive" + "evaluation of the call (which simulates inlining). It is " + "currently only used by OSAtomicChecker and should go away " + "at some point."); + for (std::vector<InlineCallFunc>::iterator + EI = InlineCallCheckers.begin(), EE = InlineCallCheckers.end(); + EI != EE; ++EI) { + ExplodedNodeSet checkDst; + bool evaluated = (*EI)(CE, Eng, Pred, checkDst); + 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 + } + } + +#ifdef NDEBUG // on release don't check that no other checker also evals. + if (anyEvaluated) { + break; + } +#endif + + // Next, check if any of the EvalCall callbacks can evaluate the call. 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); + ProgramPoint::Kind K = ProgramPoint::PostStmtKind; + const ProgramPoint &L = ProgramPoint::getProgramPoint(CE, K, + Pred->getLocationContext(), EI->Checker); + bool evaluated = false; + { // CheckerContext generates transitions(populates checkDest) on + // destruction, so introduce the scope to make sure it gets properly + // populated. + CheckerContext C(checkDst, Eng.getBuilder(), Eng, Pred, L, 0); + evaluated = (*EI)(CE, C); + } assert(!(evaluated && anyEvaluated) && "There are more than one checkers evaluating the call"); if (evaluated) { @@ -407,6 +463,7 @@ void CheckerManager::runCheckersForEvalCall(ExplodedNodeSet &Dst, } } + // If none of the checkers evaluated the call, ask ExprEngine to handle it. if (!anyEvaluated) { if (defaultEval) defaultEval->expandGraph(Dst, Pred); @@ -425,6 +482,14 @@ void CheckerManager::runCheckersOnEndOfTranslationUnit( EndOfTranslationUnitCheckers[i](TU, mgr, BR); } +void CheckerManager::runCheckersForPrintState(raw_ostream &Out, + const ProgramState *State, + const char *NL, const char *Sep) { + for (llvm::DenseMap<CheckerTag, CheckerRef>::iterator + I = CheckerTags.begin(), E = CheckerTags.end(); I != E; ++I) + I->second->printState(Out, State, NL, Sep); +} + //===----------------------------------------------------------------------===// // Internal registration functions for AST traversing. //===----------------------------------------------------------------------===// @@ -504,6 +569,10 @@ void CheckerManager::_registerForEvalCall(EvalCallFunc checkfn) { EvalCallCheckers.push_back(checkfn); } +void CheckerManager::_registerForInlineCall(InlineCallFunc checkfn) { + InlineCallCheckers.push_back(checkfn); +} + void CheckerManager::_registerForEndOfTranslationUnit( CheckEndOfTranslationUnit checkfn) { EndOfTranslationUnitCheckers.push_back(checkfn); @@ -542,7 +611,4 @@ CheckerManager::~CheckerManager() { } // Anchor for the vtable. -CheckerProvider::~CheckerProvider() { } - -// Anchor for the vtable. GraphExpander::~GraphExpander() { } diff --git a/lib/StaticAnalyzer/Core/CheckerRegistry.cpp b/lib/StaticAnalyzer/Core/CheckerRegistry.cpp new file mode 100644 index 000000000000..13401acf3da9 --- /dev/null +++ b/lib/StaticAnalyzer/Core/CheckerRegistry.cpp @@ -0,0 +1,149 @@ +//===--- CheckerRegistry.cpp - Maintains all available checkers -*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Core/CheckerRegistry.h" +#include "clang/StaticAnalyzer/Core/CheckerOptInfo.h" + +using namespace clang; +using namespace ento; + +static const char PackageSeparator = '.'; +typedef llvm::DenseSet<const CheckerRegistry::CheckerInfo *> CheckerInfoSet; + + +static bool checkerNameLT(const CheckerRegistry::CheckerInfo &a, + const CheckerRegistry::CheckerInfo &b) { + return a.FullName < b.FullName; +} + +static bool isInPackage(const CheckerRegistry::CheckerInfo &checker, + StringRef packageName) { + // Does the checker's full name have the package as a prefix? + if (!checker.FullName.startswith(packageName)) + return false; + + // Is the package actually just the name of a specific checker? + if (checker.FullName.size() == packageName.size()) + return true; + + // Is the checker in the package (or a subpackage)? + if (checker.FullName[packageName.size()] == PackageSeparator) + return true; + + return false; +} + +static void collectCheckers(const CheckerRegistry::CheckerInfoList &checkers, + const llvm::StringMap<size_t> &packageSizes, + CheckerOptInfo &opt, CheckerInfoSet &collected) { + // Use a binary search to find the possible start of the package. + CheckerRegistry::CheckerInfo packageInfo(NULL, opt.getName(), ""); + CheckerRegistry::CheckerInfoList::const_iterator e = checkers.end(); + CheckerRegistry::CheckerInfoList::const_iterator i = + std::lower_bound(checkers.begin(), e, packageInfo, checkerNameLT); + + // If we didn't even find a possible package, give up. + if (i == e) + return; + + // If what we found doesn't actually start the package, give up. + if (!isInPackage(*i, opt.getName())) + return; + + // There is at least one checker in the package; claim the option. + opt.claim(); + + // See how large the package is. + // If the package doesn't exist, assume the option refers to a single checker. + size_t size = 1; + llvm::StringMap<size_t>::const_iterator packageSize = + packageSizes.find(opt.getName()); + if (packageSize != packageSizes.end()) + size = packageSize->getValue(); + + // Step through all the checkers in the package. + for (e = i+size; i != e; ++i) { + if (opt.isEnabled()) + collected.insert(&*i); + else + collected.erase(&*i); + } +} + +void CheckerRegistry::addChecker(InitializationFunction fn, StringRef name, + StringRef desc) { + Checkers.push_back(CheckerInfo(fn, name, desc)); + + // Record the presence of the checker in its packages. + StringRef packageName, leafName; + llvm::tie(packageName, leafName) = name.rsplit(PackageSeparator); + while (!leafName.empty()) { + Packages[packageName] += 1; + llvm::tie(packageName, leafName) = packageName.rsplit(PackageSeparator); + } +} + +void CheckerRegistry::initializeManager(CheckerManager &checkerMgr, + SmallVectorImpl<CheckerOptInfo> &opts) const { + // Sort checkers for efficient collection. + std::sort(Checkers.begin(), Checkers.end(), checkerNameLT); + + // Collect checkers enabled by the options. + CheckerInfoSet enabledCheckers; + for (SmallVectorImpl<CheckerOptInfo>::iterator + i = opts.begin(), e = opts.end(); i != e; ++i) { + collectCheckers(Checkers, Packages, *i, enabledCheckers); + } + + // Initialize the CheckerManager with all enabled checkers. + for (CheckerInfoSet::iterator + i = enabledCheckers.begin(), e = enabledCheckers.end(); i != e; ++i) { + (*i)->Initialize(checkerMgr); + } +} + +void CheckerRegistry::printHelp(llvm::raw_ostream &out, + size_t maxNameChars) const { + // FIXME: Alphabetical sort puts 'experimental' in the middle. + // Would it be better to name it '~experimental' or something else + // that's ASCIIbetically last? + std::sort(Checkers.begin(), Checkers.end(), checkerNameLT); + + // FIXME: Print available packages. + + out << "CHECKERS:\n"; + + // Find the maximum option length. + size_t optionFieldWidth = 0; + for (CheckerInfoList::const_iterator i = Checkers.begin(), e = Checkers.end(); + i != e; ++i) { + // Limit the amount of padding we are willing to give up for alignment. + // Package.Name Description [Hidden] + size_t nameLength = i->FullName.size(); + if (nameLength <= maxNameChars) + optionFieldWidth = std::max(optionFieldWidth, nameLength); + } + + const size_t initialPad = 2; + for (CheckerInfoList::const_iterator i = Checkers.begin(), e = Checkers.end(); + i != e; ++i) { + out.indent(initialPad) << i->FullName; + + int pad = optionFieldWidth - i->FullName.size(); + + // Break on long option names. + if (pad < 0) { + out << '\n'; + pad = optionFieldWidth + initialPad; + } + out.indent(pad + 2) << i->Desc; + + out << '\n'; + } +} diff --git a/lib/StaticAnalyzer/Core/CoreEngine.cpp b/lib/StaticAnalyzer/Core/CoreEngine.cpp index 34cd6e8884a0..525219846a93 100644 --- a/lib/StaticAnalyzer/Core/CoreEngine.cpp +++ b/lib/StaticAnalyzer/Core/CoreEngine.cpp @@ -17,22 +17,12 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" #include "clang/Index/TranslationUnit.h" #include "clang/AST/Expr.h" +#include "clang/AST/StmtCXX.h" #include "llvm/Support/Casting.h" #include "llvm/ADT/DenseMap.h" - -using llvm::cast; -using llvm::isa; using namespace clang; using namespace ento; -// This should be removed in the future. -namespace clang { -namespace ento { -TransferFuncs* MakeCFRefCountTF(ASTContext& Ctx, bool GCEnabled, - const LangOptions& lopts); -} -} - //===----------------------------------------------------------------------===// // Worklist classes for exploration of reachable states. //===----------------------------------------------------------------------===// @@ -41,7 +31,7 @@ WorkList::Visitor::~Visitor() {} namespace { class DFS : public WorkList { - llvm::SmallVector<WorkListUnit,20> Stack; + SmallVector<WorkListUnit,20> Stack; public: virtual bool hasWork() const { return !Stack.empty(); @@ -59,7 +49,7 @@ public: } virtual bool visitItemsInWorkList(Visitor &V) { - for (llvm::SmallVectorImpl<WorkListUnit>::iterator + for (SmallVectorImpl<WorkListUnit>::iterator I = Stack.begin(), E = Stack.end(); I != E; ++I) { if (V.visit(*I)) return true; @@ -107,7 +97,7 @@ WorkList *WorkList::makeBFS() { return new BFS(); } namespace { class BFSBlockDFSContents : public WorkList { std::deque<WorkListUnit> Queue; - llvm::SmallVector<WorkListUnit,20> Stack; + SmallVector<WorkListUnit,20> Stack; public: virtual bool hasWork() const { return !Queue.empty() || !Stack.empty(); @@ -136,7 +126,7 @@ namespace { return U; } virtual bool visitItemsInWorkList(Visitor &V) { - for (llvm::SmallVectorImpl<WorkListUnit>::iterator + for (SmallVectorImpl<WorkListUnit>::iterator I = Stack.begin(), E = Stack.end(); I != E; ++I) { if (V.visit(*I)) return true; @@ -162,12 +152,12 @@ WorkList* WorkList::makeBFSBlockDFSContents() { /// ExecuteWorkList - Run the worklist algorithm for a maximum number of steps. bool CoreEngine::ExecuteWorkList(const LocationContext *L, unsigned Steps, - const GRState *InitState) { + const ProgramState *InitState) { if (G->num_roots() == 0) { // Initialize the analysis by constructing // the root if none exists. - const CFGBlock* Entry = &(L->getCFG()->getEntry()); + const CFGBlock *Entry = &(L->getCFG()->getEntry()); assert (Entry->empty() && "Entry block must be empty."); @@ -176,7 +166,7 @@ bool CoreEngine::ExecuteWorkList(const LocationContext *L, unsigned Steps, "Entry block must have 1 successor."); // Get the solitary successor. - const CFGBlock* Succ = *(Entry->succ_begin()); + const CFGBlock *Succ = *(Entry->succ_begin()); // Construct an edge representing the // starting location in the function. @@ -208,7 +198,7 @@ bool CoreEngine::ExecuteWorkList(const LocationContext *L, unsigned Steps, WList->setBlockCounter(WU.getBlockCounter()); // Retrieve the node. - ExplodedNode* Node = WU.getNode(); + ExplodedNode *Node = WU.getNode(); // Dispatch on the location type. switch (Node->getLocation().getKind()) { @@ -246,11 +236,11 @@ bool CoreEngine::ExecuteWorkList(const LocationContext *L, unsigned Steps, } void CoreEngine::ExecuteWorkListWithInitialState(const LocationContext *L, - unsigned Steps, - const GRState *InitState, - ExplodedNodeSet &Dst) { + unsigned Steps, + const ProgramState *InitState, + ExplodedNodeSet &Dst) { ExecuteWorkList(L, Steps, InitState); - for (llvm::SmallVectorImpl<ExplodedNode*>::iterator I = G->EndNodes.begin(), + for (SmallVectorImpl<ExplodedNode*>::iterator I = G->EndNodes.begin(), E = G->EndNodes.end(); I != E; ++I) { Dst.Add(*I); } @@ -268,9 +258,9 @@ void CoreEngine::HandleCallExit(const CallExit &L, ExplodedNode *Pred) { SubEng.processCallExit(Builder); } -void CoreEngine::HandleBlockEdge(const BlockEdge& L, ExplodedNode* Pred) { +void CoreEngine::HandleBlockEdge(const BlockEdge &L, ExplodedNode *Pred) { - const CFGBlock* Blk = L.getDst(); + const CFGBlock *Blk = L.getDst(); // Check if we are entering the EXIT block. if (Blk == &(L.getLocationContext()->getCFG()->getExit())) { @@ -305,15 +295,15 @@ void CoreEngine::HandleBlockEdge(const BlockEdge& L, ExplodedNode* Pred) { } } - for (llvm::SmallVectorImpl<ExplodedNode*>::const_iterator + for (SmallVectorImpl<ExplodedNode*>::const_iterator I = nodeBuilder.sinks().begin(), E = nodeBuilder.sinks().end(); I != E; ++I) { blocksExhausted.push_back(std::make_pair(L, *I)); } } -void CoreEngine::HandleBlockEntrance(const BlockEntrance& L, - ExplodedNode* Pred) { +void CoreEngine::HandleBlockEntrance(const BlockEntrance &L, + ExplodedNode *Pred) { // Increment the block counter. BlockCounter Counter = WList->getBlockCounter(); @@ -324,21 +314,19 @@ void CoreEngine::HandleBlockEntrance(const BlockEntrance& L, // Process the entrance of the block. if (CFGElement E = L.getFirstElement()) { - StmtNodeBuilder Builder(L.getBlock(), 0, Pred, this, - SubEng.getStateManager()); + StmtNodeBuilder Builder(L.getBlock(), 0, Pred, this); SubEng.processCFGElement(E, Builder); } else HandleBlockExit(L.getBlock(), Pred); } -void CoreEngine::HandleBlockExit(const CFGBlock * B, ExplodedNode* Pred) { +void CoreEngine::HandleBlockExit(const CFGBlock * B, ExplodedNode *Pred) { - if (const Stmt* Term = B->getTerminator()) { + if (const Stmt *Term = B->getTerminator()) { switch (Term->getStmtClass()) { default: - assert(false && "Analysis for this terminator not implemented."); - break; + llvm_unreachable("Analysis for this terminator not implemented."); case Stmt::BinaryOperatorClass: // '&&' and '||' HandleBranch(cast<BinaryOperator>(Term)->getLHS(), Term, B, Pred); @@ -361,6 +349,10 @@ void CoreEngine::HandleBlockExit(const CFGBlock * B, ExplodedNode* Pred) { HandleBranch(cast<DoStmt>(Term)->getCond(), Term, B, Pred); return; + case Stmt::CXXForRangeStmtClass: + HandleBranch(cast<CXXForRangeStmt>(Term)->getCond(), Term, B, Pred); + return; + case Stmt::ForStmtClass: HandleBranch(cast<ForStmt>(Term)->getCond(), Term, B, Pred); return; @@ -422,34 +414,34 @@ void CoreEngine::HandleBlockExit(const CFGBlock * B, ExplodedNode* Pred) { Pred->State, Pred); } -void CoreEngine::HandleBranch(const Stmt* Cond, const Stmt* Term, - const CFGBlock * B, ExplodedNode* Pred) { +void CoreEngine::HandleBranch(const Stmt *Cond, const Stmt *Term, + const CFGBlock * B, ExplodedNode *Pred) { assert(B->succ_size() == 2); BranchNodeBuilder Builder(B, *(B->succ_begin()), *(B->succ_begin()+1), Pred, this); SubEng.processBranch(Cond, Term, Builder); } -void CoreEngine::HandlePostStmt(const CFGBlock* B, unsigned StmtIdx, - ExplodedNode* Pred) { +void CoreEngine::HandlePostStmt(const CFGBlock *B, unsigned StmtIdx, + ExplodedNode *Pred) { assert (!B->empty()); if (StmtIdx == B->size()) HandleBlockExit(B, Pred); else { - StmtNodeBuilder Builder(B, StmtIdx, Pred, this, - SubEng.getStateManager()); + StmtNodeBuilder Builder(B, StmtIdx, Pred, this); SubEng.processCFGElement((*B)[StmtIdx], Builder); } } /// generateNode - Utility method to generate nodes, hook up successors, /// and add nodes to the worklist. -void CoreEngine::generateNode(const ProgramPoint& Loc, - const GRState* State, ExplodedNode* Pred) { +void CoreEngine::generateNode(const ProgramPoint &Loc, + const ProgramState *State, + ExplodedNode *Pred) { bool IsNew; - ExplodedNode* Node = G->getNode(Loc, State, &IsNew); + ExplodedNode *Node = G->getNode(Loc, State, &IsNew); if (Pred) Node->addPredecessor(Pred, *G); // Link 'Node' with its predecessor. @@ -463,7 +455,7 @@ void CoreEngine::generateNode(const ProgramPoint& Loc, } ExplodedNode * -GenericNodeBuilderImpl::generateNodeImpl(const GRState *state, +GenericNodeBuilderImpl::generateNodeImpl(const ProgramState *state, ExplodedNode *pred, ProgramPoint programPoint, bool asSink) { @@ -483,14 +475,14 @@ GenericNodeBuilderImpl::generateNodeImpl(const GRState *state, return 0; } -StmtNodeBuilder::StmtNodeBuilder(const CFGBlock* b, unsigned idx, - ExplodedNode* N, CoreEngine* e, - GRStateManager &mgr) - : Eng(*e), B(*b), Idx(idx), Pred(N), Mgr(mgr), +StmtNodeBuilder::StmtNodeBuilder(const CFGBlock *b, + unsigned idx, + ExplodedNode *N, + CoreEngine* e) + : Eng(*e), B(*b), Idx(idx), Pred(N), PurgingDeadSymbols(false), BuildSinks(false), hasGeneratedNode(false), PointKind(ProgramPoint::PostStmtKind), Tag(0) { Deferred.insert(N); - CleanedState = Pred->getState(); } StmtNodeBuilder::~StmtNodeBuilder() { @@ -499,7 +491,7 @@ StmtNodeBuilder::~StmtNodeBuilder() { GenerateAutoTransition(*I); } -void StmtNodeBuilder::GenerateAutoTransition(ExplodedNode* N) { +void StmtNodeBuilder::GenerateAutoTransition(ExplodedNode *N) { assert (!N->isSink()); // Check if this node entered a callee. @@ -526,18 +518,20 @@ void StmtNodeBuilder::GenerateAutoTransition(ExplodedNode* N) { } bool IsNew; - ExplodedNode* Succ = Eng.G->getNode(Loc, N->State, &IsNew); + ExplodedNode *Succ = Eng.G->getNode(Loc, N->State, &IsNew); Succ->addPredecessor(N, *Eng.G); if (IsNew) Eng.WList->enqueue(Succ, &B, Idx+1); } -ExplodedNode* StmtNodeBuilder::MakeNode(ExplodedNodeSet& Dst, const Stmt* S, - ExplodedNode* Pred, const GRState* St, - ProgramPoint::Kind K) { +ExplodedNode *StmtNodeBuilder::MakeNode(ExplodedNodeSet &Dst, + const Stmt *S, + ExplodedNode *Pred, + const ProgramState *St, + ProgramPoint::Kind K) { - ExplodedNode* N = generateNode(S, St, Pred, K); + ExplodedNode *N = generateNode(S, St, Pred, K); if (N) { if (BuildSinks) @@ -549,46 +543,24 @@ ExplodedNode* StmtNodeBuilder::MakeNode(ExplodedNodeSet& Dst, const Stmt* S, return N; } -static ProgramPoint GetProgramPoint(const Stmt *S, ProgramPoint::Kind K, - const LocationContext *LC, const void *tag){ - switch (K) { - default: - assert(false && "Unhandled ProgramPoint kind"); - case ProgramPoint::PreStmtKind: - return PreStmt(S, LC, tag); - case ProgramPoint::PostStmtKind: - return PostStmt(S, LC, tag); - case ProgramPoint::PreLoadKind: - return PreLoad(S, LC, tag); - case ProgramPoint::PostLoadKind: - return PostLoad(S, LC, tag); - case ProgramPoint::PreStoreKind: - return PreStore(S, LC, tag); - case ProgramPoint::PostStoreKind: - return PostStore(S, LC, tag); - case ProgramPoint::PostLValueKind: - return PostLValue(S, LC, tag); - case ProgramPoint::PostPurgeDeadSymbolsKind: - return PostPurgeDeadSymbols(S, LC, tag); - } -} - ExplodedNode* -StmtNodeBuilder::generateNodeInternal(const Stmt* S, const GRState* state, - ExplodedNode* Pred, - ProgramPoint::Kind K, - const void *tag) { +StmtNodeBuilder::generateNodeInternal(const Stmt *S, + const ProgramState *state, + ExplodedNode *Pred, + ProgramPoint::Kind K, + const ProgramPointTag *tag) { - const ProgramPoint &L = GetProgramPoint(S, K, Pred->getLocationContext(),tag); + const ProgramPoint &L = ProgramPoint::getProgramPoint(S, K, + Pred->getLocationContext(), tag); return generateNodeInternal(L, state, Pred); } ExplodedNode* StmtNodeBuilder::generateNodeInternal(const ProgramPoint &Loc, - const GRState* State, - ExplodedNode* Pred) { + const ProgramState *State, + ExplodedNode *Pred) { bool IsNew; - ExplodedNode* N = Eng.G->getNode(Loc, State, &IsNew); + ExplodedNode *N = Eng.G->getNode(Loc, State, &IsNew); N->addPredecessor(Pred, *Eng.G); Deferred.erase(Pred); @@ -601,11 +573,11 @@ StmtNodeBuilder::generateNodeInternal(const ProgramPoint &Loc, } // This function generate a new ExplodedNode but not a new branch(block edge). -ExplodedNode* BranchNodeBuilder::generateNode(const Stmt* Condition, - const GRState* State) { +ExplodedNode *BranchNodeBuilder::generateNode(const Stmt *Condition, + const ProgramState *State) { bool IsNew; - ExplodedNode* Succ + ExplodedNode *Succ = Eng.G->getNode(PostCondition(Condition, Pred->getLocationContext()), State, &IsNew); @@ -619,8 +591,8 @@ ExplodedNode* BranchNodeBuilder::generateNode(const Stmt* Condition, return NULL; } -ExplodedNode* BranchNodeBuilder::generateNode(const GRState* State, - bool branch) { +ExplodedNode *BranchNodeBuilder::generateNode(const ProgramState *State, + bool branch) { // If the branch has been marked infeasible we should not generate a node. if (!isFeasible(branch)) @@ -628,7 +600,7 @@ ExplodedNode* BranchNodeBuilder::generateNode(const GRState* State, bool IsNew; - ExplodedNode* Succ = + ExplodedNode *Succ = Eng.G->getNode(BlockEdge(Src,branch ? DstT:DstF,Pred->getLocationContext()), State, &IsNew); @@ -657,11 +629,12 @@ BranchNodeBuilder::~BranchNodeBuilder() { ExplodedNode* -IndirectGotoNodeBuilder::generateNode(const iterator& I, const GRState* St, - bool isSink) { +IndirectGotoNodeBuilder::generateNode(const iterator &I, + const ProgramState *St, + bool isSink) { bool IsNew; - ExplodedNode* Succ = Eng.G->getNode(BlockEdge(Src, I.getBlock(), + ExplodedNode *Succ = Eng.G->getNode(BlockEdge(Src, I.getBlock(), Pred->getLocationContext()), St, &IsNew); Succ->addPredecessor(Pred, *Eng.G); @@ -681,34 +654,38 @@ IndirectGotoNodeBuilder::generateNode(const iterator& I, const GRState* St, ExplodedNode* -SwitchNodeBuilder::generateCaseStmtNode(const iterator& I, const GRState* St){ +SwitchNodeBuilder::generateCaseStmtNode(const iterator &I, + const ProgramState *St) { bool IsNew; - - ExplodedNode* Succ = Eng.G->getNode(BlockEdge(Src, I.getBlock(), - Pred->getLocationContext()), St, &IsNew); + ExplodedNode *Succ = Eng.G->getNode(BlockEdge(Src, I.getBlock(), + Pred->getLocationContext()), + St, &IsNew); Succ->addPredecessor(Pred, *Eng.G); - if (IsNew) { Eng.WList->enqueue(Succ); return Succ; } - return NULL; } ExplodedNode* -SwitchNodeBuilder::generateDefaultCaseNode(const GRState* St, bool isSink) { - +SwitchNodeBuilder::generateDefaultCaseNode(const ProgramState *St, + bool isSink) { // Get the block for the default case. - assert (Src->succ_rbegin() != Src->succ_rend()); - CFGBlock* DefaultBlock = *Src->succ_rbegin(); + assert(Src->succ_rbegin() != Src->succ_rend()); + CFGBlock *DefaultBlock = *Src->succ_rbegin(); + // Sanity check for default blocks that are unreachable and not caught + // by earlier stages. + if (!DefaultBlock) + return NULL; + bool IsNew; - ExplodedNode* Succ = Eng.G->getNode(BlockEdge(Src, DefaultBlock, - Pred->getLocationContext()), St, &IsNew); + ExplodedNode *Succ = Eng.G->getNode(BlockEdge(Src, DefaultBlock, + Pred->getLocationContext()), St, &IsNew); Succ->addPredecessor(Pred, *Eng.G); if (IsNew) { @@ -735,12 +712,13 @@ EndOfFunctionNodeBuilder::~EndOfFunctionNodeBuilder() { } ExplodedNode* -EndOfFunctionNodeBuilder::generateNode(const GRState* State, - ExplodedNode* P, const void *tag) { +EndOfFunctionNodeBuilder::generateNode(const ProgramState *State, + ExplodedNode *P, + const ProgramPointTag *tag) { hasGeneratedNode = true; bool IsNew; - ExplodedNode* Node = Eng.G->getNode(BlockEntrance(&B, + ExplodedNode *Node = Eng.G->getNode(BlockEntrance(&B, Pred->getLocationContext(), tag ? tag : Tag), State, &IsNew); @@ -754,7 +732,7 @@ EndOfFunctionNodeBuilder::generateNode(const GRState* State, return NULL; } -void EndOfFunctionNodeBuilder::GenerateCallExitNode(const GRState *state) { +void EndOfFunctionNodeBuilder::GenerateCallExitNode(const ProgramState *state) { hasGeneratedNode = true; // Create a CallExit node and enqueue it. const StackFrameContext *LocCtx @@ -773,7 +751,7 @@ void EndOfFunctionNodeBuilder::GenerateCallExitNode(const GRState *state) { } -void CallEnterNodeBuilder::generateNode(const GRState *state) { +void CallEnterNodeBuilder::generateNode(const ProgramState *state) { // Check if the callee is in the same translation unit. if (CalleeCtx->getTranslationUnit() != Pred->getLocationContext()->getTranslationUnit()) { @@ -787,30 +765,13 @@ void CallEnterNodeBuilder::generateNode(const GRState *state) { // Create a new AnalysisManager with components of the callee's // TranslationUnit. - // The Diagnostic is actually shared when we create ASTUnits from AST files. - AnalysisManager AMgr(TU->getASTContext(), TU->getDiagnostic(), - OldMgr.getLangOptions(), - OldMgr.getPathDiagnosticClient(), - OldMgr.getStoreManagerCreator(), - OldMgr.getConstraintManagerCreator(), - OldMgr.getCheckerManager(), - OldMgr.getIndexer(), - OldMgr.getMaxNodes(), OldMgr.getMaxVisit(), - OldMgr.shouldVisualizeGraphviz(), - OldMgr.shouldVisualizeUbigraph(), - OldMgr.shouldPurgeDead(), - OldMgr.shouldEagerlyAssume(), - OldMgr.shouldTrimGraph(), - OldMgr.shouldInlineCall(), - OldMgr.getAnalysisContextManager().getUseUnoptimizedCFG(), - OldMgr.getAnalysisContextManager().getAddImplicitDtors(), - OldMgr.getAnalysisContextManager().getAddInitializers(), - OldMgr.shouldEagerlyTrimExplodedGraph()); - llvm::OwningPtr<TransferFuncs> TF(MakeCFRefCountTF(AMgr.getASTContext(), - /* GCEnabled */ false, - AMgr.getLangOptions())); + // The Diagnostic is actually shared when we create ASTUnits from AST files. + AnalysisManager AMgr(TU->getASTContext(), TU->getDiagnostic(), OldMgr); + // Create the new engine. - ExprEngine NewEng(AMgr, TF.take()); + // FIXME: This cast isn't really safe. + bool GCEnabled = static_cast<ExprEngine&>(Eng.SubEng).isO |