diff options
Diffstat (limited to 'contrib/llvm/tools/clang/lib/StaticAnalyzer')
101 files changed, 10626 insertions, 4968 deletions
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/AdjustedReturnValueChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/AdjustedReturnValueChecker.cpp index dc524ba..84ea8c7 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/AdjustedReturnValueChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/AdjustedReturnValueChecker.cpp @@ -37,20 +37,21 @@ void AdjustedReturnValueChecker::checkPostStmt(const CallExpr *CE, QualType expectedResultTy = CE->getType(); // Fetch the signature of the called function. - const ProgramState *state = C.getState(); + ProgramStateRef state = C.getState(); + const LocationContext *LCtx = C.getLocationContext(); - SVal V = state->getSVal(CE); + SVal V = state->getSVal(CE, LCtx); if (V.isUnknown()) return; // Casting to void? Discard the value. if (expectedResultTy->isVoidType()) { - C.generateNode(state->BindExpr(CE, UnknownVal())); + C.addTransition(state->BindExpr(CE, LCtx, UnknownVal())); return; } - const MemRegion *callee = state->getSVal(CE->getCallee()).getAsRegion(); + const MemRegion *callee = state->getSVal(CE->getCallee(), LCtx).getAsRegion(); if (!callee) return; @@ -82,7 +83,7 @@ void AdjustedReturnValueChecker::checkPostStmt(const CallExpr *CE, // the cast avoids some assertion failures elsewhere. SValBuilder &svalBuilder = C.getSValBuilder(); V = svalBuilder.evalCast(V, expectedResultTy, actualResultTy); - C.generateNode(state->BindExpr(CE, V)); + C.addTransition(state->BindExpr(CE, LCtx, V)); } } diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/AnalyzerStatsChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/AnalyzerStatsChecker.cpp index cd977bf..aa6f97b 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/AnalyzerStatsChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/AnalyzerStatsChecker.cpp @@ -8,6 +8,7 @@ //===----------------------------------------------------------------------===// // This file reports various statistics about analyzer visitation. //===----------------------------------------------------------------------===// +#define DEBUG_TYPE "StatsChecker" #include "ClangSACheckers.h" #include "clang/StaticAnalyzer/Core/Checker.h" @@ -16,12 +17,20 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" +#include "clang/AST/DeclObjC.h" #include "clang/Basic/SourceManager.h" #include "llvm/ADT/SmallPtrSet.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/Statistic.h" using namespace clang; using namespace ento; +STATISTIC(NumBlocks, + "The # of blocks in top level functions"); +STATISTIC(NumBlocksUnreachable, + "The # of unreachable blocks in analyzing top level functions"); + namespace { class AnalyzerStatsChecker : public Checker<check::EndAnalysis> { public: @@ -33,18 +42,23 @@ void AnalyzerStatsChecker::checkEndAnalysis(ExplodedGraph &G, BugReporter &B, ExprEngine &Eng) const { const CFG *C = 0; - const Decl *D = 0; - const LocationContext *LC = 0; const SourceManager &SM = B.getSourceManager(); llvm::SmallPtrSet<const CFGBlock*, 256> reachable; - // Iterate over explodedgraph + // Root node should have the location context of the top most function. + const ExplodedNode *GraphRoot = *G.roots_begin(); + const LocationContext *LC = GraphRoot->getLocation().getLocationContext(); + + const Decl *D = LC->getDecl(); + + // Iterate over the exploded graph. for (ExplodedGraph::node_iterator I = G.nodes_begin(); I != G.nodes_end(); ++I) { const ProgramPoint &P = I->getLocation(); - // Save the LocationContext if we don't have it already - if (!LC) - LC = P.getLocationContext(); + + // Only check the coverage in the top level function (optimization). + if (D != P.getLocationContext()->getDecl()) + continue; if (const BlockEntrance *BE = dyn_cast<BlockEntrance>(&P)) { const CFGBlock *CB = BE->getBlock(); @@ -52,9 +66,8 @@ void AnalyzerStatsChecker::checkEndAnalysis(ExplodedGraph &G, } } - // Get the CFG and the Decl of this block + // Get the CFG and the Decl of this block. C = LC->getCFG(); - D = LC->getAnalysisContext()->getDecl(); unsigned total = 0, unreachable = 0; @@ -70,33 +83,39 @@ void AnalyzerStatsChecker::checkEndAnalysis(ExplodedGraph &G, // We never 'reach' the entry block, so correct the unreachable count unreachable--; + // There is no BlockEntrance corresponding to the exit block as well, so + // assume it is reached as well. + unreachable--; // Generate the warning string - llvm::SmallString<128> buf; + SmallString<128> buf; llvm::raw_svector_ostream output(buf); PresumedLoc Loc = SM.getPresumedLoc(D->getLocation()); - if (Loc.isValid()) { - output << Loc.getFilename() << " : "; + if (!Loc.isValid()) + return; - if (isa<FunctionDecl>(D) || isa<ObjCMethodDecl>(D)) { - const NamedDecl *ND = cast<NamedDecl>(D); - output << *ND; - } - else if (isa<BlockDecl>(D)) { - output << "block(line:" << Loc.getLine() << ":col:" << Loc.getColumn(); - } + if (isa<FunctionDecl>(D) || isa<ObjCMethodDecl>(D)) { + const NamedDecl *ND = cast<NamedDecl>(D); + output << *ND; + } + else if (isa<BlockDecl>(D)) { + output << "block(line:" << Loc.getLine() << ":col:" << Loc.getColumn(); } + NumBlocksUnreachable += unreachable; + NumBlocks += total; + std::string NameOfRootFunction = output.str(); + output << " -> Total CFGBlocks: " << total << " | Unreachable CFGBlocks: " << unreachable << " | Exhausted Block: " << (Eng.wasBlocksExhausted() ? "yes" : "no") << " | Empty WorkList: " << (Eng.hasEmptyWorkList() ? "yes" : "no"); - B.EmitBasicReport("Analyzer Statistics", "Internal Statistics", output.str(), - PathDiagnosticLocation(D, SM)); + B.EmitBasicReport(D, "Analyzer Statistics", "Internal Statistics", + output.str(), PathDiagnosticLocation(D, SM)); - // Emit warning for each block we bailed out on + // Emit warning for each block we bailed out on. typedef CoreEngine::BlocksExhausted::const_iterator ExhaustedIterator; const CoreEngine &CE = Eng.getCoreEngine(); for (ExhaustedIterator I = CE.blocks_exhausted_begin(), @@ -104,10 +123,15 @@ void AnalyzerStatsChecker::checkEndAnalysis(ExplodedGraph &G, const BlockEdge &BE = I->first; const CFGBlock *Exit = BE.getDst(); 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", - PathDiagnosticLocation::createBegin(CS->getStmt(), SM, LC)); + if (const CFGStmt *CS = dyn_cast<CFGStmt>(&CE)) { + SmallString<128> bufI; + llvm::raw_svector_ostream outputI(bufI); + outputI << "(" << NameOfRootFunction << ")" << + ": The analyzer generated a sink at this point"; + B.EmitBasicReport(D, "Sink Point", "Internal Statistics", outputI.str(), + PathDiagnosticLocation::createBegin(CS->getStmt(), + SM, LC)); + } } } diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ArrayBoundChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ArrayBoundChecker.cpp index 6935c5f..b2ad184 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ArrayBoundChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ArrayBoundChecker.cpp @@ -25,7 +25,7 @@ using namespace ento; namespace { class ArrayBoundChecker : public Checker<check::Location> { - mutable llvm::OwningPtr<BuiltinBug> BT; + mutable OwningPtr<BuiltinBug> BT; public: void checkLocation(SVal l, bool isLoad, const Stmt* S, CheckerContext &C) const; @@ -51,15 +51,15 @@ void ArrayBoundChecker::checkLocation(SVal l, bool isLoad, const Stmt* LoadS, if (Idx.isZeroConstant()) return; - const ProgramState *state = C.getState(); + ProgramStateRef state = C.getState(); // Get the size of the array. DefinedOrUnknownSVal NumElements = C.getStoreManager().getSizeInElements(state, ER->getSuperRegion(), ER->getValueType()); - const ProgramState *StInBound = state->assumeInBound(Idx, NumElements, true); - const ProgramState *StOutBound = state->assumeInBound(Idx, NumElements, false); + ProgramStateRef StInBound = state->assumeInBound(Idx, NumElements, true); + ProgramStateRef StOutBound = state->assumeInBound(Idx, NumElements, false); if (StOutBound && !StInBound) { ExplodedNode *N = C.generateSink(StOutBound); if (!N) @@ -84,7 +84,6 @@ void ArrayBoundChecker::checkLocation(SVal l, bool isLoad, const Stmt* LoadS, // Array bound check succeeded. From this point forward the array bound // should always succeed. - assert(StInBound); C.addTransition(StInBound); } diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp index 6175028..c6efe94 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp @@ -19,6 +19,8 @@ #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" #include "clang/AST/CharUnits.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/STLExtras.h" using namespace clang; using namespace ento; @@ -26,11 +28,11 @@ using namespace ento; namespace { class ArrayBoundCheckerV2 : public Checker<check::Location> { - mutable llvm::OwningPtr<BuiltinBug> BT; + mutable OwningPtr<BuiltinBug> BT; - enum OOB_Kind { OOB_Precedes, OOB_Excedes }; + enum OOB_Kind { OOB_Precedes, OOB_Excedes, OOB_Tainted }; - void reportOOB(CheckerContext &C, const ProgramState *errorState, + void reportOOB(CheckerContext &C, ProgramStateRef errorState, OOB_Kind kind) const; public: @@ -54,7 +56,7 @@ public: NonLoc getByteOffset() const { return cast<NonLoc>(byteOffset); } const SubRegion *getRegion() const { return baseRegion; } - static RegionRawOffsetV2 computeOffset(const ProgramState *state, + static RegionRawOffsetV2 computeOffset(ProgramStateRef state, SValBuilder &svalBuilder, SVal location); @@ -92,8 +94,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 ProgramState *state = checkerContext.getState(); - const ProgramState *originalState = state; + ProgramStateRef state = checkerContext.getState(); + ProgramStateRef originalState = state; SValBuilder &svalBuilder = checkerContext.getSValBuilder(); const RegionRawOffsetV2 &rawOffset = @@ -118,7 +120,7 @@ void ArrayBoundCheckerV2::checkLocation(SVal location, bool isLoad, if (!lowerBoundToCheck) return; - const ProgramState *state_precedesLowerBound, *state_withinLowerBound; + ProgramStateRef state_precedesLowerBound, state_withinLowerBound; llvm::tie(state_precedesLowerBound, state_withinLowerBound) = state->assume(*lowerBoundToCheck); @@ -150,12 +152,20 @@ void ArrayBoundCheckerV2::checkLocation(SVal location, bool isLoad, if (!upperboundToCheck) break; - const ProgramState *state_exceedsUpperBound, *state_withinUpperBound; + ProgramStateRef state_exceedsUpperBound, state_withinUpperBound; llvm::tie(state_exceedsUpperBound, state_withinUpperBound) = state->assume(*upperboundToCheck); + + // If we are under constrained and the index variables are tainted, report. + if (state_exceedsUpperBound && state_withinUpperBound) { + if (state->isTainted(rawOffset.getByteOffset())) + reportOOB(checkerContext, state_exceedsUpperBound, OOB_Tainted); + return; + } - // Are we constrained enough to definitely exceed the upper bound? - if (state_exceedsUpperBound && !state_withinUpperBound) { + // If we are constrained enough to definitely exceed the upper bound, report. + if (state_exceedsUpperBound) { + assert(!state_withinUpperBound); reportOOB(checkerContext, state_exceedsUpperBound, OOB_Excedes); return; } @@ -166,11 +176,11 @@ void ArrayBoundCheckerV2::checkLocation(SVal location, bool isLoad, while (false); if (state != originalState) - checkerContext.generateNode(state); + checkerContext.addTransition(state); } void ArrayBoundCheckerV2::reportOOB(CheckerContext &checkerContext, - const ProgramState *errorState, + ProgramStateRef errorState, OOB_Kind kind) const { ExplodedNode *errorNode = checkerContext.generateSink(errorState); @@ -183,11 +193,20 @@ void ArrayBoundCheckerV2::reportOOB(CheckerContext &checkerContext, // FIXME: This diagnostics are preliminary. We should get far better // diagnostics for explaining buffer overruns. - llvm::SmallString<256> buf; + SmallString<256> buf; llvm::raw_svector_ostream os(buf); - os << "Out of bound memory access " - << (kind == OOB_Precedes ? "(accessed memory precedes memory block)" - : "(access exceeds upper limit of memory block)"); + os << "Out of bound memory access "; + switch (kind) { + case OOB_Precedes: + os << "(accessed memory precedes memory block)"; + break; + case OOB_Excedes: + os << "(access exceeds upper limit of memory block)"; + break; + case OOB_Tainted: + os << "(index is tainted)"; + break; + } checkerContext.EmitReport(new BugReport(*BT, os.str(), errorNode)); } @@ -221,7 +240,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 ProgramState *state, +static inline SVal scaleValue(ProgramStateRef state, NonLoc baseVal, CharUnits scaling, SValBuilder &sb) { return sb.evalBinOpNN(state, BO_Mul, baseVal, @@ -231,7 +250,7 @@ static inline SVal scaleValue(const ProgramState *state, // Add an SVal to another, treating unknown and undefined values as // summing to UnknownVal. Used by 'computeOffset'. -static SVal addValue(const ProgramState *state, SVal x, SVal y, +static SVal addValue(ProgramStateRef state, SVal x, SVal y, SValBuilder &svalBuilder) { // We treat UnknownVals and UndefinedVals the same here because we // only care about computing offsets. @@ -245,7 +264,7 @@ static SVal addValue(const ProgramState *state, SVal x, SVal y, /// Compute a raw byte offset from a base region. Used for array bounds /// checking. -RegionRawOffsetV2 RegionRawOffsetV2::computeOffset(const ProgramState *state, +RegionRawOffsetV2 RegionRawOffsetV2::computeOffset(ProgramStateRef state, SValBuilder &svalBuilder, SVal location) { @@ -277,9 +296,9 @@ RegionRawOffsetV2 RegionRawOffsetV2::computeOffset(const ProgramState *state, offset = addValue(state, getValue(offset, svalBuilder), scaleValue(state, - cast<NonLoc>(index), - astContext.getTypeSizeInChars(elemType), - svalBuilder), + cast<NonLoc>(index), + astContext.getTypeSizeInChars(elemType), + svalBuilder), svalBuilder); if (offset.isUnknownOrUndef()) diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/AttrNonNullChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/AttrNonNullChecker.cpp index 8296eb9..ab66e98 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/AttrNonNullChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/AttrNonNullChecker.cpp @@ -24,7 +24,7 @@ using namespace ento; namespace { class AttrNonNullChecker : public Checker< check::PreStmt<CallExpr> > { - mutable llvm::OwningPtr<BugType> BT; + mutable OwningPtr<BugType> BT; public: void checkPreStmt(const CallExpr *CE, CheckerContext &C) const; @@ -33,10 +33,11 @@ public: void AttrNonNullChecker::checkPreStmt(const CallExpr *CE, CheckerContext &C) const { - const ProgramState *state = C.getState(); + ProgramStateRef state = C.getState(); + const LocationContext *LCtx = C.getLocationContext(); // Check if the callee has a 'nonnull' attribute. - SVal X = state->getSVal(CE->getCallee()); + SVal X = state->getSVal(CE->getCallee(), LCtx); const FunctionDecl *FD = X.getAsFunctionDecl(); if (!FD) @@ -55,7 +56,7 @@ void AttrNonNullChecker::checkPreStmt(const CallExpr *CE, if (!Att->isNonNull(idx)) continue; - SVal V = state->getSVal(*I); + SVal V = state->getSVal(*I, LCtx); DefinedSVal *DV = dyn_cast<DefinedSVal>(&V); // If the value is unknown or undefined, we can't perform this check. @@ -85,7 +86,7 @@ void AttrNonNullChecker::checkPreStmt(const CallExpr *CE, } ConstraintManager &CM = C.getConstraintManager(); - const ProgramState *stateNotNull, *stateNull; + ProgramStateRef stateNotNull, stateNull; llvm::tie(stateNotNull, stateNull) = CM.assumeDual(state, *DV); if (stateNull && !stateNotNull) { @@ -108,7 +109,7 @@ void AttrNonNullChecker::checkPreStmt(const CallExpr *CE, const Expr *arg = *I; R->addRange(arg->getSourceRange()); R->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(errorNode, - arg)); + arg, R)); // Emit the bug report. C.EmitReport(R); } diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp index 08cff0f..6dd0a8c 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp @@ -28,6 +28,7 @@ #include "clang/AST/Expr.h" #include "clang/AST/ExprObjC.h" #include "clang/AST/ASTContext.h" +#include "llvm/ADT/SmallString.h" using namespace clang; using namespace ento; @@ -70,7 +71,7 @@ static inline bool isNil(SVal X) { namespace { class NilArgChecker : public Checker<check::PreObjCMessage> { - mutable llvm::OwningPtr<APIMisuse> BT; + mutable OwningPtr<APIMisuse> BT; void WarnNilArg(CheckerContext &C, const ObjCMessage &msg, unsigned Arg) const; @@ -88,7 +89,7 @@ void NilArgChecker::WarnNilArg(CheckerContext &C, BT.reset(new APIMisuse("nil argument")); if (ExplodedNode *N = C.generateSink()) { - llvm::SmallString<128> sbuf; + SmallString<128> sbuf; llvm::raw_svector_ostream os(sbuf); os << "Argument to '" << GetReceiverNameType(msg) << "' method '" << msg.getSelector().getAsString() << "' cannot be nil"; @@ -129,7 +130,7 @@ void NilArgChecker::checkPreObjCMessage(ObjCMessage msg, Name == "compare:options:range:locale:" || Name == "componentsSeparatedByCharactersInSet:" || Name == "initWithFormat:") { - if (isNil(msg.getArgSVal(0, C.getState()))) + if (isNil(msg.getArgSVal(0, C.getLocationContext(), C.getState()))) WarnNilArg(C, msg, 0); } } @@ -141,7 +142,7 @@ void NilArgChecker::checkPreObjCMessage(ObjCMessage msg, namespace { class CFNumberCreateChecker : public Checker< check::PreStmt<CallExpr> > { - mutable llvm::OwningPtr<APIMisuse> BT; + mutable OwningPtr<APIMisuse> BT; mutable IdentifierInfo* II; public: CFNumberCreateChecker() : II(0) {} @@ -249,11 +250,8 @@ static const char* GetCFNumberTypeStr(uint64_t i) { void CFNumberCreateChecker::checkPreStmt(const CallExpr *CE, CheckerContext &C) const { - const Expr *Callee = CE->getCallee(); - const ProgramState *state = C.getState(); - SVal CallV = state->getSVal(Callee); - const FunctionDecl *FD = CallV.getAsFunctionDecl(); - + ProgramStateRef state = C.getState(); + const FunctionDecl *FD = C.getCalleeDecl(CE); if (!FD) return; @@ -265,7 +263,8 @@ void CFNumberCreateChecker::checkPreStmt(const CallExpr *CE, return; // Get the value of the "theType" argument. - SVal TheTypeVal = state->getSVal(CE->getArg(1)); + const LocationContext *LCtx = C.getLocationContext(); + SVal TheTypeVal = state->getSVal(CE->getArg(1), LCtx); // FIXME: We really should allow ranges of valid theType values, and // bifurcate the state appropriately. @@ -283,7 +282,7 @@ void CFNumberCreateChecker::checkPreStmt(const CallExpr *CE, // Look at the value of the integer being passed by reference. Essentially // we want to catch cases where the value passed in is not equal to the // size of the type being created. - SVal TheValueExpr = state->getSVal(CE->getArg(2)); + SVal TheValueExpr = state->getSVal(CE->getArg(2), LCtx); // FIXME: Eventually we should handle arbitrary locations. We can do this // by having an enhanced memory model that does low-level typing. @@ -316,8 +315,8 @@ void CFNumberCreateChecker::checkPreStmt(const CallExpr *CE, // the bits initialized to the provided values. // if (ExplodedNode *N = SourceSize < TargetSize ? C.generateSink() - : C.generateNode()) { - llvm::SmallString<128> sbuf; + : C.addTransition()) { + SmallString<128> sbuf; llvm::raw_svector_ostream os(sbuf); os << (SourceSize == 8 ? "An " : "A ") @@ -348,7 +347,7 @@ void CFNumberCreateChecker::checkPreStmt(const CallExpr *CE, namespace { class CFRetainReleaseChecker : public Checker< check::PreStmt<CallExpr> > { - mutable llvm::OwningPtr<APIMisuse> BT; + mutable OwningPtr<APIMisuse> BT; mutable IdentifierInfo *Retain, *Release; public: CFRetainReleaseChecker(): Retain(0), Release(0) {} @@ -363,11 +362,8 @@ void CFRetainReleaseChecker::checkPreStmt(const CallExpr *CE, if (CE->getNumArgs() != 1) return; - // Get the function declaration of the callee. - const ProgramState *state = C.getState(); - SVal X = state->getSVal(CE->getCallee()); - const FunctionDecl *FD = X.getAsFunctionDecl(); - + ProgramStateRef state = C.getState(); + const FunctionDecl *FD = C.getCalleeDecl(CE); if (!FD) return; @@ -388,7 +384,7 @@ void CFRetainReleaseChecker::checkPreStmt(const CallExpr *CE, // Get the argument's value. const Expr *Arg = CE->getArg(0); - SVal ArgVal = state->getSVal(Arg); + SVal ArgVal = state->getSVal(Arg, C.getLocationContext()); DefinedSVal *DefArgVal = dyn_cast<DefinedSVal>(&ArgVal); if (!DefArgVal) return; @@ -401,7 +397,7 @@ void CFRetainReleaseChecker::checkPreStmt(const CallExpr *CE, DefinedOrUnknownSVal ArgIsNull = svalBuilder.evalEQ(state, zero, *DefArgVal); // Are they equal? - const ProgramState *stateTrue, *stateFalse; + ProgramStateRef stateTrue, stateFalse; llvm::tie(stateTrue, stateFalse) = state->assume(ArgIsNull); if (stateTrue && !stateFalse) { @@ -415,7 +411,8 @@ void CFRetainReleaseChecker::checkPreStmt(const CallExpr *CE, BugReport *report = new BugReport(*BT, description, N); report->addRange(Arg->getSourceRange()); - report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, Arg)); + report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, Arg, + report)); C.EmitReport(report); return; } @@ -434,7 +431,7 @@ class ClassReleaseChecker : public Checker<check::PreObjCMessage> { mutable Selector retainS; mutable Selector autoreleaseS; mutable Selector drainS; - mutable llvm::OwningPtr<BugType> BT; + mutable OwningPtr<BugType> BT; public: void checkPreObjCMessage(ObjCMessage msg, CheckerContext &C) const; @@ -464,8 +461,8 @@ void ClassReleaseChecker::checkPreObjCMessage(ObjCMessage msg, if (!(S == releaseS || S == retainS || S == autoreleaseS || S == drainS)) return; - if (ExplodedNode *N = C.generateNode()) { - llvm::SmallString<200> buf; + if (ExplodedNode *N = C.addTransition()) { + SmallString<200> buf; llvm::raw_svector_ostream os(buf); os << "The '" << S.getAsString() << "' message should be sent to instances " @@ -488,9 +485,10 @@ class VariadicMethodTypeChecker : public Checker<check::PreObjCMessage> { mutable Selector arrayWithObjectsS; mutable Selector dictionaryWithObjectsAndKeysS; mutable Selector setWithObjectsS; + mutable Selector orderedSetWithObjectsS; mutable Selector initWithObjectsS; mutable Selector initWithObjectsAndKeysS; - mutable llvm::OwningPtr<BugType> BT; + mutable OwningPtr<BugType> BT; bool isVariadicMessage(const ObjCMessage &msg) const; @@ -533,6 +531,11 @@ VariadicMethodTypeChecker::isVariadicMessage(const ObjCMessage &msg) const { if (isReceiverClassOrSuperclass(Class, "NSSet") && S == initWithObjectsS) return true; + + // -[NSOrderedSet initWithObjects:] + if (isReceiverClassOrSuperclass(Class, "NSOrderedSet") && + S == initWithObjectsS) + return true; } else { const ObjCInterfaceDecl *Class = msg.getReceiverInterface(); @@ -550,6 +553,11 @@ VariadicMethodTypeChecker::isVariadicMessage(const ObjCMessage &msg) const { if (isReceiverClassOrSuperclass(Class, "NSSet") && S == setWithObjectsS) return true; + + // -[NSOrderedSet orderedSetWithObjects:] + if (isReceiverClassOrSuperclass(Class, "NSOrderedSet") && + S == orderedSetWithObjectsS) + return true; } return false; @@ -566,6 +574,7 @@ void VariadicMethodTypeChecker::checkPreObjCMessage(ObjCMessage msg, dictionaryWithObjectsAndKeysS = GetUnarySelector("dictionaryWithObjectsAndKeys", Ctx); setWithObjectsS = GetUnarySelector("setWithObjects", Ctx); + orderedSetWithObjectsS = GetUnarySelector("orderedSetWithObjects", Ctx); initWithObjectsS = GetUnarySelector("initWithObjects", Ctx); initWithObjectsAndKeysS = GetUnarySelector("initWithObjectsAndKeys", Ctx); @@ -587,7 +596,7 @@ void VariadicMethodTypeChecker::checkPreObjCMessage(ObjCMessage msg, // Verify that all arguments have Objective-C types. llvm::Optional<ExplodedNode*> errorNode; - const ProgramState *state = C.getState(); + ProgramStateRef state = C.getState(); for (unsigned I = variadicArgsBegin; I != variadicArgsEnd; ++I) { QualType ArgTy = msg.getArgType(I); @@ -599,7 +608,8 @@ void VariadicMethodTypeChecker::checkPreObjCMessage(ObjCMessage msg, continue; // Ignore pointer constants. - if (isa<loc::ConcreteInt>(msg.getArgSVal(I, state))) + if (isa<loc::ConcreteInt>(msg.getArgSVal(I, C.getLocationContext(), + state))) continue; // Ignore pointer types annotated with 'NSObject' attribute. @@ -612,13 +622,13 @@ void VariadicMethodTypeChecker::checkPreObjCMessage(ObjCMessage msg, // Generate only one error node to use for all bug reports. if (!errorNode.hasValue()) { - errorNode = C.generateNode(); + errorNode = C.addTransition(); } if (!errorNode.getValue()) continue; - llvm::SmallString<128> sbuf; + SmallString<128> sbuf; llvm::raw_svector_ostream os(sbuf); if (const char *TypeName = GetReceiverNameType(msg)) diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/BoolAssignmentChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/BoolAssignmentChecker.cpp new file mode 100644 index 0000000..a4fc396 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/BoolAssignmentChecker.cpp @@ -0,0 +1,157 @@ +//== BoolAssignmentChecker.cpp - Boolean assignment checker -----*- C++ -*--==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This defines BoolAssignmentChecker, a builtin check in ExprEngine that +// performs checks for assignment of non-Boolean values to Boolean variables. +// +//===----------------------------------------------------------------------===// + +#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/BugType.h" + +using namespace clang; +using namespace ento; + +namespace { + class BoolAssignmentChecker : public Checker< check::Bind > { + mutable llvm::OwningPtr<BuiltinBug> BT; + void emitReport(ProgramStateRef state, CheckerContext &C) const; + public: + void checkBind(SVal loc, SVal val, const Stmt *S, CheckerContext &C) const; + }; +} // end anonymous namespace + +void BoolAssignmentChecker::emitReport(ProgramStateRef state, + CheckerContext &C) const { + if (ExplodedNode *N = C.addTransition(state)) { + if (!BT) + BT.reset(new BuiltinBug("Assignment of a non-Boolean value")); + C.EmitReport(new BugReport(*BT, BT->getDescription(), N)); + } +} + +static bool isBooleanType(QualType Ty) { + if (Ty->isBooleanType()) // C++ or C99 + return true; + + if (const TypedefType *TT = Ty->getAs<TypedefType>()) + return TT->getDecl()->getName() == "BOOL" || // Objective-C + TT->getDecl()->getName() == "_Bool" || // stdbool.h < C99 + TT->getDecl()->getName() == "Boolean"; // MacTypes.h + + return false; +} + +void BoolAssignmentChecker::checkBind(SVal loc, SVal val, const Stmt *S, + CheckerContext &C) const { + + // We are only interested in stores into Booleans. + const TypedValueRegion *TR = + dyn_cast_or_null<TypedValueRegion>(loc.getAsRegion()); + + if (!TR) + return; + + QualType valTy = TR->getValueType(); + + if (!isBooleanType(valTy)) + return; + + // Get the value of the right-hand side. We only care about values + // that are defined (UnknownVals and UndefinedVals are handled by other + // checkers). + const DefinedSVal *DV = dyn_cast<DefinedSVal>(&val); + if (!DV) + return; + + // Check if the assigned value meets our criteria for correctness. It must + // be a value that is either 0 or 1. One way to check this is to see if + // the value is possibly < 0 (for a negative value) or greater than 1. + ProgramStateRef state = C.getState(); + SValBuilder &svalBuilder = C.getSValBuilder(); + ConstraintManager &CM = C.getConstraintManager(); + + // First, ensure that the value is >= 0. + DefinedSVal zeroVal = svalBuilder.makeIntVal(0, valTy); + SVal greaterThanOrEqualToZeroVal = + svalBuilder.evalBinOp(state, BO_GE, *DV, zeroVal, + svalBuilder.getConditionType()); + + DefinedSVal *greaterThanEqualToZero = + dyn_cast<DefinedSVal>(&greaterThanOrEqualToZeroVal); + + if (!greaterThanEqualToZero) { + // The SValBuilder cannot construct a valid SVal for this condition. + // This means we cannot properly reason about it. + return; + } + + ProgramStateRef stateLT, stateGE; + llvm::tie(stateGE, stateLT) = CM.assumeDual(state, *greaterThanEqualToZero); + + // Is it possible for the value to be less than zero? + if (stateLT) { + // It is possible for the value to be less than zero. We only + // want to emit a warning, however, if that value is fully constrained. + // If it it possible for the value to be >= 0, then essentially the + // value is underconstrained and there is nothing left to be done. + if (!stateGE) + emitReport(stateLT, C); + + // In either case, we are done. + return; + } + + // If we reach here, it must be the case that the value is constrained + // to only be >= 0. + assert(stateGE == state); + + // At this point we know that the value is >= 0. + // Now check to ensure that the value is <= 1. + DefinedSVal OneVal = svalBuilder.makeIntVal(1, valTy); + SVal lessThanEqToOneVal = + svalBuilder.evalBinOp(state, BO_LE, *DV, OneVal, + svalBuilder.getConditionType()); + + DefinedSVal *lessThanEqToOne = + dyn_cast<DefinedSVal>(&lessThanEqToOneVal); + + if (!lessThanEqToOne) { + // The SValBuilder cannot construct a valid SVal for this condition. + // This means we cannot properly reason about it. + return; + } + + ProgramStateRef stateGT, stateLE; + llvm::tie(stateLE, stateGT) = CM.assumeDual(state, *lessThanEqToOne); + + // Is it possible for the value to be greater than one? + if (stateGT) { + // It is possible for the value to be greater than one. We only + // want to emit a warning, however, if that value is fully constrained. + // If it is possible for the value to be <= 1, then essentially the + // value is underconstrained and there is nothing left to be done. + if (!stateLE) + emitReport(stateGT, C); + + // In either case, we are done. + return; + } + + // If we reach here, it must be the case that the value is constrained + // to only be <= 1. + assert(stateLE == state); +} + +void ento::registerBoolAssignmentChecker(CheckerManager &mgr) { + mgr.registerChecker<BoolAssignmentChecker>(); +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/BuiltinFunctionChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/BuiltinFunctionChecker.cpp index a57d031..509bc79 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/BuiltinFunctionChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/BuiltinFunctionChecker.cpp @@ -30,12 +30,10 @@ public: } bool BuiltinFunctionChecker::evalCall(const CallExpr *CE, - CheckerContext &C) const{ - const ProgramState *state = C.getState(); - const Expr *Callee = CE->getCallee(); - SVal L = state->getSVal(Callee); - const FunctionDecl *FD = L.getAsFunctionDecl(); - + CheckerContext &C) const { + ProgramStateRef state = C.getState(); + const FunctionDecl *FD = C.getCalleeDecl(CE); + const LocationContext *LCtx = C.getLocationContext(); if (!FD) return false; @@ -48,8 +46,8 @@ bool BuiltinFunctionChecker::evalCall(const CallExpr *CE, case Builtin::BI__builtin_expect: { // For __builtin_expect, just return the value of the subexpression. assert (CE->arg_begin() != CE->arg_end()); - SVal X = state->getSVal(*(CE->arg_begin())); - C.generateNode(state->BindExpr(CE, X)); + SVal X = state->getSVal(*(CE->arg_begin()), LCtx); + C.addTransition(state->BindExpr(CE, LCtx, X)); return true; } @@ -57,14 +55,13 @@ bool BuiltinFunctionChecker::evalCall(const CallExpr *CE, // FIXME: Refactor into StoreManager itself? MemRegionManager& RM = C.getStoreManager().getRegionManager(); const AllocaRegion* R = - RM.getAllocaRegion(CE, C.getCurrentBlockCount(), - C.getPredecessor()->getLocationContext()); + RM.getAllocaRegion(CE, C.getCurrentBlockCount(), C.getLocationContext()); // Set the extent of the region in bytes. This enables us to use the // SVal of the argument directly. If we save the extent in bits, we // cannot represent values like symbol*8. DefinedOrUnknownSVal Size = - cast<DefinedOrUnknownSVal>(state->getSVal(*(CE->arg_begin()))); + cast<DefinedOrUnknownSVal>(state->getSVal(*(CE->arg_begin()), LCtx)); SValBuilder& svalBuilder = C.getSValBuilder(); DefinedOrUnknownSVal Extent = R->getExtent(svalBuilder); @@ -72,7 +69,7 @@ bool BuiltinFunctionChecker::evalCall(const CallExpr *CE, svalBuilder.evalEQ(state, Extent, Size); state = state->assume(extentMatchesSizeArg, true); - C.generateNode(state->BindExpr(CE, loc::MemRegionVal(R))); + C.addTransition(state->BindExpr(CE, LCtx, loc::MemRegionVal(R))); return true; } } diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp index 1625219..9eb7edf 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp @@ -13,11 +13,14 @@ //===----------------------------------------------------------------------===// #include "ClangSACheckers.h" +#include "InterCheckerAPI.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/BugType.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringSwitch.h" using namespace clang; @@ -30,25 +33,40 @@ class CStringChecker : public Checker< eval::Call, check::DeadSymbols, check::RegionChanges > { - mutable llvm::OwningPtr<BugType> BT_Null, BT_Bounds, - BT_Overlap, BT_NotCString, - BT_AdditionOverflow; + mutable OwningPtr<BugType> BT_Null, + BT_Bounds, + BT_Overlap, + BT_NotCString, + BT_AdditionOverflow; + mutable const char *CurrentFunctionDescription; public: + /// The filter is used to filter out the diagnostics which are not enabled by + /// the user. + struct CStringChecksFilter { + DefaultBool CheckCStringNullArg; + DefaultBool CheckCStringOutOfBounds; + DefaultBool CheckCStringBufferOverlap; + DefaultBool CheckCStringNotNullTerm; + }; + + CStringChecksFilter Filter; + static void *getTag() { static int tag; return &tag; } bool evalCall(const CallExpr *CE, CheckerContext &C) const; void checkPreStmt(const DeclStmt *DS, CheckerContext &C) const; - void checkLiveSymbols(const ProgramState *state, SymbolReaper &SR) const; + void checkLiveSymbols(ProgramStateRef state, SymbolReaper &SR) const; void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const; - bool wantsRegionChangeUpdate(const ProgramState *state) const; + bool wantsRegionChangeUpdate(ProgramStateRef state) const; - const ProgramState * - checkRegionChanges(const ProgramState *state, + ProgramStateRef + checkRegionChanges(ProgramStateRef state, const StoreManager::InvalidatedSymbols *, ArrayRef<const MemRegion *> ExplicitRegions, - ArrayRef<const MemRegion *> Regions) const; + ArrayRef<const MemRegion *> Regions, + const CallOrObjCMessage *Call) const; typedef void (CStringChecker::*FnCheck)(CheckerContext &, const CallExpr *) const; @@ -58,7 +76,7 @@ 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 ProgramState *state, + ProgramStateRef state, const Expr *Size, const Expr *Source, const Expr *Dest, @@ -95,48 +113,48 @@ public: bool ignoreCase = false) const; // Utility methods - std::pair<const ProgramState*, const ProgramState*> + std::pair<ProgramStateRef , ProgramStateRef > static assumeZero(CheckerContext &C, - const ProgramState *state, SVal V, QualType Ty); + ProgramStateRef state, SVal V, QualType Ty); - static const ProgramState *setCStringLength(const ProgramState *state, + static ProgramStateRef setCStringLength(ProgramStateRef state, const MemRegion *MR, SVal strLength); static SVal getCStringLengthForRegion(CheckerContext &C, - const ProgramState *&state, + ProgramStateRef &state, const Expr *Ex, const MemRegion *MR, bool hypothetical); SVal getCStringLength(CheckerContext &C, - const ProgramState *&state, + ProgramStateRef &state, const Expr *Ex, SVal Buf, bool hypothetical = false) const; const StringLiteral *getCStringLiteral(CheckerContext &C, - const ProgramState *&state, + ProgramStateRef &state, const Expr *expr, SVal val) const; - static const ProgramState *InvalidateBuffer(CheckerContext &C, - const ProgramState *state, + static ProgramStateRef InvalidateBuffer(CheckerContext &C, + ProgramStateRef state, const Expr *Ex, SVal V); static bool SummarizeRegion(raw_ostream &os, ASTContext &Ctx, const MemRegion *MR); // Re-usable checks - const ProgramState *checkNonNull(CheckerContext &C, - const ProgramState *state, + ProgramStateRef checkNonNull(CheckerContext &C, + ProgramStateRef state, const Expr *S, SVal l) const; - const ProgramState *CheckLocation(CheckerContext &C, - const ProgramState *state, + ProgramStateRef CheckLocation(CheckerContext &C, + ProgramStateRef state, const Expr *S, SVal l, const char *message = NULL) const; - const ProgramState *CheckBufferAccess(CheckerContext &C, - const ProgramState *state, + ProgramStateRef CheckBufferAccess(CheckerContext &C, + ProgramStateRef state, const Expr *Size, const Expr *FirstBuf, const Expr *SecondBuf, @@ -144,8 +162,8 @@ public: const char *secondMessage = NULL, bool WarnAboutSize = false) const; - const ProgramState *CheckBufferAccess(CheckerContext &C, - const ProgramState *state, + ProgramStateRef CheckBufferAccess(CheckerContext &C, + ProgramStateRef state, const Expr *Size, const Expr *Buf, const char *message = NULL, @@ -154,18 +172,18 @@ public: return CheckBufferAccess(C, state, Size, Buf, NULL, message, NULL, WarnAboutSize); } - const ProgramState *CheckOverlap(CheckerContext &C, - const ProgramState *state, + ProgramStateRef CheckOverlap(CheckerContext &C, + ProgramStateRef state, const Expr *Size, const Expr *First, const Expr *Second) const; void emitOverlapBug(CheckerContext &C, - const ProgramState *state, + ProgramStateRef state, const Stmt *First, const Stmt *Second) const; - const ProgramState *checkAdditionOverflow(CheckerContext &C, - const ProgramState *state, + ProgramStateRef checkAdditionOverflow(CheckerContext &C, + ProgramStateRef state, NonLoc left, NonLoc right) const; }; @@ -190,38 +208,41 @@ namespace ento { // Individual checks and utility methods. //===----------------------------------------------------------------------===// -std::pair<const ProgramState*, const ProgramState*> -CStringChecker::assumeZero(CheckerContext &C, const ProgramState *state, SVal V, +std::pair<ProgramStateRef , ProgramStateRef > +CStringChecker::assumeZero(CheckerContext &C, ProgramStateRef state, SVal V, QualType Ty) { DefinedSVal *val = dyn_cast<DefinedSVal>(&V); if (!val) - return std::pair<const ProgramState*, const ProgramState *>(state, state); + return std::pair<ProgramStateRef , ProgramStateRef >(state, state); SValBuilder &svalBuilder = C.getSValBuilder(); DefinedOrUnknownSVal zero = svalBuilder.makeZeroVal(Ty); return state->assume(svalBuilder.evalEQ(state, *val, zero)); } -const ProgramState *CStringChecker::checkNonNull(CheckerContext &C, - const ProgramState *state, +ProgramStateRef CStringChecker::checkNonNull(CheckerContext &C, + ProgramStateRef state, const Expr *S, SVal l) const { // If a previous check has failed, propagate the failure. if (!state) return NULL; - const ProgramState *stateNull, *stateNonNull; + ProgramStateRef stateNull, stateNonNull; llvm::tie(stateNull, stateNonNull) = assumeZero(C, state, l, S->getType()); if (stateNull && !stateNonNull) { + if (!Filter.CheckCStringNullArg) + return NULL; + ExplodedNode *N = C.generateSink(stateNull); if (!N) return NULL; if (!BT_Null) - BT_Null.reset(new BuiltinBug("API", + BT_Null.reset(new BuiltinBug("Unix API", "Null pointer argument in call to byte string function")); - llvm::SmallString<80> buf; + SmallString<80> buf; llvm::raw_svector_ostream os(buf); assert(CurrentFunctionDescription); os << "Null pointer argument in call to " << CurrentFunctionDescription; @@ -231,7 +252,8 @@ const ProgramState *CStringChecker::checkNonNull(CheckerContext &C, BugReport *report = new BugReport(*BT, os.str(), N); report->addRange(S->getSourceRange()); - report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, S)); + report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, S, + report)); C.EmitReport(report); return NULL; } @@ -242,8 +264,8 @@ const ProgramState *CStringChecker::checkNonNull(CheckerContext &C, } // FIXME: This was originally copied from ArrayBoundChecker.cpp. Refactor? -const ProgramState *CStringChecker::CheckLocation(CheckerContext &C, - const ProgramState *state, +ProgramStateRef CStringChecker::CheckLocation(CheckerContext &C, + ProgramStateRef state, const Expr *S, SVal l, const char *warningMsg) const { // If a previous check has failed, propagate the failure. @@ -272,8 +294,8 @@ const ProgramState *CStringChecker::CheckLocation(CheckerContext &C, // Get the index of the accessed element. DefinedOrUnknownSVal Idx = cast<DefinedOrUnknownSVal>(ER->getIndex()); - const ProgramState *StInBound = state->assumeInBound(Idx, Size, true); - const ProgramState *StOutBound = state->assumeInBound(Idx, Size, false); + ProgramStateRef StInBound = state->assumeInBound(Idx, Size, true); + ProgramStateRef StOutBound = state->assumeInBound(Idx, Size, false); if (StOutBound && !StInBound) { ExplodedNode *N = C.generateSink(StOutBound); if (!N) @@ -293,7 +315,7 @@ const ProgramState *CStringChecker::CheckLocation(CheckerContext &C, assert(CurrentFunctionDescription); assert(CurrentFunctionDescription[0] != '\0'); - llvm::SmallString<80> buf; + SmallString<80> buf; llvm::raw_svector_ostream os(buf); os << (char)toupper(CurrentFunctionDescription[0]) << &CurrentFunctionDescription[1] @@ -315,8 +337,8 @@ const ProgramState *CStringChecker::CheckLocation(CheckerContext &C, return StInBound; } -const ProgramState *CStringChecker::CheckBufferAccess(CheckerContext &C, - const ProgramState *state, +ProgramStateRef CStringChecker::CheckBufferAccess(CheckerContext &C, + ProgramStateRef state, const Expr *Size, const Expr *FirstBuf, const Expr *SecondBuf, @@ -329,20 +351,25 @@ const ProgramState *CStringChecker::CheckBufferAccess(CheckerContext &C, SValBuilder &svalBuilder = C.getSValBuilder(); ASTContext &Ctx = svalBuilder.getContext(); + const LocationContext *LCtx = C.getLocationContext(); QualType sizeTy = Size->getType(); QualType PtrTy = Ctx.getPointerType(Ctx.CharTy); // Check that the first buffer is non-null. - SVal BufVal = state->getSVal(FirstBuf); + SVal BufVal = state->getSVal(FirstBuf, LCtx); state = checkNonNull(C, state, FirstBuf, BufVal); if (!state) return NULL; + // If out-of-bounds checking is turned off, skip the rest. + if (!Filter.CheckCStringOutOfBounds) + return state; + // Get the access length and make sure it is known. // FIXME: This assumes the caller has already checked that the access length // is positive. And that it's unsigned. - SVal LengthVal = state->getSVal(Size); + SVal LengthVal = state->getSVal(Size, LCtx); NonLoc *Length = dyn_cast<NonLoc>(&LengthVal); if (!Length) return state; @@ -368,7 +395,7 @@ const ProgramState *CStringChecker::CheckBufferAccess(CheckerContext &C, // If there's a second buffer, check it as well. if (SecondBuf) { - BufVal = state->getSVal(SecondBuf); + BufVal = state->getSVal(SecondBuf, LCtx); state = checkNonNull(C, state, SecondBuf, BufVal); if (!state) return NULL; @@ -387,11 +414,14 @@ const ProgramState *CStringChecker::CheckBufferAccess(CheckerContext &C, return state; } -const ProgramState *CStringChecker::CheckOverlap(CheckerContext &C, - const ProgramState *state, +ProgramStateRef CStringChecker::CheckOverlap(CheckerContext &C, + ProgramStateRef state, const Expr *Size, const Expr *First, const Expr *Second) const { + if (!Filter.CheckCStringBufferOverlap) + return state; + // Do a simple check for overlap: if the two arguments are from the same // buffer, see if the end of the first is greater than the start of the second // or vice versa. @@ -400,11 +430,12 @@ const ProgramState *CStringChecker::CheckOverlap(CheckerContext &C, if (!state) return NULL; - const ProgramState *stateTrue, *stateFalse; + ProgramStateRef stateTrue, stateFalse; // Get the buffer values and make sure they're known locations. - SVal firstVal = state->getSVal(First); - SVal secondVal = state->getSVal(Second); + const LocationContext *LCtx = C.getLocationContext(); + SVal firstVal = state->getSVal(First, LCtx); + SVal secondVal = state->getSVal(Second, LCtx); Loc *firstLoc = dyn_cast<Loc>(&firstVal); if (!firstLoc) @@ -456,7 +487,7 @@ const ProgramState *CStringChecker::CheckOverlap(CheckerContext &C, } // Get the length, and make sure it too is known. - SVal LengthVal = state->getSVal(Size); + SVal LengthVal = state->getSVal(Size, LCtx); NonLoc *Length = dyn_cast<NonLoc>(&LengthVal); if (!Length) return state; @@ -498,7 +529,7 @@ const ProgramState *CStringChecker::CheckOverlap(CheckerContext &C, return stateFalse; } -void CStringChecker::emitOverlapBug(CheckerContext &C, const ProgramState *state, +void CStringChecker::emitOverlapBug(CheckerContext &C, ProgramStateRef state, const Stmt *First, const Stmt *Second) const { ExplodedNode *N = C.generateSink(state); if (!N) @@ -517,10 +548,14 @@ void CStringChecker::emitOverlapBug(CheckerContext &C, const ProgramState *state C.EmitReport(report); } -const ProgramState *CStringChecker::checkAdditionOverflow(CheckerContext &C, - const ProgramState *state, +ProgramStateRef CStringChecker::checkAdditionOverflow(CheckerContext &C, + ProgramStateRef state, NonLoc left, NonLoc right) const { + // If out-of-bounds checking is turned off, skip the rest. + if (!Filter.CheckCStringOutOfBounds) + return state; + // If a previous check has failed, propagate the failure. if (!state) return NULL; @@ -532,10 +567,11 @@ const ProgramState *CStringChecker::checkAdditionOverflow(CheckerContext &C, const llvm::APSInt &maxValInt = BVF.getMaxValue(sizeTy); NonLoc maxVal = svalBuilder.makeIntVal(maxValInt); - SVal maxMinusRight = svalBuilder.evalBinOpNN(state, BO_Sub, maxVal, right, - sizeTy); - - if (maxMinusRight.isUnknownOrUndef()) { + SVal maxMinusRight; + if (isa<nonloc::ConcreteInt>(right)) { + maxMinusRight = svalBuilder.evalBinOpNN(state, BO_Sub, maxVal, right, + sizeTy); + } else { // Try switching the operands. (The order of these two assignments is // important!) maxMinusRight = svalBuilder.evalBinOpNN(state, BO_Sub, maxVal, left, @@ -549,7 +585,7 @@ const ProgramState *CStringChecker::checkAdditionOverflow(CheckerContext &C, SVal willOverflow = svalBuilder.evalBinOpNN(state, BO_GT, left, *maxMinusRightNL, cmpTy); - const ProgramState *stateOverflow, *stateOkay; + ProgramStateRef stateOverflow, stateOkay; llvm::tie(stateOverflow, stateOkay) = state->assume(cast<DefinedOrUnknownSVal>(willOverflow)); @@ -585,7 +621,7 @@ const ProgramState *CStringChecker::checkAdditionOverflow(CheckerContext &C, return state; } -const ProgramState *CStringChecker::setCStringLength(const ProgramState *state, +ProgramStateRef CStringChecker::setCStringLength(ProgramStateRef state, const MemRegion *MR, SVal strLength) { assert(!strLength.isUndef() && "Attempt to set an undefined string length"); @@ -626,7 +662,7 @@ const ProgramState *CStringChecker::setCStringLength(const ProgramState *state, } SVal CStringChecker::getCStringLengthForRegion(CheckerContext &C, - const ProgramState *&state, + ProgramStateRef &state, const Expr *Ex, const MemRegion *MR, bool hypothetical) { @@ -650,7 +686,7 @@ SVal CStringChecker::getCStringLengthForRegion(CheckerContext &C, return strLength; } -SVal CStringChecker::getCStringLength(CheckerContext &C, const ProgramState *&state, +SVal CStringChecker::getCStringLength(CheckerContext &C, ProgramStateRef &state, const Expr *Ex, SVal Buf, bool hypothetical) const { const MemRegion *MR = Buf.getAsRegion(); @@ -659,12 +695,15 @@ SVal CStringChecker::getCStringLength(CheckerContext &C, const ProgramState *&st // C string. In the context of locations, the only time we can issue such // a warning is for labels. if (loc::GotoLabel *Label = dyn_cast<loc::GotoLabel>(&Buf)) { - if (ExplodedNode *N = C.generateNode(state)) { + if (!Filter.CheckCStringNotNullTerm) + return UndefinedVal(); + + if (ExplodedNode *N = C.addTransition(state)) { if (!BT_NotCString) - BT_NotCString.reset(new BuiltinBug("API", + BT_NotCString.reset(new BuiltinBug("Unix API", "Argument is not a null-terminated string.")); - llvm::SmallString<120> buf; + SmallString<120> buf; llvm::raw_svector_ostream os(buf); assert(CurrentFunctionDescription); os << "Argument to " << CurrentFunctionDescription @@ -678,8 +717,8 @@ SVal CStringChecker::getCStringLength(CheckerContext &C, const ProgramState *&st report->addRange(Ex->getSourceRange()); C.EmitReport(report); } - return UndefinedVal(); + } // If it's not a region and not a label, give up. @@ -716,12 +755,15 @@ SVal CStringChecker::getCStringLength(CheckerContext &C, const ProgramState *&st // Other regions (mostly non-data) can't have a reliable C string length. // In this case, an error is emitted and UndefinedVal is returned. // The caller should always be prepared to handle this case. - if (ExplodedNode *N = C.generateNode(state)) { + if (!Filter.CheckCStringNotNullTerm) + return UndefinedVal(); + + if (ExplodedNode *N = C.addTransition(state)) { if (!BT_NotCString) - BT_NotCString.reset(new BuiltinBug("API", + BT_NotCString.reset(new BuiltinBug("Unix API", "Argument is not a null-terminated string.")); - llvm::SmallString<120> buf; + SmallString<120> buf; llvm::raw_svector_ostream os(buf); assert(CurrentFunctionDescription); @@ -745,7 +787,7 @@ SVal CStringChecker::getCStringLength(CheckerContext &C, const ProgramState *&st } const StringLiteral *CStringChecker::getCStringLiteral(CheckerContext &C, - const ProgramState *&state, const Expr *expr, SVal val) const { + ProgramStateRef &state, const Expr *expr, SVal val) const { // Get the memory region pointed to by the val. const MemRegion *bufRegion = val.getAsRegion(); @@ -764,8 +806,8 @@ const StringLiteral *CStringChecker::getCStringLiteral(CheckerContext &C, return strRegion->getStringLiteral(); } -const ProgramState *CStringChecker::InvalidateBuffer(CheckerContext &C, - const ProgramState *state, +ProgramStateRef CStringChecker::InvalidateBuffer(CheckerContext &C, + ProgramStateRef state, const Expr *E, SVal V) { Loc *L = dyn_cast<Loc>(&V); if (!L) @@ -786,7 +828,8 @@ const ProgramState *CStringChecker::InvalidateBuffer(CheckerContext &C, // Invalidate this region. unsigned Count = C.getCurrentBlockCount(); - return state->invalidateRegions(R, E, Count); + const LocationContext *LCtx = C.getPredecessor()->getLocationContext(); + return state->invalidateRegions(R, E, Count, LCtx); } // If we have a non-region value by chance, just remove the binding. @@ -838,27 +881,28 @@ bool CStringChecker::SummarizeRegion(raw_ostream &os, ASTContext &Ctx, void CStringChecker::evalCopyCommon(CheckerContext &C, const CallExpr *CE, - const ProgramState *state, + ProgramStateRef state, const Expr *Size, const Expr *Dest, const Expr *Source, bool Restricted, bool IsMempcpy) const { CurrentFunctionDescription = "memory copy function"; // See if the size argument is zero. - SVal sizeVal = state->getSVal(Size); + const LocationContext *LCtx = C.getLocationContext(); + SVal sizeVal = state->getSVal(Size, LCtx); QualType sizeTy = Size->getType(); - const ProgramState *stateZeroSize, *stateNonZeroSize; + ProgramStateRef stateZeroSize, stateNonZeroSize; llvm::tie(stateZeroSize, stateNonZeroSize) = assumeZero(C, state, sizeVal, sizeTy); // Get the value of the Dest. - SVal destVal = state->getSVal(Dest); + SVal destVal = state->getSVal(Dest, LCtx); // If the size is zero, there won't be any actual memory access, so // just bind the return value to the destination buffer and return. if (stateZeroSize) { - stateZeroSize = stateZeroSize->BindExpr(CE, destVal); + stateZeroSize = stateZeroSize->BindExpr(CE, LCtx, destVal); C.addTransition(stateZeroSize); } @@ -873,7 +917,7 @@ void CStringChecker::evalCopyCommon(CheckerContext &C, return; // Get the value of the Src. - SVal srcVal = state->getSVal(Source); + SVal srcVal = state->getSVal(Source, LCtx); // Ensure the source is not null. If it is NULL there will be a // NULL pointer dereference. @@ -909,20 +953,20 @@ void CStringChecker::evalCopyCommon(CheckerContext &C, Dest->getType()); // The byte after the last byte copied is the return value. - state = state->BindExpr(CE, lastElement); + state = state->BindExpr(CE, LCtx, lastElement); } else { // If we don't know how much we copied, we can at least // conjure a return value for later. unsigned Count = C.getCurrentBlockCount(); SVal result = - C.getSValBuilder().getConjuredSymbolVal(NULL, CE, Count); - state = state->BindExpr(CE, result); + C.getSValBuilder().getConjuredSymbolVal(NULL, CE, LCtx, Count); + state = state->BindExpr(CE, LCtx, result); } } else { // All other copies return the destination buffer. // (Well, bcopy() has a void return type, but this won't hurt.) - state = state->BindExpr(CE, destVal); + state = state->BindExpr(CE, LCtx, destVal); } // Invalidate the destination. @@ -930,46 +974,62 @@ void CStringChecker::evalCopyCommon(CheckerContext &C, // can use LazyCompoundVals to copy the source values into the destination. // This would probably remove any existing bindings past the end of the // copied region, but that's still an improvement over blank invalidation. - state = InvalidateBuffer(C, state, Dest, state->getSVal(Dest)); + state = InvalidateBuffer(C, state, Dest, + state->getSVal(Dest, C.getLocationContext())); C.addTransition(state); } } void CStringChecker::evalMemcpy(CheckerContext &C, const CallExpr *CE) const { + if (CE->getNumArgs() < 3) + return; + // 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 ProgramState *state = C.getState(); + ProgramStateRef state = C.getState(); evalCopyCommon(C, CE, state, CE->getArg(2), Dest, CE->getArg(1), true); } void CStringChecker::evalMempcpy(CheckerContext &C, const CallExpr *CE) const { + if (CE->getNumArgs() < 3) + return; + // 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 ProgramState *state = C.getState(); + ProgramStateRef state = C.getState(); evalCopyCommon(C, CE, state, CE->getArg(2), Dest, CE->getArg(1), true, true); } void CStringChecker::evalMemmove(CheckerContext &C, const CallExpr *CE) const { + if (CE->getNumArgs() < 3) + return; + // 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 ProgramState *state = C.getState(); + ProgramStateRef state = C.getState(); evalCopyCommon(C, CE, state, CE->getArg(2), Dest, CE->getArg(1)); } void CStringChecker::evalBcopy(CheckerContext &C, const CallExpr *CE) const { + if (CE->getNumArgs() < 3) + return; + // void bcopy(const void *src, void *dst, size_t n); evalCopyCommon(C, CE, C.getState(), CE->getArg(2), CE->getArg(1), CE->getArg(0)); } void CStringChecker::evalMemcmp(CheckerContext &C, const CallExpr *CE) const { + if (CE->getNumArgs() < 3) + return; + // int memcmp(const void *s1, const void *s2, size_t n); CurrentFunctionDescription = "memory comparison function"; @@ -977,14 +1037,15 @@ void CStringChecker::evalMemcmp(CheckerContext &C, const CallExpr *CE) const { const Expr *Right = CE->getArg(1); const Expr *Size = CE->getArg(2); - const ProgramState *state = C.getState(); + ProgramStateRef state = C.getState(); SValBuilder &svalBuilder = C.getSValBuilder(); // See if the size argument is zero. - SVal sizeVal = state->getSVal(Size); + const LocationContext *LCtx = C.getLocationContext(); + SVal sizeVal = state->getSVal(Size, LCtx); QualType sizeTy = Size->getType(); - const ProgramState *stateZeroSize, *stateNonZeroSize; + ProgramStateRef stateZeroSize, stateNonZeroSize; llvm::tie(stateZeroSize, stateNonZeroSize) = assumeZero(C, state, sizeVal, sizeTy); @@ -992,7 +1053,8 @@ void CStringChecker::evalMemcmp(CheckerContext &C, const CallExpr *CE) const { // have to check either of the buffers. if (stateZeroSize) { state = stateZeroSize; - state = state->BindExpr(CE, svalBuilder.makeZeroVal(CE->getType())); + state = state->BindExpr(CE, LCtx, + svalBuilder.makeZeroVal(CE->getType())); C.addTransition(state); } @@ -1002,12 +1064,14 @@ void CStringChecker::evalMemcmp(CheckerContext &C, const CallExpr *CE) const { // If we know the two buffers are the same, we know the result is 0. // First, get the two buffers' addresses. Another checker will have already // made sure they're not undefined. - DefinedOrUnknownSVal LV = cast<DefinedOrUnknownSVal>(state->getSVal(Left)); - DefinedOrUnknownSVal RV = cast<DefinedOrUnknownSVal>(state->getSVal(Right)); + DefinedOrUnknownSVal LV = + cast<DefinedOrUnknownSVal>(state->getSVal(Left, LCtx)); + DefinedOrUnknownSVal RV = + cast<DefinedOrUnknownSVal>(state->getSVal(Right, LCtx)); // See if they are the same. DefinedOrUnknownSVal SameBuf = svalBuilder.evalEQ(state, LV, RV); - const ProgramState *StSameBuf, *StNotSameBuf; + ProgramStateRef StSameBuf, StNotSameBuf; llvm::tie(StSameBuf, StNotSameBuf) = state->assume(SameBuf); // If the two arguments might be the same buffer, we know the result is 0, @@ -1016,8 +1080,9 @@ void CStringChecker::evalMemcmp(CheckerContext &C, const CallExpr *CE) const { state = StSameBuf; state = CheckBufferAccess(C, state, Size, Left); if (state) { - state = StSameBuf->BindExpr(CE, svalBuilder.makeZeroVal(CE->getType())); - C.addTransition(state); + state = StSameBuf->BindExpr(CE, LCtx, + svalBuilder.makeZeroVal(CE->getType())); + C.addTransition(state); } } @@ -1029,8 +1094,8 @@ void CStringChecker::evalMemcmp(CheckerContext &C, const CallExpr *CE) const { if (state) { // The return value is the comparison result, which we don't know. unsigned Count = C.getCurrentBlockCount(); - SVal CmpV = svalBuilder.getConjuredSymbolVal(NULL, CE, Count); - state = state->BindExpr(CE, CmpV); + SVal CmpV = svalBuilder.getConjuredSymbolVal(NULL, CE, LCtx, Count); + state = state->BindExpr(CE, LCtx, CmpV); C.addTransition(state); } } @@ -1039,12 +1104,18 @@ void CStringChecker::evalMemcmp(CheckerContext &C, const CallExpr *CE) const { void CStringChecker::evalstrLength(CheckerContext &C, const CallExpr *CE) const { + if (CE->getNumArgs() < 1) + return; + // size_t strlen(const char *s); evalstrLengthCommon(C, CE, /* IsStrnlen = */ false); } void CStringChecker::evalstrnLength(CheckerContext &C, const CallExpr *CE) const { + if (CE->getNumArgs() < 2) + return; + // size_t strnlen(const char *s, size_t maxlen); evalstrLengthCommon(C, CE, /* IsStrnlen = */ true); } @@ -1052,13 +1123,14 @@ void CStringChecker::evalstrnLength(CheckerContext &C, void CStringChecker::evalstrLengthCommon(CheckerContext &C, const CallExpr *CE, bool IsStrnlen) const { CurrentFunctionDescription = "string length function"; - const ProgramState *state = C.getState(); + ProgramStateRef state = C.getState(); + const LocationContext *LCtx = C.getLocationContext(); if (IsStrnlen) { const Expr *maxlenExpr = CE->getArg(1); - SVal maxlenVal = state->getSVal(maxlenExpr); + SVal maxlenVal = state->getSVal(maxlenExpr, LCtx); - const ProgramState *stateZeroSize, *stateNonZeroSize; + ProgramStateRef stateZeroSize, stateNonZeroSize; llvm::tie(stateZeroSize, stateNonZeroSize) = assumeZero(C, state, maxlenVal, maxlenExpr->getType()); @@ -1066,7 +1138,7 @@ void CStringChecker::evalstrLengthCommon(CheckerContext &C, const CallExpr *CE, // have to check the string itself. if (stateZeroSize) { SVal zero = C.getSValBuilder().makeZeroVal(CE->getType()); - stateZeroSize = stateZeroSize->BindExpr(CE, zero); + stateZeroSize = stateZeroSize->BindExpr(CE, LCtx, zero); C.addTransition(stateZeroSize); } @@ -1080,7 +1152,7 @@ void CStringChecker::evalstrLengthCommon(CheckerContext &C, const CallExpr *CE, // Check that the string argument is non-null. const Expr *Arg = CE->getArg(0); - SVal ArgVal = state->getSVal(Arg); + SVal ArgVal = state->getSVal(Arg, LCtx); state = checkNonNull(C, state, Arg, ArgVal); @@ -1104,13 +1176,13 @@ void CStringChecker::evalstrLengthCommon(CheckerContext &C, const CallExpr *CE, // It's a little unfortunate to be getting this again, // but it's not that expensive... const Expr *maxlenExpr = CE->getArg(1); - SVal maxlenVal = state->getSVal(maxlenExpr); + SVal maxlenVal = state->getSVal(maxlenExpr, LCtx); NonLoc *strLengthNL = dyn_cast<NonLoc>(&strLength); NonLoc *maxlenValNL = dyn_cast<NonLoc>(&maxlenVal); if (strLengthNL && maxlenValNL) { - const ProgramState *stateStringTooLong, *stateStringNotTooLong; + ProgramStateRef stateStringTooLong, stateStringNotTooLong; // Check if the strLength is greater than the maxlen. llvm::tie(stateStringTooLong, stateStringNotTooLong) = @@ -1135,7 +1207,7 @@ void CStringChecker::evalstrLengthCommon(CheckerContext &C, const CallExpr *CE, // 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.getCurrentBlockCount(); - result = C.getSValBuilder().getConjuredSymbolVal(NULL, CE, Count); + result = C.getSValBuilder().getConjuredSymbolVal(NULL, CE, LCtx, Count); NonLoc *resultNL = cast<NonLoc>(&result); if (strLengthNL) { @@ -1163,17 +1235,20 @@ void CStringChecker::evalstrLengthCommon(CheckerContext &C, const CallExpr *CE, // value, so it can be used in constraints, at least. if (result.isUnknown()) { unsigned Count = C.getCurrentBlockCount(); - result = C.getSValBuilder().getConjuredSymbolVal(NULL, CE, Count); + result = C.getSValBuilder().getConjuredSymbolVal(NULL, CE, LCtx, Count); } } // Bind the return value. assert(!result.isUnknown() && "Should have conjured a value by now"); - state = state->BindExpr(CE, result); + state = state->BindExpr(CE, LCtx, result); C.addTransition(state); } void CStringChecker::evalStrcpy(CheckerContext &C, const CallExpr *CE) const { + if (CE->getNumArgs() < 2) + return; + // char *strcpy(char *restrict dst, const char *restrict src); evalStrcpyCommon(C, CE, /* returnEnd = */ false, @@ -1182,6 +1257,9 @@ void CStringChecker::evalStrcpy(CheckerContext &C, const CallExpr *CE) const { } void CStringChecker::evalStrncpy(CheckerContext &C, const CallExpr *CE) const { + if (CE->getNumArgs() < 3) + return; + // char *strncpy(char *restrict dst, const char *restrict src, size_t n); evalStrcpyCommon(C, CE, /* returnEnd = */ false, @@ -1190,6 +1268,9 @@ void CStringChecker::evalStrncpy(CheckerContext &C, const CallExpr *CE) const { } void CStringChecker::evalStpcpy(CheckerContext &C, const CallExpr *CE) const { + if (CE->getNumArgs() < 2) + return; + // char *stpcpy(char *restrict dst, const char *restrict src); evalStrcpyCommon(C, CE, /* returnEnd = */ true, @@ -1198,6 +1279,9 @@ void CStringChecker::evalStpcpy(CheckerContext &C, const CallExpr *CE) const { } void CStringChecker::evalStrcat(CheckerContext &C, const CallExpr *CE) const { + if (CE->getNumArgs() < 2) + return; + //char *strcat(char *restrict s1, const char *restrict s2); evalStrcpyCommon(C, CE, /* returnEnd = */ false, @@ -1206,6 +1290,9 @@ void CStringChecker::evalStrcat(CheckerContext &C, const CallExpr *CE) const { } void CStringChecker::evalStrncat(CheckerContext &C, const CallExpr *CE) const { + if (CE->getNumArgs() < 3) + return; + //char *strncat(char *restrict s1, const char *restrict s2, size_t n); evalStrcpyCommon(C, CE, /* returnEnd = */ false, @@ -1217,11 +1304,12 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE, bool returnEnd, bool isBounded, bool isAppending) const { CurrentFunctionDescription = "string copy function"; - const ProgramState *state = C.getState(); + ProgramStateRef state = C.getState(); + const LocationContext *LCtx = C.getLocationContext(); // Check that the destination is non-null. const Expr *Dst = CE->getArg(0); - SVal DstVal = state->getSVal(Dst); + SVal DstVal = state->getSVal(Dst, LCtx); state = checkNonNull(C, state, Dst, DstVal); if (!state) @@ -1229,7 +1317,7 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE, // Check that the source is non-null. const Expr *srcExpr = CE->getArg(1); - SVal srcVal = state->getSVal(srcExpr); + SVal srcVal = state->getSVal(srcExpr, LCtx); state = checkNonNull(C, state, srcExpr, srcVal); if (!state) return; @@ -1256,7 +1344,7 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE, if (isBounded) { // Get the max number of characters to copy. const Expr *lenExpr = CE->getArg(2); - SVal lenVal = state->getSVal(lenExpr); + SVal lenVal = state->getSVal(lenExpr, LCtx); // Protect against misdeclared strncpy(). lenVal = svalBuilder.evalCast(lenVal, sizeTy, lenExpr->getType()); @@ -1267,7 +1355,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 ProgramState *stateSourceTooLong, *stateSourceNotTooLong; + ProgramStateRef 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- @@ -1507,32 +1595,44 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE, // overflow, we still need a result. Conjure a return value. if (returnEnd && Result.isUnknown()) { unsigned Count = C.getCurrentBlockCount(); - Result = svalBuilder.getConjuredSymbolVal(NULL, CE, Count); + Result = svalBuilder.getConjuredSymbolVal(NULL, CE, LCtx, Count); } // Set the return value. - state = state->BindExpr(CE, Result); + state = state->BindExpr(CE, LCtx, Result); C.addTransition(state); } void CStringChecker::evalStrcmp(CheckerContext &C, const CallExpr *CE) const { + if (CE->getNumArgs() < 2) + return; + //int strcmp(const char *s1, const char *s2); evalStrcmpCommon(C, CE, /* isBounded = */ false, /* ignoreCase = */ false); } void CStringChecker::evalStrncmp(CheckerContext &C, const CallExpr *CE) const { + if (CE->getNumArgs() < 3) + return; + //int strncmp(const char *s1, const char *s2, size_t n); evalStrcmpCommon(C, CE, /* isBounded = */ true, /* ignoreCase = */ false); } void CStringChecker::evalStrcasecmp(CheckerContext &C, const CallExpr *CE) const { + if (CE->getNumArgs() < 2) + return; + //int strcasecmp(const char *s1, const char *s2); evalStrcmpCommon(C, CE, /* isBounded = */ false, /* ignoreCase = */ true); } void CStringChecker::evalStrncasecmp(CheckerContext &C, const CallExpr *CE) const { + if (CE->getNumArgs() < 3) + return; + //int strncasecmp(const char *s1, const char *s2, size_t n); evalStrcmpCommon(C, CE, /* isBounded = */ true, /* ignoreCase = */ true); } @@ -1540,18 +1640,19 @@ void CStringChecker::evalStrncasecmp(CheckerContext &C, void CStringChecker::evalStrcmpCommon(CheckerContext &C, const CallExpr *CE, bool isBounded, bool ignoreCase) const { CurrentFunctionDescription = "string comparison function"; - const ProgramState *state = C.getState(); + ProgramStateRef state = C.getState(); + const LocationContext *LCtx = C.getLocationContext(); // Check that the first string is non-null const Expr *s1 = CE->getArg(0); - SVal s1Val = state->getSVal(s1); + SVal s1Val = state->getSVal(s1, LCtx); state = checkNonNull(C, state, s1, s1Val); if (!state) return; // Check that the second string is non-null. const Expr *s2 = CE->getArg(1); - SVal s2Val = state->getSVal(s2); + SVal s2Val = state->getSVal(s2, LCtx); state = checkNonNull(C, state, s2, s2Val); if (!state) return; @@ -1575,13 +1676,14 @@ 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 ProgramState *StSameBuf, *StNotSameBuf; + ProgramStateRef StSameBuf, StNotSameBuf; llvm::tie(StSameBuf, StNotSameBuf) = state->assume(SameBuf); // If the two arguments might be the same buffer, we know the result is 0, // and we only need to check one size. if (StSameBuf) { - StSameBuf = StSameBuf->BindExpr(CE, svalBuilder.makeZeroVal(CE->getType())); + StSameBuf = StSameBuf->BindExpr(CE, LCtx, + svalBuilder.makeZeroVal(CE->getType())); C.addTransition(StSameBuf); // If the two arguments are GUARANTEED to be the same, we're done! @@ -1607,7 +1709,7 @@ void CStringChecker::evalStrcmpCommon(CheckerContext &C, const CallExpr *CE, if (isBounded) { // Get the max number of characters to compare. const Expr *lenExpr = CE->getArg(2); - SVal lenVal = state->getSVal(lenExpr); + SVal lenVal = state->getSVal(lenExpr, LCtx); // If the length is known, we can get the right substrings. if (const llvm::APSInt *len = svalBuilder.getKnownValue(state, lenVal)) { @@ -1644,15 +1746,15 @@ void CStringChecker::evalStrcmpCommon(CheckerContext &C, const CallExpr *CE, // Build the SVal of the comparison and bind the return value. SVal resultVal = svalBuilder.makeIntVal(result, CE->getType()); - state = state->BindExpr(CE, resultVal); + state = state->BindExpr(CE, LCtx, resultVal); } } if (!canComputeResult) { // Conjure a symbolic value. It's the best we can do. unsigned Count = C.getCurrentBlockCount(); - SVal resultVal = svalBuilder.getConjuredSymbolVal(NULL, CE, Count); - state = state->BindExpr(CE, resultVal); + SVal resultVal = svalBuilder.getConjuredSymbolVal(NULL, CE, LCtx, Count); + state = state->BindExpr(CE, LCtx, resultVal); } // Record this as a possible path. @@ -1664,42 +1766,47 @@ 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 ProgramState *state = C.getState(); - const Expr *Callee = CE->getCallee(); - const FunctionDecl *FD = state->getSVal(Callee).getAsFunctionDecl(); - - if (!FD) - return false; + const FunctionDecl *FDecl = C.getCalleeDecl(CE); - // Get the name of the callee. If it's a builtin, strip off the prefix. - IdentifierInfo *II = FD->getIdentifier(); - if (!II) // if no identifier, not a simple C function + if (!FDecl) return false; - StringRef Name = II->getName(); - if (Name.startswith("__builtin_")) - Name = Name.substr(10); - - FnCheck evalFunction = llvm::StringSwitch<FnCheck>(Name) - .Cases("memcpy", "__memcpy_chk", &CStringChecker::evalMemcpy) - .Cases("mempcpy", "__mempcpy_chk", &CStringChecker::evalMempcpy) - .Cases("memcmp", "bcmp", &CStringChecker::evalMemcmp) - .Cases("memmove", "__memmove_chk", &CStringChecker::evalMemmove) - .Cases("strcpy", "__strcpy_chk", &CStringChecker::evalStrcpy) - .Cases("strncpy", "__strncpy_chk", &CStringChecker::evalStrncpy) - .Cases("stpcpy", "__stpcpy_chk", &CStringChecker::evalStpcpy) - .Cases("strcat", "__strcat_chk", &CStringChecker::evalStrcat) - .Cases("strncat", "__strncat_chk", &CStringChecker::evalStrncat) - .Case("strlen", &CStringChecker::evalstrLength) - .Case("strnlen", &CStringChecker::evalstrnLength) - .Case("strcmp", &CStringChecker::evalStrcmp) - .Case("strncmp", &CStringChecker::evalStrncmp) - .Case("strcasecmp", &CStringChecker::evalStrcasecmp) - .Case("strncasecmp", &CStringChecker::evalStrncasecmp) - .Case("bcopy", &CStringChecker::evalBcopy) - .Default(NULL); + FnCheck evalFunction = 0; + if (C.isCLibraryFunction(FDecl, "memcpy")) + evalFunction = &CStringChecker::evalMemcpy; + else if (C.isCLibraryFunction(FDecl, "mempcpy")) + evalFunction = &CStringChecker::evalMempcpy; + else if (C.isCLibraryFunction(FDecl, "memcmp")) + evalFunction = &CStringChecker::evalMemcmp; + else if (C.isCLibraryFunction(FDecl, "memmove")) + evalFunction = &CStringChecker::evalMemmove; + else if (C.isCLibraryFunction(FDecl, "strcpy")) + evalFunction = &CStringChecker::evalStrcpy; + else if (C.isCLibraryFunction(FDecl, "strncpy")) + evalFunction = &CStringChecker::evalStrncpy; + else if (C.isCLibraryFunction(FDecl, "stpcpy")) + evalFunction = &CStringChecker::evalStpcpy; + else if (C.isCLibraryFunction(FDecl, "strcat")) + evalFunction = &CStringChecker::evalStrcat; + else if (C.isCLibraryFunction(FDecl, "strncat")) + evalFunction = &CStringChecker::evalStrncat; + else if (C.isCLibraryFunction(FDecl, "strlen")) + evalFunction = &CStringChecker::evalstrLength; + else if (C.isCLibraryFunction(FDecl, "strnlen")) + evalFunction = &CStringChecker::evalstrnLength; + else if (C.isCLibraryFunction(FDecl, "strcmp")) + evalFunction = &CStringChecker::evalStrcmp; + else if (C.isCLibraryFunction(FDecl, "strncmp")) + evalFunction = &CStringChecker::evalStrncmp; + else if (C.isCLibraryFunction(FDecl, "strcasecmp")) + evalFunction = &CStringChecker::evalStrcasecmp; + else if (C.isCLibraryFunction(FDecl, "strncasecmp")) + evalFunction = &CStringChecker::evalStrncasecmp; + else if (C.isCLibraryFunction(FDecl, "bcopy")) + evalFunction = &CStringChecker::evalBcopy; + else if (C.isCLibraryFunction(FDecl, "bcmp")) + evalFunction = &CStringChecker::evalMemcmp; + // If the callee isn't a string function, let another checker handle it. if (!evalFunction) return false; @@ -1710,12 +1817,22 @@ bool CStringChecker::evalCall(const CallExpr *CE, CheckerContext &C) const { // Check and evaluate the call. (this->*evalFunction)(C, CE); + + // If the evaluate call resulted in no change, chain to the next eval call + // handler. + // Note, the custom CString evaluation calls assume that basic safety + // properties are held. However, if the user chooses to turn off some of these + // checks, we ignore the issues and leave the call evaluation to a generic + // handler. + if (!C.isDifferent()) + return false; + return true; } void CStringChecker::checkPreStmt(const DeclStmt *DS, CheckerContext &C) const { // Record string length for char a[] = "abc"; - const ProgramState *state = C.getState(); + ProgramStateRef state = C.getState(); for (DeclStmt::const_decl_iterator I = DS->decl_begin(), E = DS->decl_end(); I != E; ++I) { @@ -1733,12 +1850,12 @@ void CStringChecker::checkPreStmt(const DeclStmt *DS, CheckerContext &C) const { if (!isa<StringLiteral>(Init)) continue; - Loc VarLoc = state->getLValue(D, C.getPredecessor()->getLocationContext()); + Loc VarLoc = state->getLValue(D, C.getLocationContext()); const MemRegion *MR = VarLoc.getAsRegion(); if (!MR) continue; - SVal StrVal = state->getSVal(Init); + SVal StrVal = state->getSVal(Init, C.getLocationContext()); assert(StrVal.isValid() && "Initializer string is unknown or undefined"); DefinedOrUnknownSVal strLength = cast<DefinedOrUnknownSVal>(getCStringLength(C, state, Init, StrVal)); @@ -1749,16 +1866,17 @@ void CStringChecker::checkPreStmt(const DeclStmt *DS, CheckerContext &C) const { C.addTransition(state); } -bool CStringChecker::wantsRegionChangeUpdate(const ProgramState *state) const { +bool CStringChecker::wantsRegionChangeUpdate(ProgramStateRef state) const { CStringLength::EntryMap Entries = state->get<CStringLength>(); return !Entries.isEmpty(); } -const ProgramState * -CStringChecker::checkRegionChanges(const ProgramState *state, +ProgramStateRef +CStringChecker::checkRegionChanges(ProgramStateRef state, const StoreManager::InvalidatedSymbols *, ArrayRef<const MemRegion *> ExplicitRegions, - ArrayRef<const MemRegion *> Regions) const { + ArrayRef<const MemRegion *> Regions, + const CallOrObjCMessage *Call) const { CStringLength::EntryMap Entries = state->get<CStringLength>(); if (Entries.isEmpty()) return state; @@ -1806,7 +1924,7 @@ CStringChecker::checkRegionChanges(const ProgramState *state, return state->set<CStringLength>(Entries); } -void CStringChecker::checkLiveSymbols(const ProgramState *state, +void CStringChecker::checkLiveSymbols(ProgramStateRef state, SymbolReaper &SR) const { // Mark all symbols in our string length map as valid. CStringLength::EntryMap Entries = state->get<CStringLength>(); @@ -1815,8 +1933,8 @@ void CStringChecker::checkLiveSymbols(const ProgramState *state, I != E; ++I) { SVal Len = I.getData(); - for (SVal::symbol_iterator si = Len.symbol_begin(), se = Len.symbol_end(); - si != se; ++si) + for (SymExpr::symbol_iterator si = Len.symbol_begin(), + se = Len.symbol_end(); si != se; ++si) SR.markInUse(*si); } } @@ -1826,7 +1944,7 @@ void CStringChecker::checkDeadSymbols(SymbolReaper &SR, if (!SR.hasDeadSymbols()) return; - const ProgramState *state = C.getState(); + ProgramStateRef state = C.getState(); CStringLength::EntryMap Entries = state->get<CStringLength>(); if (Entries.isEmpty()) return; @@ -1842,9 +1960,22 @@ void CStringChecker::checkDeadSymbols(SymbolReaper &SR, } state = state->set<CStringLength>(Entries); - C.generateNode(state); + C.addTransition(state); } -void ento::registerCStringChecker(CheckerManager &mgr) { - mgr.registerChecker<CStringChecker>(); +#define REGISTER_CHECKER(name) \ +void ento::register##name(CheckerManager &mgr) {\ + static CStringChecker *TheChecker = 0; \ + if (TheChecker == 0) \ + TheChecker = mgr.registerChecker<CStringChecker>(); \ + TheChecker->Filter.Check##name = true; \ +} + +REGISTER_CHECKER(CStringNullArg) +REGISTER_CHECKER(CStringOutOfBounds) +REGISTER_CHECKER(CStringBufferOverlap) +REGISTER_CHECKER(CStringNotNullTerm) + +void ento::registerCStringCheckerBasic(CheckerManager &Mgr) { + registerCStringNullArg(Mgr); } diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CStringSyntaxChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CStringSyntaxChecker.cpp new file mode 100644 index 0000000..befc935 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CStringSyntaxChecker.cpp @@ -0,0 +1,191 @@ +//== CStringSyntaxChecker.cpp - CoreFoundation containers API *- C++ -*-==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// An AST checker that looks for common pitfalls when using C string APIs. +// - Identifies erroneous patterns in the last argument to strncat - the number +// of bytes to copy. +// +//===----------------------------------------------------------------------===// +#include "ClangSACheckers.h" +#include "clang/Analysis/AnalysisContext.h" +#include "clang/AST/Expr.h" +#include "clang/AST/OperationKinds.h" +#include "clang/AST/StmtVisitor.h" +#include "clang/Basic/TargetInfo.h" +#include "clang/Basic/TypeTraits.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/raw_ostream.h" + +using namespace clang; +using namespace ento; + +namespace { +class WalkAST: public StmtVisitor<WalkAST> { + BugReporter &BR; + AnalysisDeclContext* AC; + ASTContext &ASTC; + + /// Check if two expressions refer to the same declaration. + inline bool sameDecl(const Expr *A1, const Expr *A2) { + if (const DeclRefExpr *D1 = dyn_cast<DeclRefExpr>(A1->IgnoreParenCasts())) + if (const DeclRefExpr *D2 = dyn_cast<DeclRefExpr>(A2->IgnoreParenCasts())) + return D1->getDecl() == D2->getDecl(); + return false; + } + + /// Check if the expression E is a sizeof(WithArg). + inline bool isSizeof(const Expr *E, const Expr *WithArg) { + if (const UnaryExprOrTypeTraitExpr *UE = + dyn_cast<UnaryExprOrTypeTraitExpr>(E)) + if (UE->getKind() == UETT_SizeOf) + return sameDecl(UE->getArgumentExpr(), WithArg); + return false; + } + + /// Check if the expression E is a strlen(WithArg). + inline bool isStrlen(const Expr *E, const Expr *WithArg) { + if (const CallExpr *CE = dyn_cast<CallExpr>(E)) { + const FunctionDecl *FD = CE->getDirectCallee(); + if (!FD) + return false; + return (CheckerContext::isCLibraryFunction(FD, "strlen", ASTC) + && sameDecl(CE->getArg(0), WithArg)); + } + return false; + } + + /// Check if the expression is an integer literal with value 1. + inline bool isOne(const Expr *E) { + if (const IntegerLiteral *IL = dyn_cast<IntegerLiteral>(E)) + return (IL->getValue().isIntN(1)); + return false; + } + + inline StringRef getPrintableName(const Expr *E) { + if (const DeclRefExpr *D = dyn_cast<DeclRefExpr>(E->IgnoreParenCasts())) + return D->getDecl()->getName(); + return StringRef(); + } + + /// Identify erroneous patterns in the last argument to strncat - the number + /// of bytes to copy. + bool containsBadStrncatPattern(const CallExpr *CE); + +public: + WalkAST(BugReporter &br, AnalysisDeclContext* ac) : + BR(br), AC(ac), ASTC(AC->getASTContext()) { + } + + // Statement visitor methods. + void VisitChildren(Stmt *S); + void VisitStmt(Stmt *S) { + VisitChildren(S); + } + void VisitCallExpr(CallExpr *CE); +}; +} // end anonymous namespace + +// The correct size argument should look like following: +// strncat(dst, src, sizeof(dst) - strlen(dest) - 1); +// We look for the following anti-patterns: +// - strncat(dst, src, sizeof(dst) - strlen(dst)); +// - strncat(dst, src, sizeof(dst) - 1); +// - strncat(dst, src, sizeof(dst)); +bool WalkAST::containsBadStrncatPattern(const CallExpr *CE) { + const Expr *DstArg = CE->getArg(0); + const Expr *SrcArg = CE->getArg(1); + const Expr *LenArg = CE->getArg(2); + + // Identify wrong size expressions, which are commonly used instead. + if (const BinaryOperator *BE = + dyn_cast<BinaryOperator>(LenArg->IgnoreParenCasts())) { + // - sizeof(dst) - strlen(dst) + if (BE->getOpcode() == BO_Sub) { + const Expr *L = BE->getLHS(); + const Expr *R = BE->getRHS(); + if (isSizeof(L, DstArg) && isStrlen(R, DstArg)) + return true; + + // - sizeof(dst) - 1 + if (isSizeof(L, DstArg) && isOne(R->IgnoreParenCasts())) + return true; + } + } + // - sizeof(dst) + if (isSizeof(LenArg, DstArg)) + return true; + + // - sizeof(src) + if (isSizeof(LenArg, SrcArg)) + return true; + return false; +} + +void WalkAST::VisitCallExpr(CallExpr *CE) { + const FunctionDecl *FD = CE->getDirectCallee(); + if (!FD) + return; + + if (CheckerContext::isCLibraryFunction(FD, "strncat", ASTC)) { + if (containsBadStrncatPattern(CE)) { + const Expr *DstArg = CE->getArg(0); + const Expr *LenArg = CE->getArg(2); + SourceRange R = LenArg->getSourceRange(); + PathDiagnosticLocation Loc = + PathDiagnosticLocation::createBegin(LenArg, BR.getSourceManager(), AC); + + StringRef DstName = getPrintableName(DstArg); + + SmallString<256> S; + llvm::raw_svector_ostream os(S); + os << "Potential buffer overflow. "; + if (!DstName.empty()) { + os << "Replace with 'sizeof(" << DstName << ") " + "- strlen(" << DstName <<") - 1'"; + os << " or u"; + } else + os << "U"; + os << "se a safer 'strlcat' API"; + + BR.EmitBasicReport(FD, "Anti-pattern in the argument", "C String API", + os.str(), Loc, &R, 1); + } + } + + // Recurse and check children. + VisitChildren(CE); +} + +void WalkAST::VisitChildren(Stmt *S) { + for (Stmt::child_iterator I = S->child_begin(), E = S->child_end(); I != E; + ++I) + if (Stmt *child = *I) + Visit(child); +} + +namespace { +class CStringSyntaxChecker: public Checker<check::ASTCodeBody> { +public: + + void checkASTCodeBody(const Decl *D, AnalysisManager& Mgr, + BugReporter &BR) const { + WalkAST walker(BR, Mgr.getAnalysisDeclContext(D)); + walker.Visit(D->getBody()); + } +}; +} + +void ento::registerCStringSyntaxChecker(CheckerManager &mgr) { + mgr.registerChecker<CStringSyntaxChecker>(); +} + diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp index 4db6ac0..f601431 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp @@ -20,6 +20,7 @@ #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/AST/ParentMap.h" #include "clang/Basic/TargetInfo.h" +#include "llvm/ADT/SmallString.h" using namespace clang; using namespace ento; @@ -27,12 +28,13 @@ using namespace ento; namespace { class CallAndMessageChecker : public Checker< check::PreStmt<CallExpr>, check::PreObjCMessage > { - mutable llvm::OwningPtr<BugType> BT_call_null; - mutable llvm::OwningPtr<BugType> BT_call_undef; - mutable llvm::OwningPtr<BugType> BT_call_arg; - mutable llvm::OwningPtr<BugType> BT_msg_undef; - mutable llvm::OwningPtr<BugType> BT_msg_arg; - mutable llvm::OwningPtr<BugType> BT_msg_ret; + mutable OwningPtr<BugType> BT_call_null; + mutable OwningPtr<BugType> BT_call_undef; + mutable OwningPtr<BugType> BT_call_arg; + mutable OwningPtr<BugType> BT_msg_undef; + mutable OwningPtr<BugType> BT_objc_prop_undef; + mutable OwningPtr<BugType> BT_msg_arg; + mutable OwningPtr<BugType> BT_msg_ret; public: void checkPreStmt(const CallExpr *CE, CheckerContext &C) const; @@ -40,19 +42,22 @@ public: private: static void PreVisitProcessArgs(CheckerContext &C,CallOrObjCMessage callOrMsg, - const char *BT_desc, llvm::OwningPtr<BugType> &BT); + const char *BT_desc, OwningPtr<BugType> &BT); static bool PreVisitProcessArg(CheckerContext &C, SVal V,SourceRange argRange, - const Expr *argEx, const char *BT_desc, llvm::OwningPtr<BugType> &BT); + const Expr *argEx, + const bool checkUninitFields, + const char *BT_desc, + OwningPtr<BugType> &BT); static void EmitBadCall(BugType *BT, CheckerContext &C, const CallExpr *CE); void emitNilReceiverBug(CheckerContext &C, const ObjCMessage &msg, ExplodedNode *N) const; void HandleNilReceiver(CheckerContext &C, - const ProgramState *state, + ProgramStateRef state, ObjCMessage msg) const; - static void LazyInit_BT(const char *desc, llvm::OwningPtr<BugType> &BT) { + static void LazyInit_BT(const char *desc, OwningPtr<BugType> &BT) { if (!BT) BT.reset(new BuiltinBug(desc)); } @@ -67,17 +72,27 @@ void CallAndMessageChecker::EmitBadCall(BugType *BT, CheckerContext &C, BugReport *R = new BugReport(*BT, BT->getName(), N); R->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, - bugreporter::GetCalleeExpr(N))); + bugreporter::GetCalleeExpr(N), R)); C.EmitReport(R); } void CallAndMessageChecker::PreVisitProcessArgs(CheckerContext &C, CallOrObjCMessage callOrMsg, const char *BT_desc, - llvm::OwningPtr<BugType> &BT) { + OwningPtr<BugType> &BT) { + // Don't check for uninitialized field values in arguments if the + // caller has a body that is available and we have the chance to inline it. + // This is a hack, but is a reasonable compromise betweens sometimes warning + // and sometimes not depending on if we decide to inline a function. + const Decl *D = callOrMsg.getDecl(); + const bool checkUninitFields = + !(C.getAnalysisManager().shouldInlineCall() && + (D && D->getBody())); + for (unsigned i = 0, e = callOrMsg.getNumArgs(); i != e; ++i) if (PreVisitProcessArg(C, callOrMsg.getArgSVal(i), callOrMsg.getArgSourceRange(i), callOrMsg.getArg(i), + checkUninitFields, BT_desc, BT)) return; } @@ -85,9 +100,9 @@ void CallAndMessageChecker::PreVisitProcessArgs(CheckerContext &C, bool CallAndMessageChecker::PreVisitProcessArg(CheckerContext &C, SVal V, SourceRange argRange, const Expr *argEx, + const bool checkUninitFields, const char *BT_desc, - llvm::OwningPtr<BugType> &BT) { - + OwningPtr<BugType> &BT) { if (V.isUndef()) { if (ExplodedNode *N = C.generateSink()) { LazyInit_BT(BT_desc, BT); @@ -96,12 +111,16 @@ bool CallAndMessageChecker::PreVisitProcessArg(CheckerContext &C, BugReport *R = new BugReport(*BT, BT->getName(), N); R->addRange(argRange); if (argEx) - R->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, argEx)); + R->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, argEx, + R)); C.EmitReport(R); } return true; } + if (!checkUninitFields) + return false; + if (const nonloc::LazyCompoundVal *LV = dyn_cast<nonloc::LazyCompoundVal>(&V)) { @@ -133,7 +152,7 @@ bool CallAndMessageChecker::PreVisitProcessArg(CheckerContext &C, return true; } else { - const SVal &V = StoreMgr.Retrieve(store, loc::MemRegionVal(FR)); + const SVal &V = StoreMgr.getBinding(store, loc::MemRegionVal(FR)); if (V.isUndef()) return true; } @@ -154,7 +173,7 @@ bool CallAndMessageChecker::PreVisitProcessArg(CheckerContext &C, if (F.Find(D->getRegion())) { if (ExplodedNode *N = C.generateSink()) { LazyInit_BT(BT_desc, BT); - llvm::SmallString<512> Str; + SmallString<512> Str; llvm::raw_svector_ostream os(Str); os << "Passed-by-value struct argument contains uninitialized data"; @@ -193,7 +212,8 @@ void CallAndMessageChecker::checkPreStmt(const CallExpr *CE, CheckerContext &C) const{ const Expr *Callee = CE->getCallee()->IgnoreParens(); - SVal L = C.getState()->getSVal(Callee); + const LocationContext *LCtx = C.getLocationContext(); + SVal L = C.getState()->getSVal(Callee, LCtx); if (L.isUndef()) { if (!BT_call_undef) @@ -210,7 +230,7 @@ void CallAndMessageChecker::checkPreStmt(const CallExpr *CE, EmitBadCall(BT_call_null.get(), C, CE); } - PreVisitProcessArgs(C, CallOrObjCMessage(CE, C.getState()), + PreVisitProcessArgs(C, CallOrObjCMessage(CE, C.getState(), LCtx), "Function call argument is an uninitialized value", BT_call_arg); } @@ -218,21 +238,33 @@ void CallAndMessageChecker::checkPreStmt(const CallExpr *CE, void CallAndMessageChecker::checkPreObjCMessage(ObjCMessage msg, CheckerContext &C) const { - const ProgramState *state = C.getState(); + ProgramStateRef state = C.getState(); + const LocationContext *LCtx = C.getLocationContext(); // FIXME: Handle 'super'? if (const Expr *receiver = msg.getInstanceReceiver()) { - SVal recVal = state->getSVal(receiver); + SVal recVal = state->getSVal(receiver, LCtx); if (recVal.isUndef()) { if (ExplodedNode *N = C.generateSink()) { - if (!BT_msg_undef) - BT_msg_undef.reset(new BuiltinBug("Receiver in message expression is " - "an uninitialized value")); + BugType *BT = 0; + if (msg.isPureMessageExpr()) { + if (!BT_msg_undef) + BT_msg_undef.reset(new BuiltinBug("Receiver in message expression " + "is an uninitialized value")); + BT = BT_msg_undef.get(); + } + else { + if (!BT_objc_prop_undef) + BT_objc_prop_undef.reset(new BuiltinBug("Property access on an " + "uninitialized object pointer")); + BT = BT_objc_prop_undef.get(); + } BugReport *R = - new BugReport(*BT_msg_undef, BT_msg_undef->getName(), N); + new BugReport(*BT, BT->getName(), N); R->addRange(receiver->getSourceRange()); R->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, - receiver)); + receiver, + R)); C.EmitReport(R); } return; @@ -240,7 +272,7 @@ void CallAndMessageChecker::checkPreObjCMessage(ObjCMessage msg, // Bifurcate the state into nil and non-nil ones. DefinedOrUnknownSVal receiverVal = cast<DefinedOrUnknownSVal>(recVal); - const ProgramState *notNilState, *nilState; + ProgramStateRef notNilState, nilState; llvm::tie(notNilState, nilState) = state->assume(receiverVal); // Handle receiver must be nil. @@ -255,7 +287,8 @@ void CallAndMessageChecker::checkPreObjCMessage(ObjCMessage msg, "Argument for property setter is an uninitialized value" : "Argument in message expression is an uninitialized value"; // Check for any arguments that are uninitialized/undefined. - PreVisitProcessArgs(C, CallOrObjCMessage(msg, state), bugDesc, BT_msg_arg); + PreVisitProcessArgs(C, CallOrObjCMessage(msg, state, LCtx), + bugDesc, BT_msg_arg); } void CallAndMessageChecker::emitNilReceiverBug(CheckerContext &C, @@ -267,7 +300,7 @@ void CallAndMessageChecker::emitNilReceiverBug(CheckerContext &C, new BuiltinBug("Receiver in message expression is " "'nil' and returns a garbage value")); - llvm::SmallString<200> buf; + SmallString<200> buf; llvm::raw_svector_ostream os(buf); os << "The receiver of message '" << msg.getSelector().getAsString() << "' is nil and returns a value of type '" @@ -277,48 +310,39 @@ void CallAndMessageChecker::emitNilReceiverBug(CheckerContext &C, if (const Expr *receiver = msg.getInstanceReceiver()) { report->addRange(receiver->getSourceRange()); report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, - receiver)); + receiver, + report)); } C.EmitReport(report); } static bool supportsNilWithFloatRet(const llvm::Triple &triple) { - return triple.getVendor() == llvm::Triple::Apple && - (!triple.isMacOSXVersionLT(10,5) || - triple.getArch() == llvm::Triple::arm || - triple.getArch() == llvm::Triple::thumb); + return (triple.getVendor() == llvm::Triple::Apple && + (triple.getOS() == llvm::Triple::IOS || + !triple.isMacOSXVersionLT(10,5))); } void CallAndMessageChecker::HandleNilReceiver(CheckerContext &C, - const ProgramState *state, + ProgramStateRef state, ObjCMessage msg) const { ASTContext &Ctx = C.getASTContext(); // Check the return type of the message expression. A message to nil will // return different values depending on the return type and the architecture. QualType RetTy = msg.getType(Ctx); - CanQualType CanRetTy = Ctx.getCanonicalType(RetTy); + const LocationContext *LCtx = C.getLocationContext(); if (CanRetTy->isStructureOrClassType()) { - // FIXME: At some point we shouldn't rely on isConsumedExpr(), but instead - // 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)) - emitNilReceiverBug(C, msg, N); - return; - } - - // The result is not consumed by a surrounding expression. Just propagate - // the current state. - C.addTransition(state); + // Structure returns are safe since the compiler zeroes them out. + SVal V = C.getSValBuilder().makeZeroVal(msg.getType(Ctx)); + C.addTransition(state->BindExpr(msg.getMessageExpr(), LCtx, V)); return; } - // Other cases: check if the return type is smaller than void*. - if (CanRetTy != Ctx.VoidTy && - C.getPredecessor()->getParentMap().isConsumedExpr(msg.getOriginExpr())) { + // Other cases: check if sizeof(return type) > sizeof(void*) + if (CanRetTy != Ctx.VoidTy && C.getLocationContext()->getParentMap() + .isConsumedExpr(msg.getMessageExpr())) { // Compute: sizeof(void *) and sizeof(return type) const uint64_t voidPtrSize = Ctx.getTypeSize(Ctx.VoidPtrTy); const uint64_t returnTypeSize = Ctx.getTypeSize(CanRetTy); @@ -349,7 +373,7 @@ void CallAndMessageChecker::HandleNilReceiver(CheckerContext &C, // of this case unless we have *a lot* more knowledge. // SVal V = C.getSValBuilder().makeZeroVal(msg.getType(Ctx)); - C.generateNode(state->BindExpr(msg.getOriginExpr(), V)); + C.addTransition(state->BindExpr(msg.getMessageExpr(), LCtx, V)); return; } diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CastSizeChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CastSizeChecker.cpp index 84a9e6b..2e184fb 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CastSizeChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CastSizeChecker.cpp @@ -23,7 +23,7 @@ using namespace ento; namespace { class CastSizeChecker : public Checker< check::PreStmt<CastExpr> > { - mutable llvm::OwningPtr<BuiltinBug> BT; + mutable OwningPtr<BuiltinBug> BT; public: void checkPreStmt(const CastExpr *CE, CheckerContext &C) const; }; @@ -44,8 +44,8 @@ void CastSizeChecker::checkPreStmt(const CastExpr *CE,CheckerContext &C) const { if (ToPointeeTy->isIncompleteType()) return; - const ProgramState *state = C.getState(); - const MemRegion *R = state->getSVal(E).getAsRegion(); + ProgramStateRef state = C.getState(); + const MemRegion *R = state->getSVal(E, C.getLocationContext()).getAsRegion(); if (R == 0) return; diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CastToStructChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CastToStructChecker.cpp index c855210..1407638 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CastToStructChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CastToStructChecker.cpp @@ -24,7 +24,7 @@ using namespace ento; namespace { class CastToStructChecker : public Checker< check::PreStmt<CastExpr> > { - mutable llvm::OwningPtr<BuiltinBug> BT; + mutable OwningPtr<BuiltinBug> BT; public: void checkPreStmt(const CastExpr *CE, CheckerContext &C) const; @@ -56,7 +56,7 @@ void CastToStructChecker::checkPreStmt(const CastExpr *CE, // Now the cast-to-type is struct pointer, the original type is not void*. if (!OrigPointeeTy->isRecordType()) { - if (ExplodedNode *N = C.generateNode()) { + if (ExplodedNode *N = C.addTransition()) { if (!BT) BT.reset(new BuiltinBug("Cast from non-struct type to struct type", "Casting a non-structure type to a structure type " diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp index c325bb1..133204a 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp @@ -177,9 +177,10 @@ static void checkObjCDealloc(const ObjCImplementationDecl *D, std::string buf; llvm::raw_string_ostream os(buf); - os << "Objective-C class '" << D << "' lacks a 'dealloc' instance method"; + os << "Objective-C class '" << *D << "' lacks a 'dealloc' instance method"; - BR.EmitBasicReport(name, os.str(), DLoc); + BR.EmitBasicReport(D, name, categories::CoreFoundationObjectiveC, + os.str(), DLoc); return; } @@ -192,11 +193,12 @@ static void checkObjCDealloc(const ObjCImplementationDecl *D, std::string buf; llvm::raw_string_ostream os(buf); - os << "The 'dealloc' instance method in Objective-C class '" << D + os << "The 'dealloc' instance method in Objective-C class '" << *D << "' does not send a 'dealloc' message to its super class" " (missing [super dealloc])"; - BR.EmitBasicReport(name, os.str(), DLoc); + BR.EmitBasicReport(MD, name, categories::CoreFoundationObjectiveC, + os.str(), DLoc); return; } @@ -236,9 +238,7 @@ static void checkObjCDealloc(const ObjCImplementationDecl *D, bool requiresRelease = PD->getSetterKind() != ObjCPropertyDecl::Assign; if (scan_ivar_release(MD->getBody(), ID, PD, RS, SelfII, Ctx) != requiresRelease) { - const char *name; - const char* category = "Memory (Core Foundation/Objective-C)"; - + const char *name = 0; std::string buf; llvm::raw_string_ostream os(buf); @@ -263,7 +263,8 @@ static void checkObjCDealloc(const ObjCImplementationDecl *D, PathDiagnosticLocation SDLoc = PathDiagnosticLocation::createBegin((*I), BR.getSourceManager()); - BR.EmitBasicReport(name, category, os.str(), SDLoc); + BR.EmitBasicReport(MD, name, categories::CoreFoundationObjectiveC, + os.str(), SDLoc); } } } @@ -278,9 +279,9 @@ class ObjCDeallocChecker : public Checker< public: void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager& mgr, BugReporter &BR) const { - if (mgr.getLangOptions().getGC() == LangOptions::GCOnly) + if (mgr.getLangOpts().getGC() == LangOptions::GCOnly) return; - checkObjCDealloc(cast<ObjCImplementationDecl>(D), mgr.getLangOptions(), BR); + checkObjCDealloc(cast<ObjCImplementationDecl>(D), mgr.getLangOpts(), BR); } }; } diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CheckObjCInstMethSignature.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CheckObjCInstMethSignature.cpp index c076c1e3..6df47b1 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CheckObjCInstMethSignature.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CheckObjCInstMethSignature.cpp @@ -70,7 +70,9 @@ static void CompareReturnTypes(const ObjCMethodDecl *MethDerived, PathDiagnosticLocation::createBegin(MethDerived, BR.getSourceManager()); - BR.EmitBasicReport("Incompatible instance method return type", + BR.EmitBasicReport(MethDerived, + "Incompatible instance method return type", + categories::CoreFoundationObjectiveC, os.str(), MethDLoc); } } diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp index bf7ba18..dde9071 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp @@ -18,6 +18,7 @@ #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" +#include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/Support/raw_ostream.h" @@ -34,18 +35,40 @@ static bool isArc4RandomAvailable(const ASTContext &Ctx) { } namespace { +struct DefaultBool { + bool val; + DefaultBool() : val(false) {} + operator bool() const { return val; } + DefaultBool &operator=(bool b) { val = b; return *this; } +}; + +struct ChecksFilter { + DefaultBool check_gets; + DefaultBool check_getpw; + DefaultBool check_mktemp; + DefaultBool check_mkstemp; + DefaultBool check_strcpy; + DefaultBool check_rand; + DefaultBool check_vfork; + DefaultBool check_FloatLoopCounter; + DefaultBool check_UncheckedReturn; +}; + class WalkAST : public StmtVisitor<WalkAST> { BugReporter &BR; - AnalysisContext* AC; + AnalysisDeclContext* AC; enum { num_setids = 6 }; IdentifierInfo *II_setid[num_setids]; const bool CheckRand; + const ChecksFilter &filter; public: - WalkAST(BugReporter &br, AnalysisContext* ac) + WalkAST(BugReporter &br, AnalysisDeclContext* ac, + const ChecksFilter &f) : BR(br), AC(ac), II_setid(), - CheckRand(isArc4RandomAvailable(BR.getContext())) {} + CheckRand(isArc4RandomAvailable(BR.getContext())), + filter(f) {} // Statement visitor methods. void VisitCallExpr(CallExpr *CE); @@ -66,6 +89,7 @@ public: void checkCall_gets(const CallExpr *CE, const FunctionDecl *FD); void checkCall_getpw(const CallExpr *CE, const FunctionDecl *FD); void checkCall_mktemp(const CallExpr *CE, const FunctionDecl *FD); + void checkCall_mkstemp(const CallExpr *CE, const FunctionDecl *FD); void checkCall_strcpy(const CallExpr *CE, const FunctionDecl *FD); void checkCall_strcat(const CallExpr *CE, const FunctionDecl *FD); void checkCall_rand(const CallExpr *CE, const FunctionDecl *FD); @@ -105,6 +129,9 @@ void WalkAST::VisitCallExpr(CallExpr *CE) { .Case("gets", &WalkAST::checkCall_gets) .Case("getpw", &WalkAST::checkCall_getpw) .Case("mktemp", &WalkAST::checkCall_mktemp) + .Case("mkstemp", &WalkAST::checkCall_mkstemp) + .Case("mkdtemp", &WalkAST::checkCall_mkstemp) + .Case("mkstemps", &WalkAST::checkCall_mkstemp) .Cases("strcpy", "__strcpy_chk", &WalkAST::checkCall_strcpy) .Cases("strcat", "__strcat_chk", &WalkAST::checkCall_strcat) .Case("drand48", &WalkAST::checkCall_rand) @@ -186,6 +213,9 @@ getIncrementedVar(const Expr *expr, const VarDecl *x, const VarDecl *y) { /// CERT: FLP30-C, FLP30-CPP. /// void WalkAST::checkLoopConditionForFloat(const ForStmt *FS) { + if (!filter.check_FloatLoopCounter) + return; + // Does the loop have a condition? const Expr *condition = FS->getCond(); @@ -242,7 +272,7 @@ void WalkAST::checkLoopConditionForFloat(const ForStmt *FS) { const DeclRefExpr *drCond = vdLHS == drInc->getDecl() ? drLHS : drRHS; SmallVector<SourceRange, 2> ranges; - llvm::SmallString<256> sbuf; + SmallString<256> sbuf; llvm::raw_svector_ostream os(sbuf); os << "Variable '" << drCond->getDecl()->getName() @@ -256,7 +286,8 @@ void WalkAST::checkLoopConditionForFloat(const ForStmt *FS) { PathDiagnosticLocation FSLoc = PathDiagnosticLocation::createBegin(FS, BR.getSourceManager(), AC); - BR.EmitBasicReport(bugType, "Security", os.str(), + BR.EmitBasicReport(AC->getDecl(), + bugType, "Security", os.str(), FSLoc, ranges.data(), ranges.size()); } @@ -268,6 +299,9 @@ void WalkAST::checkLoopConditionForFloat(const ForStmt *FS) { //===----------------------------------------------------------------------===// void WalkAST::checkCall_gets(const CallExpr *CE, const FunctionDecl *FD) { + if (!filter.check_gets) + return; + const FunctionProtoType *FPT = dyn_cast<FunctionProtoType>(FD->getType().IgnoreParens()); if (!FPT) @@ -289,7 +323,8 @@ void WalkAST::checkCall_gets(const CallExpr *CE, const FunctionDecl *FD) { SourceRange R = CE->getCallee()->getSourceRange(); PathDiagnosticLocation CELoc = PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); - BR.EmitBasicReport("Potential buffer overflow in call to 'gets'", + BR.EmitBasicReport(AC->getDecl(), + "Potential buffer overflow in call to 'gets'", "Security", "Call to function 'gets' is extremely insecure as it can " "always result in a buffer overflow", @@ -302,6 +337,9 @@ void WalkAST::checkCall_gets(const CallExpr *CE, const FunctionDecl *FD) { //===----------------------------------------------------------------------===// void WalkAST::checkCall_getpw(const CallExpr *CE, const FunctionDecl *FD) { + if (!filter.check_getpw) + return; + const FunctionProtoType *FPT = dyn_cast<FunctionProtoType>(FD->getType().IgnoreParens()); if (!FPT) @@ -327,7 +365,8 @@ void WalkAST::checkCall_getpw(const CallExpr *CE, const FunctionDecl *FD) { SourceRange R = CE->getCallee()->getSourceRange(); PathDiagnosticLocation CELoc = PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); - BR.EmitBasicReport("Potential buffer overflow in call to 'getpw'", + BR.EmitBasicReport(AC->getDecl(), + "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().", @@ -335,11 +374,18 @@ void WalkAST::checkCall_getpw(const CallExpr *CE, const FunctionDecl *FD) { } //===----------------------------------------------------------------------===// -// Check: Any use of 'mktemp' is insecure.It is obsoleted by mkstemp(). +// Check: Any use of 'mktemp' is insecure. It is obsoleted by mkstemp(). // CWE-377: Insecure Temporary File //===----------------------------------------------------------------------===// void WalkAST::checkCall_mktemp(const CallExpr *CE, const FunctionDecl *FD) { + if (!filter.check_mktemp) { + // Fall back to the security check of looking for enough 'X's in the + // format string, since that is a less severe warning. + checkCall_mkstemp(CE, FD); + return; + } + const FunctionProtoType *FPT = dyn_cast<FunctionProtoType>(FD->getType().IgnoreParens()); if(!FPT) @@ -362,13 +408,98 @@ void WalkAST::checkCall_mktemp(const CallExpr *CE, const FunctionDecl *FD) { SourceRange R = CE->getCallee()->getSourceRange(); PathDiagnosticLocation CELoc = PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); - BR.EmitBasicReport("Potential insecure temporary file in call 'mktemp'", + BR.EmitBasicReport(AC->getDecl(), + "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", + "creates or uses insecure temporary file. Use 'mkstemp' " + "instead", CELoc, &R, 1); } + +//===----------------------------------------------------------------------===// +// Check: Use of 'mkstemp', 'mktemp', 'mkdtemp' should contain at least 6 X's. +//===----------------------------------------------------------------------===// + +void WalkAST::checkCall_mkstemp(const CallExpr *CE, const FunctionDecl *FD) { + if (!filter.check_mkstemp) + return; + + StringRef Name = FD->getIdentifier()->getName(); + std::pair<signed, signed> ArgSuffix = + llvm::StringSwitch<std::pair<signed, signed> >(Name) + .Case("mktemp", std::make_pair(0,-1)) + .Case("mkstemp", std::make_pair(0,-1)) + .Case("mkdtemp", std::make_pair(0,-1)) + .Case("mkstemps", std::make_pair(0,1)) + .Default(std::make_pair(-1, -1)); + + assert(ArgSuffix.first >= 0 && "Unsupported function"); + + // Check if the number of arguments is consistent with out expectations. + unsigned numArgs = CE->getNumArgs(); + if ((signed) numArgs <= ArgSuffix.first) + return; + + const StringLiteral *strArg = + dyn_cast<StringLiteral>(CE->getArg((unsigned)ArgSuffix.first) + ->IgnoreParenImpCasts()); + + // Currently we only handle string literals. It is possible to do better, + // either by looking at references to const variables, or by doing real + // flow analysis. + if (!strArg || strArg->getCharByteWidth() != 1) + return; + + // Count the number of X's, taking into account a possible cutoff suffix. + StringRef str = strArg->getString(); + unsigned numX = 0; + unsigned n = str.size(); + + // Take into account the suffix. + unsigned suffix = 0; + if (ArgSuffix.second >= 0) { + const Expr *suffixEx = CE->getArg((unsigned)ArgSuffix.second); + llvm::APSInt Result; + if (!suffixEx->EvaluateAsInt(Result, BR.getContext())) + return; + // FIXME: Issue a warning. + if (Result.isNegative()) + return; + suffix = (unsigned) Result.getZExtValue(); + n = (n > suffix) ? n - suffix : 0; + } + + for (unsigned i = 0; i < n; ++i) + if (str[i] == 'X') ++numX; + + if (numX >= 6) + return; + + // Issue a warning. + SourceRange R = strArg->getSourceRange(); + PathDiagnosticLocation CELoc = + PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); + SmallString<512> buf; + llvm::raw_svector_ostream out(buf); + out << "Call to '" << Name << "' should have at least 6 'X's in the" + " format string to be secure (" << numX << " 'X'"; + if (numX != 1) + out << 's'; + out << " seen"; + if (suffix) { + out << ", " << suffix << " character"; + if (suffix > 1) + out << 's'; + out << " used as a suffix"; + } + out << ')'; + BR.EmitBasicReport(AC->getDecl(), + "Insecure temporary file creation", "Security", + out.str(), CELoc, &R, 1); +} + //===----------------------------------------------------------------------===// // Check: Any use of 'strcpy' is insecure. // @@ -376,6 +507,9 @@ void WalkAST::checkCall_mktemp(const CallExpr *CE, const FunctionDecl *FD) { // the Bounds of a Memory Buffer //===----------------------------------------------------------------------===// void WalkAST::checkCall_strcpy(const CallExpr *CE, const FunctionDecl *FD) { + if (!filter.check_strcpy) + return; + if (!checkCall_strCommon(CE, FD)) return; @@ -383,13 +517,14 @@ void WalkAST::checkCall_strcpy(const CallExpr *CE, const FunctionDecl *FD) { SourceRange R = CE->getCallee()->getSourceRange(); PathDiagnosticLocation CELoc = PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); - BR.EmitBasicReport("Potential insecure memory buffer bounds restriction in " + BR.EmitBasicReport(AC->getDecl(), + "Potential insecure memory buffer bounds restriction in " "call 'strcpy'", "Security", "Call to function 'strcpy' is insecure as it does not " - "provide bounding of the memory buffer. Replace " - "unbounded copy functions with analogous functions that " - "support length arguments such as 'strncpy'. CWE-119.", + "provide bounding of the memory buffer. Replace " + "unbounded copy functions with analogous functions that " + "support length arguments such as 'strlcpy'. CWE-119.", CELoc, &R, 1); } @@ -400,6 +535,9 @@ void WalkAST::checkCall_strcpy(const CallExpr *CE, const FunctionDecl *FD) { // the Bounds of a Memory Buffer //===----------------------------------------------------------------------===// void WalkAST::checkCall_strcat(const CallExpr *CE, const FunctionDecl *FD) { + if (!filter.check_strcpy) + return; + if (!checkCall_strCommon(CE, FD)) return; @@ -407,13 +545,14 @@ void WalkAST::checkCall_strcat(const CallExpr *CE, const FunctionDecl *FD) { 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", - "Call to function 'strcat' is insecure as it does not " - "provide bounding of the memory buffer. Replace " - "unbounded copy functions with analogous functions that " - "support length arguments such as 'strncat'. CWE-119.", + BR.EmitBasicReport(AC->getDecl(), + "Potential insecure memory buffer bounds restriction in " + "call 'strcat'", + "Security", + "Call to function 'strcat' is insecure as it does not " + "provide bounding of the memory buffer. Replace " + "unbounded copy functions with analogous functions that " + "support length arguments such as 'strlcat'. CWE-119.", CELoc, &R, 1); } @@ -453,7 +592,7 @@ bool WalkAST::checkCall_strCommon(const CallExpr *CE, const FunctionDecl *FD) { //===----------------------------------------------------------------------===// void WalkAST::checkCall_rand(const CallExpr *CE, const FunctionDecl *FD) { - if (!CheckRand) + if (!filter.check_rand || !CheckRand) return; const FunctionProtoType *FTP @@ -475,11 +614,11 @@ void WalkAST::checkCall_rand(const CallExpr *CE, const FunctionDecl *FD) { return; // Issue a warning. - llvm::SmallString<256> buf1; + SmallString<256> buf1; llvm::raw_svector_ostream os1(buf1); os1 << '\'' << *FD << "' is a poor random number generator"; - llvm::SmallString<256> buf2; + SmallString<256> buf2; llvm::raw_svector_ostream os2(buf2); os2 << "Function '" << *FD << "' is obsolete because it implements a poor random number generator." @@ -488,7 +627,8 @@ void WalkAST::checkCall_rand(const CallExpr *CE, const FunctionDecl *FD) { SourceRange R = CE->getCallee()->getSourceRange(); PathDiagnosticLocation CELoc = PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); - BR.EmitBasicReport(os1.str(), "Security", os2.str(), CELoc, &R, 1); + BR.EmitBasicReport(AC->getDecl(), os1.str(), "Security", os2.str(), + CELoc, &R, 1); } //===----------------------------------------------------------------------===// @@ -497,7 +637,7 @@ void WalkAST::checkCall_rand(const CallExpr *CE, const FunctionDecl *FD) { //===----------------------------------------------------------------------===// void WalkAST::checkCall_random(const CallExpr *CE, const FunctionDecl *FD) { - if (!CheckRand) + if (!CheckRand || !filter.check_rand) return; const FunctionProtoType *FTP @@ -513,7 +653,8 @@ void WalkAST::checkCall_random(const CallExpr *CE, const FunctionDecl *FD) { SourceRange R = CE->getCallee()->getSourceRange(); PathDiagnosticLocation CELoc = PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); - BR.EmitBasicReport("'random' is not a secure random number generator", + BR.EmitBasicReport(AC->getDecl(), + "'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' " @@ -526,11 +667,15 @@ void WalkAST::checkCall_random(const CallExpr *CE, const FunctionDecl *FD) { //===----------------------------------------------------------------------===// void WalkAST::checkCall_vfork(const CallExpr *CE, const FunctionDecl *FD) { + if (!filter.check_vfork) + return; + // 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 " + BR.EmitBasicReport(AC->getDecl(), + "Potential insecure implementation-specific behavior in " "call 'vfork'", "Security", "Call to function 'vfork' is insecure as it can lead to " @@ -546,6 +691,9 @@ void WalkAST::checkCall_vfork(const CallExpr *CE, const FunctionDecl *FD) { //===----------------------------------------------------------------------===// void WalkAST::checkUncheckedReturnValue(CallExpr *CE) { + if (!filter.check_UncheckedReturn) + return; + const FunctionDecl *FD = CE->getDirectCallee(); if (!FD) return; @@ -586,11 +734,11 @@ void WalkAST::checkUncheckedReturnValue(CallExpr *CE) { return; // Issue a warning. - llvm::SmallString<256> buf1; + SmallString<256> buf1; llvm::raw_svector_ostream os1(buf1); os1 << "Return value is not checked in call to '" << *FD << '\''; - llvm::SmallString<256> buf2; + 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 @@ -599,7 +747,8 @@ void WalkAST::checkUncheckedReturnValue(CallExpr *CE) { SourceRange R = CE->getCallee()->getSourceRange(); PathDiagnosticLocation CELoc = PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); - BR.EmitBasicReport(os1.str(), "Security", os2.str(), CELoc, &R, 1); + BR.EmitBasicReport(AC->getDecl(), os1.str(), "Security", os2.str(), + CELoc, &R, 1); } //===----------------------------------------------------------------------===// @@ -609,14 +758,29 @@ void WalkAST::checkUncheckedReturnValue(CallExpr *CE) { namespace { class SecuritySyntaxChecker : public Checker<check::ASTCodeBody> { public: + ChecksFilter filter; + void checkASTCodeBody(const Decl *D, AnalysisManager& mgr, BugReporter &BR) const { - WalkAST walker(BR, mgr.getAnalysisContext(D)); + WalkAST walker(BR, mgr.getAnalysisDeclContext(D), filter); walker.Visit(D->getBody()); } }; } -void ento::registerSecuritySyntaxChecker(CheckerManager &mgr) { - mgr.registerChecker<SecuritySyntaxChecker>(); +#define REGISTER_CHECKER(name) \ +void ento::register##name(CheckerManager &mgr) {\ + mgr.registerChecker<SecuritySyntaxChecker>()->filter.check_##name = true;\ } + +REGISTER_CHECKER(gets) +REGISTER_CHECKER(getpw) +REGISTER_CHECKER(mkstemp) +REGISTER_CHECKER(mktemp) +REGISTER_CHECKER(strcpy) +REGISTER_CHECKER(rand) +REGISTER_CHECKER(vfork) +REGISTER_CHECKER(FloatLoopCounter) +REGISTER_CHECKER(UncheckedReturn) + + diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CheckSizeofPointer.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CheckSizeofPointer.cpp index 469be05..cc7fd37 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CheckSizeofPointer.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CheckSizeofPointer.cpp @@ -24,10 +24,10 @@ using namespace ento; namespace { class WalkAST : public StmtVisitor<WalkAST> { BugReporter &BR; - AnalysisContext* AC; + AnalysisDeclContext* AC; public: - WalkAST(BugReporter &br, AnalysisContext* ac) : BR(br), AC(ac) {} + WalkAST(BugReporter &br, AnalysisDeclContext* ac) : BR(br), AC(ac) {} void VisitUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *E); void VisitStmt(Stmt *S) { VisitChildren(S); } void VisitChildren(Stmt *S); @@ -63,7 +63,8 @@ void WalkAST::VisitUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *E) { SourceRange R = ArgEx->getSourceRange(); PathDiagnosticLocation ELoc = PathDiagnosticLocation::createBegin(E, BR.getSourceManager(), AC); - BR.EmitBasicReport("Potential unintended use of sizeof() on pointer type", + BR.EmitBasicReport(AC->getDecl(), + "Potential unintended use of sizeof() on pointer type", "Logic", "The code calls sizeof() on a pointer type. " "This can produce an unexpected result.", @@ -80,7 +81,7 @@ class SizeofPointerChecker : public Checker<check::ASTCodeBody> { public: void checkASTCodeBody(const Decl *D, AnalysisManager& mgr, BugReporter &BR) const { - WalkAST walker(BR, mgr.getAnalysisContext(D)); + WalkAST walker(BR, mgr.getAnalysisDeclContext(D)); walker.Visit(D->getBody()); } }; diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CheckerDocumentation.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CheckerDocumentation.cpp new file mode 100644 index 0000000..843502f --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CheckerDocumentation.cpp @@ -0,0 +1,233 @@ +//= CheckerDocumentation.cpp - Documentation checker ---------------*- C++ -*-// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This checker lists all the checker callbacks and provides documentation for +// checker writers. +// +//===----------------------------------------------------------------------===// + +#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/BugType.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" + +using namespace clang; +using namespace ento; + +// All checkers should be placed into anonymous namespace. +// We place the CheckerDocumentation inside ento namespace to make the +// it visible in doxygen. +namespace ento { + +/// This checker documents the callback functions checkers can use to implement +/// the custom handling of the specific events during path exploration as well +/// as reporting bugs. Most of the callbacks are targeted at path-sensitive +/// checking. +/// +/// \sa CheckerContext +class CheckerDocumentation : public Checker< check::PreStmt<DeclStmt>, + check::PostStmt<CallExpr>, + check::PreObjCMessage, + check::PostObjCMessage, + check::BranchCondition, + check::Location, + check::Bind, + check::DeadSymbols, + check::EndPath, + check::EndAnalysis, + check::EndOfTranslationUnit, + eval::Call, + eval::Assume, + check::LiveSymbols, + check::RegionChanges, + check::Event<ImplicitNullDerefEvent>, + check::ASTDecl<FunctionDecl> > { +public: + + /// \brief Pre-visit the Statement. + /// + /// The method will be called before the analyzer core processes the + /// statement. The notification is performed for every explored CFGElement, + /// which does not include the control flow statements such as IfStmt. The + /// callback can be specialized to be called with any subclass of Stmt. + /// + /// See checkBranchCondition() callback for performing custom processing of + /// the branching statements. + /// + /// check::PreStmt<DeclStmt> + void checkPreStmt(const DeclStmt *DS, CheckerContext &C) const {} + + /// \brief Post-visit the Statement. + /// + /// The method will be called after the analyzer core processes the + /// statement. The notification is performed for every explored CFGElement, + /// which does not include the control flow statements such as IfStmt. The + /// callback can be specialized to be called with any subclass of Stmt. + /// + /// check::PostStmt<DeclStmt> + void checkPostStmt(const CallExpr *DS, CheckerContext &C) const; + + /// \brief Pre-visit the Objective C messages. + void checkPreObjCMessage(const ObjCMessage &Msg, CheckerContext &C) const {} + + /// \brief Post-visit the Objective C messages. + void checkPostObjCMessage(const ObjCMessage &Msg, CheckerContext &C) const {} + + /// \brief Pre-visit of the condition statement of a branch (such as IfStmt). + void checkBranchCondition(const Stmt *Condition, CheckerContext &Ctx) const {} + + /// \brief Called on a load from and a store to a location. + /// + /// The method will be called each time a location (pointer) value is + /// accessed. + /// \param Loc The value of the location (pointer). + /// \param IsLoad The flag specifying if the location is a store or a load. + /// \param S The load is performed while processing the statement. + /// + /// check::Location + void checkLocation(SVal Loc, bool IsLoad, const Stmt *S, + CheckerContext &C) const {} + + /// \brief Called on binding of a value to a location. + /// + /// \param Loc The value of the location (pointer). + /// \param Val The value which will be stored at the location Loc. + /// \param S The bind is performed while processing the statement S. + /// + /// check::Bind + void checkBind(SVal Loc, SVal Val, const Stmt *S, CheckerContext &C) const {} + + + /// \brief Called whenever a symbol becomes dead. + /// + /// This callback should be used by the checkers to aggressively clean + /// up/reduce the checker state, which is important for reducing the overall + /// memory usage. Specifically, if a checker keeps symbol specific information + /// in the sate, it can and should be dropped after the symbol becomes dead. + /// In addition, reporting a bug as soon as the checker becomes dead leads to + /// more precise diagnostics. (For example, one should report that a malloced + /// variable is not freed right after it goes out of scope.) + /// + /// \param SR The SymbolReaper object can be queried to determine which + /// symbols are dead. + /// + /// check::DeadSymbols + void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const {} + + /// \brief Called when an end of path is reached in the ExplodedGraph. + /// + /// This callback should be used to check if the allocated resources are freed. + /// + /// check::EndPath + void checkEndPath(CheckerContext &Ctx) const {} + + /// \brief Called after all the paths in the ExplodedGraph reach end of path + /// - the symbolic execution graph is fully explored. + /// + /// This callback should be used in cases when a checker needs to have a + /// global view of the information generated on all paths. For example, to + /// compare execution summary/result several paths. + /// See IdempotentOperationChecker for a usage example. + /// + /// check::EndAnalysis + void checkEndAnalysis(ExplodedGraph &G, + BugReporter &BR, + ExprEngine &Eng) const {} + + /// \brief Called after analysis of a TranslationUnit is complete. + /// + /// check::EndOfTranslationUnit + void checkEndOfTranslationUnit(const TranslationUnitDecl *TU, + AnalysisManager &Mgr, + BugReporter &BR) const {} + + + /// \brief Evaluates function call. + /// + /// The analysis core threats all function calls in the same way. However, some + /// functions have special meaning, which should be reflected in the program + /// state. This callback allows a checker to provide domain specific knowledge + /// about the particular functions it knows about. + /// + /// \returns true if the call has been successfully evaluated + /// and false otherwise. Note, that only one checker can evaluate a call. If + /// more then one checker claim that they can evaluate the same call the + /// first one wins. + /// + /// eval::Call + bool evalCall(const CallExpr *CE, CheckerContext &C) const { return true; } + + /// \brief Handles assumptions on symbolic values. + /// + /// This method is called when a symbolic expression is assumed to be true or + /// false. For example, the assumptions are performed when evaluating a + /// condition at a branch. The callback allows checkers track the assumptions + /// performed on the symbols of interest and change the state accordingly. + /// + /// eval::Assume + ProgramStateRef evalAssume(ProgramStateRef State, + SVal Cond, + bool Assumption) const { return State; } + + /// Allows modifying SymbolReaper object. For example, checkers can explicitly + /// register symbols of interest as live. These symbols will not be marked + /// dead and removed. + /// + /// check::LiveSymbols + void checkLiveSymbols(ProgramStateRef State, SymbolReaper &SR) const {} + + + bool wantsRegionChangeUpdate(ProgramStateRef St) const { return true; } + + /// check::RegionChanges + /// Allows tracking regions which get invalidated. + /// \param state The current program state. + /// \param invalidated A set of all symbols potentially touched by the change. + /// \param ExplicitRegions The regions explicitly requested for invalidation. + /// For example, in the case of a function call, these would be arguments. + /// \param Regions The transitive closure of accessible regions, + /// i.e. all regions that may have been touched by this change. + /// \param The call expression wrapper if the regions are invalidated by a + /// call, 0 otherwise. + /// Note, in order to be notified, the checker should also implement + /// wantsRegionChangeUpdate callback. + ProgramStateRef + checkRegionChanges(ProgramStateRef State, + const StoreManager::InvalidatedSymbols *, + ArrayRef<const MemRegion *> ExplicitRegions, + ArrayRef<const MemRegion *> Regions, + const CallOrObjCMessage *Call) const { + return State; + } + + /// check::Event<ImplicitNullDerefEvent> + void checkEvent(ImplicitNullDerefEvent Event) const {} + + /// \brief Check every declaration in the AST. + /// + /// An AST traversal callback, which should only be used when the checker is + /// not path sensitive. It will be called for every Declaration in the AST and + /// can be specialized to only be called on subclasses of Decl, for example, + /// FunctionDecl. + /// + /// check::ASTDecl<FunctionDecl> + void checkASTDecl(const FunctionDecl *D, + AnalysisManager &Mgr, + BugReporter &BR) const {} + +}; + +void CheckerDocumentation::checkPostStmt(const CallExpr *DS, + CheckerContext &C) const { + return; +} + +} // end namespace diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/Checkers.td b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/Checkers.td index d53e0b8..96a8d26 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/Checkers.td +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/Checkers.td @@ -27,16 +27,21 @@ def DeadCode : Package<"deadcode">; def DeadCodeExperimental : Package<"deadcode">, InPackage<Experimental>, Hidden; def Security : Package <"security">; +def InsecureAPI : Package<"insecureAPI">, InPackage<Security>; def SecurityExperimental : Package<"security">, InPackage<Experimental>, Hidden; +def Taint : Package<"taint">, InPackage<SecurityExperimental>, Hidden; def Unix : Package<"unix">; def UnixExperimental : Package<"unix">, InPackage<Experimental>, Hidden; +def CString : Package<"cstring">, InPackage<Unix>, Hidden; +def CStringExperimental : Package<"cstring">, InPackage<UnixExperimental>, Hidden; def OSX : Package<"osx">; def OSXExperimental : Package<"osx">, InPackage<Experimental>, Hidden; def Cocoa : Package<"cocoa">, InPackage<OSX>; def CocoaExperimental : Package<"cocoa">, InPackage<OSXExperimental>, Hidden; def CoreFoundation : Package<"coreFoundation">, InPackage<OSX>; +def Containers : Package<"containers">, InPackage<CoreFoundation>; def LLVM : Package<"llvm">; def Debug : Package<"debug">; @@ -83,6 +88,10 @@ def StackAddrEscapeChecker : Checker<"StackAddressEscape">, let ParentPackage = CoreExperimental in { +def BoolAssignmentChecker : Checker<"BoolAssignment">, + HelpText<"Warn about assigning non-{0,1} values to Boolean variables">, + DescFile<"BoolAssignmentChecker.cpp">; + def CastSizeChecker : Checker<"CastSize">, HelpText<"Check when casting a malloc'ed type T, whether the size is a multiple of the size of T">, DescFile<"CastSizeChecker.cpp">; @@ -163,6 +172,10 @@ def IteratorsChecker : Checker<"Iterators">, HelpText<"Check improper uses of STL vector iterators">, DescFile<"IteratorsChecker.cpp">; +def VirtualCallChecker : Checker<"VirtualCall">, + HelpText<"Check virtual function calls during construction or destruction">, + DescFile<"VirtualCallChecker.cpp">; + } // end: "cplusplus.experimental" //===----------------------------------------------------------------------===// @@ -174,15 +187,14 @@ let ParentPackage = DeadCode in { def DeadStoresChecker : Checker<"DeadStores">, HelpText<"Check for values stored to variables that are never read afterwards">, DescFile<"DeadStoresChecker.cpp">; +} // end DeadCode + +let ParentPackage = DeadCodeExperimental in { def IdempotentOperationChecker : Checker<"IdempotentOperations">, HelpText<"Warn about idempotent operations">, DescFile<"IdempotentOperationChecker.cpp">; -} // end DeadCode - -let ParentPackage = DeadCodeExperimental in { - def UnreachableCodeChecker : Checker<"UnreachableCode">, HelpText<"Check unreachable code">, DescFile<"UnreachableCodeChecker.cpp">; @@ -193,11 +205,39 @@ def UnreachableCodeChecker : Checker<"UnreachableCode">, // Security checkers. //===----------------------------------------------------------------------===// -let ParentPackage = SecurityExperimental in { +let ParentPackage = InsecureAPI in { + def gets : Checker<"gets">, + HelpText<"Warn on uses of the 'gets' function">, + DescFile<"CheckSecuritySyntaxOnly.cpp">; + def getpw : Checker<"getpw">, + HelpText<"Warn on uses of the 'getpw' function">, + DescFile<"CheckSecuritySyntaxOnly.cpp">; + def mktemp : Checker<"mktemp">, + HelpText<"Warn on uses of the 'mktemp' function">, + DescFile<"CheckSecuritySyntaxOnly.cpp">; + def mkstemp : Checker<"mkstemp">, + HelpText<"Warn when 'mkstemp' is passed fewer than 6 X's in the format string">, + DescFile<"CheckSecuritySyntaxOnly.cpp">; + def rand : Checker<"rand">, + HelpText<"Warn on uses of the 'rand', 'random', and related functions">, + DescFile<"CheckSecuritySyntaxOnly.cpp">; + def strcpy : Checker<"strcpy">, + HelpText<"Warn on uses of the 'strcpy' and 'strcat' functions">, + DescFile<"CheckSecuritySyntaxOnly.cpp">; + def vfork : Checker<"vfork">, + HelpText<"Warn on uses of the 'vfork' function">, + DescFile<"CheckSecuritySyntaxOnly.cpp">; + def UncheckedReturn : Checker<"UncheckedReturn">, + HelpText<"Warn on uses of functions whose return values must be always checked">, + DescFile<"CheckSecuritySyntaxOnly.cpp">; +} +let ParentPackage = Security in { + def FloatLoopCounter : Checker<"FloatLoopCounter">, + HelpText<"Warn on using a floating point value as a loop counter (CERT: FLP30-C, FLP30-CPP)">, + DescFile<"CheckSecuritySyntaxOnly.cpp">; +} -def SecuritySyntaxChecker : Checker<"SecuritySyntactic">, - HelpText<"Perform quick security API checks that require no data flow">, - DescFile<"CheckSecuritySyntaxOnly.cpp">; +let ParentPackage = SecurityExperimental in { def ArrayBoundChecker : Checker<"ArrayBound">, HelpText<"Warn about buffer overflows (older checker)">, @@ -218,6 +258,18 @@ def MallocOverflowSecurityChecker : Checker<"MallocOverflow">, } // end "security.experimental" //===----------------------------------------------------------------------===// +// Taint checkers. +//===----------------------------------------------------------------------===// + +let ParentPackage = Taint in { + +def GenericTaintChecker : Checker<"TaintPropagation">, + HelpText<"Generate taint information used by other checkers">, + DescFile<"GenericTaintChecker.cpp">; + +} // end "experimental.security.taint" + +//===----------------------------------------------------------------------===// // Unix API checkers. //===----------------------------------------------------------------------===// @@ -226,6 +278,10 @@ let ParentPackage = Unix in { def UnixAPIChecker : Checker<"API">, HelpText<"Check calls to various UNIX/Posix functions">, DescFile<"UnixAPIChecker.cpp">; + +def MallocPessimistic : Checker<"Malloc">, + HelpText<"Check for memory leaks, double free, and use-after-free problems.">, + DescFile<"MallocChecker.cpp">; } // end "unix" @@ -235,14 +291,14 @@ def ChrootChecker : Checker<"Chroot">, HelpText<"Check improper use of chroot">, DescFile<"ChrootChecker.cpp">; -def CStringChecker : Checker<"CString">, - HelpText<"Check calls to functions in <string.h>">, - DescFile<"CStringChecker.cpp">; - -def MallocChecker : Checker<"Malloc">, - HelpText<"Check for potential memory leaks, double free, and use-after-free problems">, +def MallocOptimistic : Checker<"MallocWithAnnotations">, + HelpText<"Check for memory leaks, double free, and use-after-free problems. Assumes that all user-defined functions which might free a pointer are annotated.">, DescFile<"MallocChecker.cpp">; +def MallocSizeofChecker : Checker<"MallocSizeof">, + HelpText<"Check for dubious malloc arguments involving sizeof">, + DescFile<"MallocSizeofChecker.cpp">; + def PthreadLockChecker : Checker<"PthreadLock">, HelpText<"Simple lock -> unlock checker">, DescFile<"PthreadLockChecker.cpp">; @@ -253,6 +309,32 @@ def StreamChecker : Checker<"Stream">, } // end "unix.experimental" +let ParentPackage = CString in { + +def CStringNullArg : Checker<"NullArg">, + HelpText<"Check for null pointers being passed as arguments to C string functions">, + DescFile<"CStringChecker.cpp">; + +def CStringSyntaxChecker : Checker<"BadSizeArg">, + HelpText<"Check the size argument passed into C string functions for common erroneous patterns">, + DescFile<"CStringSyntaxChecker.cpp">; +} + +let ParentPackage = CStringExperimental in { + +def CStringOutOfBounds : Checker<"OutOfBounds">, + HelpText<"Check for out-of-bounds access in string functions">, + DescFile<"CStringChecker.cpp">; + +def CStringBufferOverlap : Checker<"BufferOverlap">, + HelpText<"Checks for overlap in two buffer arguments">, + DescFile<"CStringChecker.cpp">; + +def CStringNotNullTerm : Checker<"NotNullTerminated">, + HelpText<"Check for arguments which are not null-terminating strings">, + DescFile<"CStringChecker.cpp">; +} + //===----------------------------------------------------------------------===// // Mac OS X, Cocoa, and Core Foundation checkers. //===----------------------------------------------------------------------===// @@ -291,7 +373,7 @@ def ClassReleaseChecker : Checker<"ClassRelease">, DescFile<"BasicObjCFoundationChecks.cpp">; def VariadicMethodTypeChecker : Checker<"VariadicMethodTypes">, - HelpText<"Check for passing non-Objective-C types to variadic methods that expect" + HelpText<"Check for passing non-Objective-C types to variadic methods that expect " "only Objective-C types">, DescFile<"BasicObjCFoundationChecks.cpp">; @@ -306,7 +388,11 @@ def ObjCMethSigsChecker : Checker<"IncompatibleMethodTypes">, def ObjCUnusedIvarsChecker : Checker<"UnusedIvars">, HelpText<"Warn about private ivars that are never used">, DescFile<"ObjCUnusedIVarsChecker.cpp">; - + +def ObjCSelfInitChecker : Checker<"SelfInit">, + HelpText<"Check that 'self' is properly initialized inside an initializer method">, + DescFile<"ObjCSelfInitChecker.cpp">; + def NSErrorChecker : Checker<"NSError">, HelpText<"Check usage of NSError** parameters">, DescFile<"NSErrorChecker.cpp">; @@ -319,10 +405,6 @@ def RetainCountChecker : Checker<"RetainCount">, let ParentPackage = CocoaExperimental in { -def ObjCSelfInitChecker : Checker<"SelfInit">, - HelpText<"Check that 'self' is properly initialized inside an initializer method">, - DescFile<"ObjCSelfInitChecker.cpp">; - def ObjCDeallocChecker : Checker<"Dealloc">, HelpText<"Warn about Objective-C classes that lack a correct implementation of -dealloc">, DescFile<"CheckObjCDealloc.cpp">; @@ -344,6 +426,16 @@ def CFErrorChecker : Checker<"CFError">, DescFile<"NSErrorChecker.cpp">; } +let ParentPackage = Containers in { +def ObjCContainersASTChecker : Checker<"PointerSizedValues">, + HelpText<"Warns if 'CFArray', 'CFDictionary', 'CFSet' are created with non-pointer-size values">, + DescFile<"ObjCContainersASTChecker.cpp">; + +def ObjCContainersChecker : Checker<"OutOfBounds">, + HelpText<"Checks for index out-of-bounds when using 'CFArray' API">, + DescFile<"ObjCContainersChecker.cpp">; + +} //===----------------------------------------------------------------------===// // Checkers for LLVM development. //===----------------------------------------------------------------------===// @@ -359,6 +451,10 @@ def LLVMConventionsChecker : Checker<"Conventions">, let ParentPackage = Debug in { +def DominatorsTreeDumper : Checker<"DumpDominators">, + HelpText<"Print the dominance tree for a given CFG">, + DescFile<"DebugCheckers.cpp">; + def LiveVariablesDumper : Checker<"DumpLiveVars">, HelpText<"Print results of live variable analysis">, DescFile<"DebugCheckers.cpp">; @@ -371,9 +467,21 @@ def CFGDumper : Checker<"DumpCFG">, HelpText<"Display Control-Flow Graphs">, DescFile<"DebugCheckers.cpp">; +def CallGraphViewer : Checker<"ViewCallGraph">, + HelpText<"View Call Graph using GraphViz">, + DescFile<"DebugCheckers.cpp">; + +def CallGraphDumper : Checker<"DumpCallGraph">, + HelpText<"Display Call Graph">, + DescFile<"DebugCheckers.cpp">; + def AnalyzerStatsChecker : Checker<"Stats">, HelpText<"Emit warnings with analyzer statistics">, DescFile<"AnalyzerStatsChecker.cpp">; +def TaintTesterChecker : Checker<"TaintTest">, + HelpText<"Mark tainted symbols as such.">, + DescFile<"TaintTesterChecker.cpp">; + } // end "debug" diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ChrootChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ChrootChecker.cpp index 3c92381..30d0609 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ChrootChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ChrootChecker.cpp @@ -41,7 +41,7 @@ bool isRootChanged(intptr_t k) { return k == ROOT_CHANGED; } class ChrootChecker : public Checker<eval::Call, check::PreStmt<CallExpr> > { mutable IdentifierInfo *II_chroot, *II_chdir; // This bug refers to possibly break out of a chroot() jail. - mutable llvm::OwningPtr<BuiltinBug> BT_BreakJail; + mutable OwningPtr<BuiltinBug> BT_BreakJail; public: ChrootChecker() : II_chroot(0), II_chdir(0) {} @@ -62,10 +62,7 @@ private: } // end anonymous namespace bool ChrootChecker::evalCall(const CallExpr *CE, CheckerContext &C) const { - const ProgramState *state = C.getState(); - const Expr *Callee = CE->getCallee(); - SVal L = state->getSVal(Callee); - const FunctionDecl *FD = L.getAsFunctionDecl(); + const FunctionDecl *FD = C.getCalleeDecl(CE); if (!FD) return false; @@ -88,7 +85,7 @@ bool ChrootChecker::evalCall(const CallExpr *CE, CheckerContext &C) const { } void ChrootChecker::Chroot(CheckerContext &C, const CallExpr *CE) const { - const ProgramState *state = C.getState(); + ProgramStateRef state = C.getState(); ProgramStateManager &Mgr = state->getStateManager(); // Once encouter a chroot(), set the enum value ROOT_CHANGED directly in @@ -98,7 +95,7 @@ void ChrootChecker::Chroot(CheckerContext &C, const CallExpr *CE) const { } void ChrootChecker::Chdir(CheckerContext &C, const CallExpr *CE) const { - const ProgramState *state = C.getState(); + ProgramStateRef state = C.getState(); ProgramStateManager &Mgr = state->getStateManager(); // If there are no jail state in the GDM, just return. @@ -108,7 +105,7 @@ void ChrootChecker::Chdir(CheckerContext &C, const CallExpr *CE) const { // After chdir("/"), enter the jail, set the enum value JAIL_ENTERED. const Expr *ArgExpr = CE->getArg(0); - SVal ArgVal = state->getSVal(ArgExpr); + SVal ArgVal = state->getSVal(ArgExpr, C.getLocationContext()); if (const MemRegion *R = ArgVal.getAsRegion()) { R = R->StripCasts(); @@ -125,10 +122,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 ProgramState *state = C.getState(); - const Expr *Callee = CE->getCallee(); - SVal L = state->getSVal(Callee); - const FunctionDecl *FD = L.getAsFunctionDecl(); + const FunctionDecl *FD = C.getCalleeDecl(CE); if (!FD) return; @@ -143,10 +137,10 @@ 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 = C.getState()->FindGDM(ChrootChecker::getTag()); if (k) if (isRootChanged((intptr_t) *k)) - if (ExplodedNode *N = C.generateNode()) { + if (ExplodedNode *N = C.addTransition()) { if (!BT_BreakJail) BT_BreakJail.reset(new BuiltinBug("Break out of jail", "No call of chdir(\"/\") immediately " diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ClangSACheckers.h b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ClangSACheckers.h index 289ce8d..230baa7 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ClangSACheckers.h +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ClangSACheckers.h @@ -12,6 +12,8 @@ // //===----------------------------------------------------------------------===// +#include "clang/StaticAnalyzer/Checkers/CommonBugCategories.h" + #ifndef LLVM_CLANG_SA_LIB_CHECKERS_CLANGSACHECKERS_H #define LLVM_CLANG_SA_LIB_CHECKERS_CLANGSACHECKERS_H diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CommonBugCategories.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CommonBugCategories.cpp new file mode 100644 index 0000000..e2a8ea6 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CommonBugCategories.cpp @@ -0,0 +1,18 @@ +//=--- CommonBugCategories.cpp - Provides common issue categories -*- C++ -*-=// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// Common strings used for the "category" of many static analyzer issues. +namespace clang { namespace ento { namespace categories { + +const char *CoreFoundationObjectiveC = "Core Foundation/Objective-C"; +const char *MemoryCoreFoundationObjectiveC = + "Memory (Core Foundation/Objective-C)"; +const char *UnixAPI = "Unix API"; +}}} + diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp index 901af43..510e8cd 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp @@ -23,6 +23,7 @@ #include "clang/AST/ASTContext.h" #include "clang/AST/ParentMap.h" #include "llvm/ADT/SmallPtrSet.h" +#include "llvm/ADT/SmallString.h" using namespace clang; using namespace ento; @@ -67,22 +68,37 @@ void ReachableCode::computeReachableBlocks() { } } +static const Expr *LookThroughTransitiveAssignments(const Expr *Ex) { + while (Ex) { + const BinaryOperator *BO = + dyn_cast<BinaryOperator>(Ex->IgnoreParenCasts()); + if (!BO) + break; + if (BO->getOpcode() == BO_Assign) { + Ex = BO->getRHS(); + continue; + } + break; + } + return Ex; +} + namespace { class DeadStoreObs : public LiveVariables::Observer { const CFG &cfg; ASTContext &Ctx; BugReporter& BR; - AnalysisContext* AC; + AnalysisDeclContext* AC; ParentMap& Parents; llvm::SmallPtrSet<const VarDecl*, 20> Escaped; - llvm::OwningPtr<ReachableCode> reachableCode; + OwningPtr<ReachableCode> reachableCode; const CFGBlock *currentBlock; enum DeadStoreKind { Standard, Enclosing, DeadIncrement, DeadInit }; public: DeadStoreObs(const CFG &cfg, ASTContext &ctx, - BugReporter& br, AnalysisContext* ac, ParentMap& parents, + BugReporter& br, AnalysisDeclContext* ac, ParentMap& parents, llvm::SmallPtrSet<const VarDecl*, 20> &escaped) : cfg(cfg), Ctx(ctx), BR(br), AC(ac), Parents(parents), Escaped(escaped), currentBlock(0) {} @@ -104,14 +120,11 @@ public: if (!reachableCode->isReachable(currentBlock)) return; - llvm::SmallString<64> buf; + SmallString<64> buf; llvm::raw_svector_ostream os(buf); const char *BugType = 0; switch (dsk) { - default: - llvm_unreachable("Impossible dead store type."); - case DeadInit: BugType = "Dead initialization"; os << "Value stored to '" << *V @@ -132,7 +145,7 @@ public: return; } - BR.EmitBasicReport(BugType, "Dead store", os.str(), L, R); + BR.EmitBasicReport(AC->getDecl(), BugType, "Dead store", os.str(), L, R); } void CheckVarDecl(const VarDecl *VD, const Expr *Ex, const Expr *Val, @@ -202,17 +215,18 @@ public: 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. + const Expr *RHS = LookThroughTransitiveAssignments(B->getRHS()); + QualType T = VD->getType(); if (T->isPointerType() || T->isObjCObjectPointerType()) { - if (B->getRHS()->isNullPointerConstant(Ctx, - Expr::NPC_ValueDependentIsNull)) + if (RHS->isNullPointerConstant(Ctx, Expr::NPC_ValueDependentIsNull)) return; } - Expr *RHS = B->getRHS()->IgnoreParenCasts(); + RHS = RHS->IgnoreParenCasts(); // Special case: self-assignments. These are often used to shut up // "unused variable" compiler warnings. - if (DeclRefExpr *RhsDR = dyn_cast<DeclRefExpr>(RHS)) + if (const DeclRefExpr *RhsDR = dyn_cast<DeclRefExpr>(RHS)) if (VD == dyn_cast<VarDecl>(RhsDR->getDecl())) return; @@ -254,10 +268,15 @@ public: if (V->getType()->getAs<ReferenceType>()) return; - if (Expr *E = V->getInit()) { - while (ExprWithCleanups *exprClean = dyn_cast<ExprWithCleanups>(E)) + if (const Expr *E = V->getInit()) { + while (const ExprWithCleanups *exprClean = + dyn_cast<ExprWithCleanups>(E)) E = exprClean->getSubExpr(); + // Look through transitive assignments, e.g.: + // int x = y = 0; + E = LookThroughTransitiveAssignments(E); + // Don't warn on C++ objects (yet) until we can show that their // constructors/destructors don't have side effects. if (isa<CXXConstructExpr>(E)) @@ -274,11 +293,12 @@ public: // If x is EVER assigned a new value later, don't issue // a warning. This is because such initialization can be // due to defensive programming. - if (E->isConstantInitializer(Ctx, false)) + if (E->isEvaluatable(Ctx)) return; - if (DeclRefExpr *DRE=dyn_cast<DeclRefExpr>(E->IgnoreParenCasts())) - if (VarDecl *VD = dyn_cast<VarDecl>(DRE->getDecl())) { + if (const DeclRefExpr *DRE = + dyn_cast<DeclRefExpr>(E->IgnoreParenCasts())) + if (const VarDecl *VD = dyn_cast<VarDecl>(DRE->getDecl())) { // Special case: check for initialization from constant // variables. // @@ -350,7 +370,7 @@ public: BugReporter &BR) const { if (LiveVariables *L = mgr.getAnalysis<LiveVariables>(D)) { CFG &cfg = *mgr.getCFG(D); - AnalysisContext *AC = mgr.getAnalysisContext(D); + AnalysisDeclContext *AC = mgr.getAnalysisDeclContext(D); ParentMap &pmap = mgr.getParentMap(D); FindEscaped FS(&cfg); FS.getCFG().VisitBlockStmts(FS); diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DebugCheckers.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DebugCheckers.cpp index d9d5694..34053cd 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DebugCheckers.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DebugCheckers.cpp @@ -15,11 +15,36 @@ #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" #include "clang/Analysis/Analyses/LiveVariables.h" +#include "clang/Analysis/Analyses/Dominators.h" +#include "clang/Analysis/CallGraph.h" +#include "llvm/Support/Process.h" using namespace clang; using namespace ento; //===----------------------------------------------------------------------===// +// DominatorsTreeDumper +//===----------------------------------------------------------------------===// + +namespace { +class DominatorsTreeDumper : public Checker<check::ASTCodeBody> { +public: + void checkASTCodeBody(const Decl *D, AnalysisManager& mgr, + BugReporter &BR) const { + if (AnalysisDeclContext *AC = mgr.getAnalysisDeclContext(D)) { + DominatorTree dom; + dom.buildDominatorTree(*AC); + dom.dump(); + } + } +}; +} + +void ento::registerDominatorsTreeDumper(CheckerManager &mgr) { + mgr.registerChecker<DominatorsTreeDumper>(); +} + +//===----------------------------------------------------------------------===// // LiveVariablesDumper //===----------------------------------------------------------------------===// @@ -49,7 +74,7 @@ public: void checkASTCodeBody(const Decl *D, AnalysisManager& mgr, BugReporter &BR) const { if (CFG *cfg = mgr.getCFG(D)) { - cfg->viewCFG(mgr.getLangOptions()); + cfg->viewCFG(mgr.getLangOpts()); } } }; @@ -69,7 +94,8 @@ public: void checkASTCodeBody(const Decl *D, AnalysisManager& mgr, BugReporter &BR) const { if (CFG *cfg = mgr.getCFG(D)) { - cfg->dump(mgr.getLangOptions()); + cfg->dump(mgr.getLangOpts(), + llvm::sys::Process::StandardErrHasColors()); } } }; @@ -78,3 +104,43 @@ public: void ento::registerCFGDumper(CheckerManager &mgr) { mgr.registerChecker<CFGDumper>(); } + +//===----------------------------------------------------------------------===// +// CallGraphViewer +//===----------------------------------------------------------------------===// + +namespace { +class CallGraphViewer : public Checker< check::ASTDecl<TranslationUnitDecl> > { +public: + void checkASTDecl(const TranslationUnitDecl *TU, AnalysisManager& mgr, + BugReporter &BR) const { + CallGraph CG; + CG.addToCallGraph(const_cast<TranslationUnitDecl*>(TU)); + CG.viewGraph(); + } +}; +} + +void ento::registerCallGraphViewer(CheckerManager &mgr) { + mgr.registerChecker<CallGraphViewer>(); +} + +//===----------------------------------------------------------------------===// +// CallGraphDumper +//===----------------------------------------------------------------------===// + +namespace { +class CallGraphDumper : public Checker< check::ASTDecl<TranslationUnitDecl> > { +public: + void checkASTDecl(const TranslationUnitDecl *TU, AnalysisManager& mgr, + BugReporter &BR) const { + CallGraph CG; + CG.addToCallGraph(const_cast<TranslationUnitDecl*>(TU)); + CG.dump(); + } +}; +} + +void ento::registerCallGraphDumper(CheckerManager &mgr) { + mgr.registerChecker<CallGraphDumper>(); +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp index eeda734..81a2745 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp @@ -13,10 +13,12 @@ //===----------------------------------------------------------------------===// #include "ClangSACheckers.h" +#include "clang/AST/ExprObjC.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/BugType.h" +#include "llvm/ADT/SmallString.h" using namespace clang; using namespace ento; @@ -25,35 +27,42 @@ namespace { class DereferenceChecker : public Checker< check::Location, EventDispatcher<ImplicitNullDerefEvent> > { - mutable llvm::OwningPtr<BuiltinBug> BT_null; - mutable llvm::OwningPtr<BuiltinBug> BT_undef; + mutable OwningPtr<BuiltinBug> BT_null; + mutable OwningPtr<BuiltinBug> BT_undef; public: void checkLocation(SVal location, bool isLoad, const Stmt* S, CheckerContext &C) const; - static void AddDerefSource(raw_ostream &os, + static const MemRegion *AddDerefSource(raw_ostream &os, SmallVectorImpl<SourceRange> &Ranges, - const Expr *Ex, bool loadedFrom = false); + const Expr *Ex, const ProgramState *state, + const LocationContext *LCtx, + bool loadedFrom = false); }; } // end anonymous namespace -void DereferenceChecker::AddDerefSource(raw_ostream &os, - SmallVectorImpl<SourceRange> &Ranges, - const Expr *Ex, - bool loadedFrom) { +const MemRegion * +DereferenceChecker::AddDerefSource(raw_ostream &os, + SmallVectorImpl<SourceRange> &Ranges, + const Expr *Ex, + const ProgramState *state, + const LocationContext *LCtx, + bool loadedFrom) { Ex = Ex->IgnoreParenLValueCasts(); + const MemRegion *sourceR = 0; switch (Ex->getStmtClass()) { default: - return; + break; case Stmt::DeclRefExprClass: { const DeclRefExpr *DR = cast<DeclRefExpr>(Ex); if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) { os << " (" << (loadedFrom ? "loaded from" : "from") << " variable '" << VD->getName() << "')"; Ranges.push_back(DR->getSourceRange()); + sourceR = state->getLValue(VD, LCtx).getAsRegion(); } - return; + break; } case Stmt::MemberExprClass: { const MemberExpr *ME = cast<MemberExpr>(Ex); @@ -64,6 +73,7 @@ void DereferenceChecker::AddDerefSource(raw_ostream &os, break; } } + return sourceR; } void DereferenceChecker::checkLocation(SVal l, bool isLoad, const Stmt* S, @@ -77,7 +87,7 @@ void DereferenceChecker::checkLocation(SVal l, bool isLoad, const Stmt* S, BugReport *report = new BugReport(*BT_undef, BT_undef->getDescription(), N); report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, - bugreporter::GetDerefExpr(N))); + bugreporter::GetDerefExpr(N), report)); C.EmitReport(report); } return; @@ -89,8 +99,9 @@ void DereferenceChecker::checkLocation(SVal l, bool isLoad, const Stmt* S, if (!isa<Loc>(location)) return; - const ProgramState *state = C.getState(); - const ProgramState *notNullState, *nullState; + ProgramStateRef state = C.getState(); + const LocationContext *LCtx = C.getLocationContext(); + ProgramStateRef notNullState, nullState; llvm::tie(notNullState, nullState) = state->assume(location); // The explicit NULL case. @@ -106,20 +117,24 @@ void DereferenceChecker::checkLocation(SVal l, bool isLoad, const Stmt* S, if (!BT_null) BT_null.reset(new BuiltinBug("Dereference of null pointer")); - llvm::SmallString<100> buf; + SmallString<100> buf; SmallVector<SourceRange, 2> Ranges; // Walk through lvalue casts to get the original expression // that syntactically caused the load. if (const Expr *expr = dyn_cast<Expr>(S)) S = expr->IgnoreParenLValueCasts(); + + const MemRegion *sourceR = 0; switch (S->getStmtClass()) { case Stmt::ArraySubscriptExprClass: { llvm::raw_svector_ostream os(buf); os << "Array access"; const ArraySubscriptExpr *AE = cast<ArraySubscriptExpr>(S); - AddDerefSource(os, Ranges, AE->getBase()->IgnoreParenCasts()); + sourceR = + AddDerefSource(os, Ranges, AE->getBase()->IgnoreParenCasts(), + state.getPtr(), LCtx); os << " results in a null pointer dereference"; break; } @@ -127,7 +142,9 @@ void DereferenceChecker::checkLocation(SVal l, bool isLoad, const Stmt* S, llvm::raw_svector_ostream os(buf); os << "Dereference of null pointer"; const UnaryOperator *U = cast<UnaryOperator>(S); - AddDerefSource(os, Ranges, U->getSubExpr()->IgnoreParens(), true); + sourceR = + AddDerefSource(os, Ranges, U->getSubExpr()->IgnoreParens(), + state.getPtr(), LCtx, true); break; } case Stmt::MemberExprClass: { @@ -136,7 +153,9 @@ void DereferenceChecker::checkLocation(SVal l, bool isLoad, const Stmt* S, llvm::raw_svector_ostream os(buf); os << "Access to field '" << M->getMemberNameInfo() << "' results in a dereference of a null pointer"; - AddDerefSource(os, Ranges, M->getBase()->IgnoreParenCasts(), true); + sourceR = + AddDerefSource(os, Ranges, M->getBase()->IgnoreParenCasts(), + state.getPtr(), LCtx, true); } break; } @@ -163,12 +182,17 @@ void DereferenceChecker::checkLocation(SVal l, bool isLoad, const Stmt* S, N); report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, - bugreporter::GetDerefExpr(N))); + bugreporter::GetDerefExpr(N), report)); for (SmallVectorImpl<SourceRange>::iterator I = Ranges.begin(), E = Ranges.end(); I!=E; ++I) report->addRange(*I); + if (sourceR) { + report->markInteresting(sourceR); + report->markInteresting(state->getRawSVal(loc::MemRegionVal(sourceR))); + } + C.EmitReport(report); return; } diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DivZeroChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DivZeroChecker.cpp index 75b7cc4..2627f0c 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DivZeroChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DivZeroChecker.cpp @@ -23,12 +23,31 @@ using namespace ento; namespace { class DivZeroChecker : public Checker< check::PreStmt<BinaryOperator> > { - mutable llvm::OwningPtr<BuiltinBug> BT; + mutable OwningPtr<BuiltinBug> BT; + void reportBug(const char *Msg, + ProgramStateRef StateZero, + CheckerContext &C) const ; public: void checkPreStmt(const BinaryOperator *B, CheckerContext &C) const; }; } // end anonymous namespace +void DivZeroChecker::reportBug(const char *Msg, + ProgramStateRef StateZero, + CheckerContext &C) const { + if (ExplodedNode *N = C.generateSink(StateZero)) { + if (!BT) + BT.reset(new BuiltinBug("Division by zero")); + + BugReport *R = + new BugReport(*BT, Msg, N); + + R->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, + bugreporter::GetDenomExpr(N), R)); + C.EmitReport(R); + } +} + void DivZeroChecker::checkPreStmt(const BinaryOperator *B, CheckerContext &C) const { BinaryOperator::Opcode Op = B->getOpcode(); @@ -42,7 +61,7 @@ void DivZeroChecker::checkPreStmt(const BinaryOperator *B, !B->getRHS()->getType()->isScalarType()) return; - SVal Denom = C.getState()->getSVal(B->getRHS()); + SVal Denom = C.getState()->getSVal(B->getRHS(), C.getLocationContext()); const DefinedSVal *DV = dyn_cast<DefinedSVal>(&Denom); // Divide-by-undefined handled in the generic checking for uses of @@ -52,22 +71,18 @@ void DivZeroChecker::checkPreStmt(const BinaryOperator *B, // Check for divide by zero. ConstraintManager &CM = C.getConstraintManager(); - const ProgramState *stateNotZero, *stateZero; + ProgramStateRef stateNotZero, stateZero; llvm::tie(stateNotZero, stateZero) = CM.assumeDual(C.getState(), *DV); - if (stateZero && !stateNotZero) { - if (ExplodedNode *N = C.generateSink(stateZero)) { - if (!BT) - BT.reset(new BuiltinBug("Division by zero")); - - BugReport *R = - new BugReport(*BT, BT->getDescription(), N); - - R->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, - bugreporter::GetDenomExpr(N))); + if (!stateNotZero) { + assert(stateZero); + reportBug("Division by zero", stateZero, C); + return; + } - C.EmitReport(R); - } + bool TaintedD = C.getState()->isTainted(*DV); + if ((stateNotZero && stateZero && TaintedD)) { + reportBug("Division by a tainted value, possibly zero", stateZero, C); return; } diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/FixedAddressChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/FixedAddressChecker.cpp index 531d87e..a1f2f3b 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/FixedAddressChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/FixedAddressChecker.cpp @@ -25,7 +25,7 @@ using namespace ento; namespace { class FixedAddressChecker : public Checker< check::PreStmt<BinaryOperator> > { - mutable llvm::OwningPtr<BuiltinBug> BT; + mutable OwningPtr<BuiltinBug> BT; public: void checkPreStmt(const BinaryOperator *B, CheckerContext &C) const; @@ -44,14 +44,13 @@ void FixedAddressChecker::checkPreStmt(const BinaryOperator *B, if (!T->isPointerType()) return; - const ProgramState *state = C.getState(); - - SVal RV = state->getSVal(B->getRHS()); + ProgramStateRef state = C.getState(); + SVal RV = state->getSVal(B->getRHS(), C.getLocationContext()); if (!RV.isConstant() || RV.isZeroConstant()) return; - if (ExplodedNode *N = C.generateNode()) { + if (ExplodedNode *N = C.addTransition()) { if (!BT) BT.reset(new BuiltinBug("Use fixed address", "Using a fixed address is not portable because that " diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp new file mode 100644 index 0000000..135b81d --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp @@ -0,0 +1,740 @@ +//== GenericTaintChecker.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 defines the attack surface for generic taint propagation. +// +// The taint information produced by it might be useful to other checkers. For +// example, checkers should report errors which involve tainted data more +// aggressively, even if the involved symbols are under constrained. +// +//===----------------------------------------------------------------------===// +#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/PathSensitive/ProgramStateTrait.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/Basic/Builtins.h" +#include <climits> + +using namespace clang; +using namespace ento; + +namespace { +class GenericTaintChecker : public Checker< check::PostStmt<CallExpr>, + check::PreStmt<CallExpr> > { +public: + static void *getTag() { static int Tag; return &Tag; } + + void checkPostStmt(const CallExpr *CE, CheckerContext &C) const; + void checkPostStmt(const DeclRefExpr *DRE, CheckerContext &C) const; + + void checkPreStmt(const CallExpr *CE, CheckerContext &C) const; + +private: + static const unsigned InvalidArgIndex = UINT_MAX; + /// Denotes the return vale. + static const unsigned ReturnValueIndex = UINT_MAX - 1; + + mutable OwningPtr<BugType> BT; + inline void initBugType() const { + if (!BT) + BT.reset(new BugType("Use of Untrusted Data", "Untrusted Data")); + } + + /// \brief Catch taint related bugs. Check if tainted data is passed to a + /// system call etc. + bool checkPre(const CallExpr *CE, CheckerContext &C) const; + + /// \brief Add taint sources on a pre-visit. + void addSourcesPre(const CallExpr *CE, CheckerContext &C) const; + + /// \brief Propagate taint generated at pre-visit. + bool propagateFromPre(const CallExpr *CE, CheckerContext &C) const; + + /// \brief Add taint sources on a post visit. + void addSourcesPost(const CallExpr *CE, CheckerContext &C) const; + + /// Check if the region the expression evaluates to is the standard input, + /// and thus, is tainted. + static bool isStdin(const Expr *E, CheckerContext &C); + + /// \brief Given a pointer argument, get the symbol of the value it contains + /// (points to). + static SymbolRef getPointedToSymbol(CheckerContext &C, const Expr *Arg); + + /// Functions defining the attack surface. + typedef ProgramStateRef (GenericTaintChecker::*FnCheck)(const CallExpr *, + CheckerContext &C) const; + ProgramStateRef postScanf(const CallExpr *CE, CheckerContext &C) const; + ProgramStateRef postSocket(const CallExpr *CE, CheckerContext &C) const; + ProgramStateRef postRetTaint(const CallExpr *CE, CheckerContext &C) const; + + /// Taint the scanned input if the file is tainted. + ProgramStateRef preFscanf(const CallExpr *CE, CheckerContext &C) const; + + /// Check for CWE-134: Uncontrolled Format String. + static const char MsgUncontrolledFormatString[]; + bool checkUncontrolledFormatString(const CallExpr *CE, + CheckerContext &C) const; + + /// Check for: + /// CERT/STR02-C. "Sanitize data passed to complex subsystems" + /// CWE-78, "Failure to Sanitize Data into an OS Command" + static const char MsgSanitizeSystemArgs[]; + bool checkSystemCall(const CallExpr *CE, StringRef Name, + CheckerContext &C) const; + + /// Check if tainted data is used as a buffer size ins strn.. functions, + /// and allocators. + static const char MsgTaintedBufferSize[]; + bool checkTaintedBufferSize(const CallExpr *CE, const FunctionDecl *FDecl, + CheckerContext &C) const; + + /// Generate a report if the expression is tainted or points to tainted data. + bool generateReportIfTainted(const Expr *E, const char Msg[], + CheckerContext &C) const; + + + typedef llvm::SmallVector<unsigned, 2> ArgVector; + + /// \brief A struct used to specify taint propagation rules for a function. + /// + /// If any of the possible taint source arguments is tainted, all of the + /// destination arguments should also be tainted. Use InvalidArgIndex in the + /// src list to specify that all of the arguments can introduce taint. Use + /// InvalidArgIndex in the dst arguments to signify that all the non-const + /// pointer and reference arguments might be tainted on return. If + /// ReturnValueIndex is added to the dst list, the return value will be + /// tainted. + struct TaintPropagationRule { + /// List of arguments which can be taint sources and should be checked. + ArgVector SrcArgs; + /// List of arguments which should be tainted on function return. + ArgVector DstArgs; + // TODO: Check if using other data structures would be more optimal. + + TaintPropagationRule() {} + + TaintPropagationRule(unsigned SArg, + unsigned DArg, bool TaintRet = false) { + SrcArgs.push_back(SArg); + DstArgs.push_back(DArg); + if (TaintRet) + DstArgs.push_back(ReturnValueIndex); + } + + TaintPropagationRule(unsigned SArg1, unsigned SArg2, + unsigned DArg, bool TaintRet = false) { + SrcArgs.push_back(SArg1); + SrcArgs.push_back(SArg2); + DstArgs.push_back(DArg); + if (TaintRet) + DstArgs.push_back(ReturnValueIndex); + } + + /// Get the propagation rule for a given function. + static TaintPropagationRule + getTaintPropagationRule(const FunctionDecl *FDecl, + StringRef Name, + CheckerContext &C); + + inline void addSrcArg(unsigned A) { SrcArgs.push_back(A); } + inline void addDstArg(unsigned A) { DstArgs.push_back(A); } + + inline bool isNull() const { return SrcArgs.empty(); } + + inline bool isDestinationArgument(unsigned ArgNum) const { + return (std::find(DstArgs.begin(), + DstArgs.end(), ArgNum) != DstArgs.end()); + } + + static inline bool isTaintedOrPointsToTainted(const Expr *E, + ProgramStateRef State, + CheckerContext &C) { + return (State->isTainted(E, C.getLocationContext()) || isStdin(E, C) || + (E->getType().getTypePtr()->isPointerType() && + State->isTainted(getPointedToSymbol(C, E)))); + } + + /// \brief Pre-process a function which propagates taint according to the + /// taint rule. + ProgramStateRef process(const CallExpr *CE, CheckerContext &C) const; + + }; +}; + +const unsigned GenericTaintChecker::ReturnValueIndex; +const unsigned GenericTaintChecker::InvalidArgIndex; + +const char GenericTaintChecker::MsgUncontrolledFormatString[] = + "Untrusted data is used as a format string " + "(CWE-134: Uncontrolled Format String)"; + +const char GenericTaintChecker::MsgSanitizeSystemArgs[] = + "Untrusted data is passed to a system call " + "(CERT/STR02-C. Sanitize data passed to complex subsystems)"; + +const char GenericTaintChecker::MsgTaintedBufferSize[] = + "Untrusted data is used to specify the buffer size " + "(CERT/STR31-C. Guarantee that storage for strings has sufficient space for " + "character data and the null terminator)"; + +} // end of anonymous namespace + +/// A set which is used to pass information from call pre-visit instruction +/// to the call post-visit. The values are unsigned integers, which are either +/// ReturnValueIndex, or indexes of the pointer/reference argument, which +/// points to data, which should be tainted on return. +namespace { struct TaintArgsOnPostVisit{}; } +namespace clang { namespace ento { +template<> struct ProgramStateTrait<TaintArgsOnPostVisit> + : public ProgramStatePartialTrait<llvm::ImmutableSet<unsigned> > { + static void *GDMIndex() { return GenericTaintChecker::getTag(); } +}; +}} + +GenericTaintChecker::TaintPropagationRule +GenericTaintChecker::TaintPropagationRule::getTaintPropagationRule( + const FunctionDecl *FDecl, + StringRef Name, + CheckerContext &C) { + // TODO: Currently, we might loose precision here: we always mark a return + // value as tainted even if it's just a pointer, pointing to tainted data. + + // Check for exact name match for functions without builtin substitutes. + TaintPropagationRule Rule = llvm::StringSwitch<TaintPropagationRule>(Name) + .Case("atoi", TaintPropagationRule(0, ReturnValueIndex)) + .Case("atol", TaintPropagationRule(0, ReturnValueIndex)) + .Case("atoll", TaintPropagationRule(0, ReturnValueIndex)) + .Case("getc", TaintPropagationRule(0, ReturnValueIndex)) + .Case("fgetc", TaintPropagationRule(0, ReturnValueIndex)) + .Case("getc_unlocked", TaintPropagationRule(0, ReturnValueIndex)) + .Case("getw", TaintPropagationRule(0, ReturnValueIndex)) + .Case("toupper", TaintPropagationRule(0, ReturnValueIndex)) + .Case("tolower", TaintPropagationRule(0, ReturnValueIndex)) + .Case("strchr", TaintPropagationRule(0, ReturnValueIndex)) + .Case("strrchr", TaintPropagationRule(0, ReturnValueIndex)) + .Case("read", TaintPropagationRule(0, 2, 1, true)) + .Case("pread", TaintPropagationRule(InvalidArgIndex, 1, true)) + .Case("gets", TaintPropagationRule(InvalidArgIndex, 0, true)) + .Case("fgets", TaintPropagationRule(2, 0, true)) + .Case("getline", TaintPropagationRule(2, 0)) + .Case("getdelim", TaintPropagationRule(3, 0)) + .Case("fgetln", TaintPropagationRule(0, ReturnValueIndex)) + .Default(TaintPropagationRule()); + + if (!Rule.isNull()) + return Rule; + + // Check if it's one of the memory setting/copying functions. + // This check is specialized but faster then calling isCLibraryFunction. + unsigned BId = 0; + if ( (BId = FDecl->getMemoryFunctionKind()) ) + switch(BId) { + case Builtin::BImemcpy: + case Builtin::BImemmove: + case Builtin::BIstrncpy: + case Builtin::BIstrncat: + return TaintPropagationRule(1, 2, 0, true); + case Builtin::BIstrlcpy: + case Builtin::BIstrlcat: + return TaintPropagationRule(1, 2, 0, false); + case Builtin::BIstrndup: + return TaintPropagationRule(0, 1, ReturnValueIndex); + + default: + break; + }; + + // Process all other functions which could be defined as builtins. + if (Rule.isNull()) { + if (C.isCLibraryFunction(FDecl, "snprintf") || + C.isCLibraryFunction(FDecl, "sprintf")) + return TaintPropagationRule(InvalidArgIndex, 0, true); + else if (C.isCLibraryFunction(FDecl, "strcpy") || + C.isCLibraryFunction(FDecl, "stpcpy") || + C.isCLibraryFunction(FDecl, "strcat")) + return TaintPropagationRule(1, 0, true); + else if (C.isCLibraryFunction(FDecl, "bcopy")) + return TaintPropagationRule(0, 2, 1, false); + else if (C.isCLibraryFunction(FDecl, "strdup") || + C.isCLibraryFunction(FDecl, "strdupa")) + return TaintPropagationRule(0, ReturnValueIndex); + else if (C.isCLibraryFunction(FDecl, "wcsdup")) + return TaintPropagationRule(0, ReturnValueIndex); + } + + // Skipping the following functions, since they might be used for cleansing + // or smart memory copy: + // - memccpy - copying untill hitting a special character. + + return TaintPropagationRule(); +} + +void GenericTaintChecker::checkPreStmt(const CallExpr *CE, + CheckerContext &C) const { + // Check for errors first. + if (checkPre(CE, C)) + return; + + // Add taint second. + addSourcesPre(CE, C); +} + +void GenericTaintChecker::checkPostStmt(const CallExpr *CE, + CheckerContext &C) const { + if (propagateFromPre(CE, C)) + return; + addSourcesPost(CE, C); +} + +void GenericTaintChecker::addSourcesPre(const CallExpr *CE, + CheckerContext &C) const { + ProgramStateRef State = 0; + const FunctionDecl *FDecl = C.getCalleeDecl(CE); + StringRef Name = C.getCalleeName(FDecl); + if (Name.empty()) + return; + + // First, try generating a propagation rule for this function. + TaintPropagationRule Rule = + TaintPropagationRule::getTaintPropagationRule(FDecl, Name, C); + if (!Rule.isNull()) { + State = Rule.process(CE, C); + if (!State) + return; + C.addTransition(State); + return; + } + + // Otherwise, check if we have custom pre-processing implemented. + FnCheck evalFunction = llvm::StringSwitch<FnCheck>(Name) + .Case("fscanf", &GenericTaintChecker::preFscanf) + .Default(0); + // Check and evaluate the call. + if (evalFunction) + State = (this->*evalFunction)(CE, C); + if (!State) + return; + C.addTransition(State); + +} + +bool GenericTaintChecker::propagateFromPre(const CallExpr *CE, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + + // Depending on what was tainted at pre-visit, we determined a set of + // arguments which should be tainted after the function returns. These are + // stored in the state as TaintArgsOnPostVisit set. + llvm::ImmutableSet<unsigned> TaintArgs = State->get<TaintArgsOnPostVisit>(); + if (TaintArgs.isEmpty()) + return false; + + for (llvm::ImmutableSet<unsigned>::iterator + I = TaintArgs.begin(), E = TaintArgs.end(); I != E; ++I) { + unsigned ArgNum = *I; + + // Special handling for the tainted return value. + if (ArgNum == ReturnValueIndex) { + State = State->addTaint(CE, C.getLocationContext()); + continue; + } + + // The arguments are pointer arguments. The data they are pointing at is + // tainted after the call. + if (CE->getNumArgs() < (ArgNum + 1)) + return false; + const Expr* Arg = CE->getArg(ArgNum); + SymbolRef Sym = getPointedToSymbol(C, Arg); + if (Sym) + State = State->addTaint(Sym); + } + + // Clear up the taint info from the state. + State = State->remove<TaintArgsOnPostVisit>(); + + if (State != C.getState()) { + C.addTransition(State); + return true; + } + return false; +} + +void GenericTaintChecker::addSourcesPost(const CallExpr *CE, + CheckerContext &C) const { + // Define the attack surface. + // Set the evaluation function by switching on the callee name. + StringRef Name = C.getCalleeName(CE); + if (Name.empty()) + return; + FnCheck evalFunction = llvm::StringSwitch<FnCheck>(Name) + .Case("scanf", &GenericTaintChecker::postScanf) + // TODO: Add support for vfscanf & family. + .Case("getchar", &GenericTaintChecker::postRetTaint) + .Case("getchar_unlocked", &GenericTaintChecker::postRetTaint) + .Case("getenv", &GenericTaintChecker::postRetTaint) + .Case("fopen", &GenericTaintChecker::postRetTaint) + .Case("fdopen", &GenericTaintChecker::postRetTaint) + .Case("freopen", &GenericTaintChecker::postRetTaint) + .Case("getch", &GenericTaintChecker::postRetTaint) + .Case("wgetch", &GenericTaintChecker::postRetTaint) + .Case("socket", &GenericTaintChecker::postSocket) + .Default(0); + + // If the callee isn't defined, it is not of security concern. + // Check and evaluate the call. + ProgramStateRef State = 0; + if (evalFunction) + State = (this->*evalFunction)(CE, C); + if (!State) + return; + + C.addTransition(State); +} + +bool GenericTaintChecker::checkPre(const CallExpr *CE, CheckerContext &C) const{ + + if (checkUncontrolledFormatString(CE, C)) + return true; + + const FunctionDecl *FDecl = C.getCalleeDecl(CE); + StringRef Name = C.getCalleeName(FDecl); + if (Name.empty()) + return false; + + if (checkSystemCall(CE, Name, C)) + return true; + + if (checkTaintedBufferSize(CE, FDecl, C)) + return true; + + return false; +} + +SymbolRef GenericTaintChecker::getPointedToSymbol(CheckerContext &C, + const Expr* Arg) { + ProgramStateRef State = C.getState(); + SVal AddrVal = State->getSVal(Arg->IgnoreParens(), C.getLocationContext()); + if (AddrVal.isUnknownOrUndef()) + return 0; + + Loc *AddrLoc = dyn_cast<Loc>(&AddrVal); + if (!AddrLoc) + return 0; + + const PointerType *ArgTy = + dyn_cast<PointerType>(Arg->getType().getCanonicalType().getTypePtr()); + SVal Val = State->getSVal(*AddrLoc, + ArgTy ? ArgTy->getPointeeType(): QualType()); + return Val.getAsSymbol(); +} + +ProgramStateRef +GenericTaintChecker::TaintPropagationRule::process(const CallExpr *CE, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + + // Check for taint in arguments. + bool IsTainted = false; + for (ArgVector::const_iterator I = SrcArgs.begin(), + E = SrcArgs.end(); I != E; ++I) { + unsigned ArgNum = *I; + + if (ArgNum == InvalidArgIndex) { + // Check if any of the arguments is tainted, but skip the + // destination arguments. + for (unsigned int i = 0; i < CE->getNumArgs(); ++i) { + if (isDestinationArgument(i)) + continue; + if ((IsTainted = isTaintedOrPointsToTainted(CE->getArg(i), State, C))) + break; + } + break; + } + + if (CE->getNumArgs() < (ArgNum + 1)) + return State; + if ((IsTainted = isTaintedOrPointsToTainted(CE->getArg(ArgNum), State, C))) + break; + } + if (!IsTainted) + return State; + + // Mark the arguments which should be tainted after the function returns. + for (ArgVector::const_iterator I = DstArgs.begin(), + E = DstArgs.end(); I != E; ++I) { + unsigned ArgNum = *I; + + // Should we mark all arguments as tainted? + if (ArgNum == InvalidArgIndex) { + // For all pointer and references that were passed in: + // If they are not pointing to const data, mark data as tainted. + // TODO: So far we are just going one level down; ideally we'd need to + // recurse here. + for (unsigned int i = 0; i < CE->getNumArgs(); ++i) { + const Expr *Arg = CE->getArg(i); + // Process pointer argument. + const Type *ArgTy = Arg->getType().getTypePtr(); + QualType PType = ArgTy->getPointeeType(); + if ((!PType.isNull() && !PType.isConstQualified()) + || (ArgTy->isReferenceType() && !Arg->getType().isConstQualified())) + State = State->add<TaintArgsOnPostVisit>(i); + } + continue; + } + + // Should mark the return value? + if (ArgNum == ReturnValueIndex) { + State = State->add<TaintArgsOnPostVisit>(ReturnValueIndex); + continue; + } + + // Mark the given argument. + assert(ArgNum < CE->getNumArgs()); + State = State->add<TaintArgsOnPostVisit>(ArgNum); + } + + return State; +} + + +// If argument 0 (file descriptor) is tainted, all arguments except for arg 0 +// and arg 1 should get taint. +ProgramStateRef GenericTaintChecker::preFscanf(const CallExpr *CE, + CheckerContext &C) const { + assert(CE->getNumArgs() >= 2); + ProgramStateRef State = C.getState(); + + // Check is the file descriptor is tainted. + if (State->isTainted(CE->getArg(0), C.getLocationContext()) || + isStdin(CE->getArg(0), C)) { + // All arguments except for the first two should get taint. + for (unsigned int i = 2; i < CE->getNumArgs(); ++i) + State = State->add<TaintArgsOnPostVisit>(i); + return State; + } + + return 0; +} + + +// If argument 0(protocol domain) is network, the return value should get taint. +ProgramStateRef GenericTaintChecker::postSocket(const CallExpr *CE, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + if (CE->getNumArgs() < 3) + return State; + + SourceLocation DomLoc = CE->getArg(0)->getExprLoc(); + StringRef DomName = C.getMacroNameOrSpelling(DomLoc); + // White list the internal communication protocols. + if (DomName.equals("AF_SYSTEM") || DomName.equals("AF_LOCAL") || + DomName.equals("AF_UNIX") || DomName.equals("AF_RESERVED_36")) + return State; + State = State->addTaint(CE, C.getLocationContext()); + return State; +} + +ProgramStateRef GenericTaintChecker::postScanf(const CallExpr *CE, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + if (CE->getNumArgs() < 2) + return State; + + SVal x = State->getSVal(CE->getArg(1), C.getLocationContext()); + // All arguments except for the very first one should get taint. + for (unsigned int i = 1; i < CE->getNumArgs(); ++i) { + // The arguments are pointer arguments. The data they are pointing at is + // tainted after the call. + const Expr* Arg = CE->getArg(i); + SymbolRef Sym = getPointedToSymbol(C, Arg); + if (Sym) + State = State->addTaint(Sym); + } + return State; +} + +ProgramStateRef GenericTaintChecker::postRetTaint(const CallExpr *CE, + CheckerContext &C) const { + return C.getState()->addTaint(CE, C.getLocationContext()); +} + +bool GenericTaintChecker::isStdin(const Expr *E, CheckerContext &C) { + ProgramStateRef State = C.getState(); + SVal Val = State->getSVal(E, C.getLocationContext()); + + // stdin is a pointer, so it would be a region. + const MemRegion *MemReg = Val.getAsRegion(); + + // The region should be symbolic, we do not know it's value. + const SymbolicRegion *SymReg = dyn_cast_or_null<SymbolicRegion>(MemReg); + if (!SymReg) + return false; + + // Get it's symbol and find the declaration region it's pointing to. + const SymbolRegionValue *Sm =dyn_cast<SymbolRegionValue>(SymReg->getSymbol()); + if (!Sm) + return false; + const DeclRegion *DeclReg = dyn_cast_or_null<DeclRegion>(Sm->getRegion()); + if (!DeclReg) + return false; + + // This region corresponds to a declaration, find out if it's a global/extern + // variable named stdin with the proper type. + if (const VarDecl *D = dyn_cast_or_null<VarDecl>(DeclReg->getDecl())) { + D = D->getCanonicalDecl(); + if ((D->getName().find("stdin") != StringRef::npos) && D->isExternC()) + if (const PointerType * PtrTy = + dyn_cast<PointerType>(D->getType().getTypePtr())) + if (PtrTy->getPointeeType() == C.getASTContext().getFILEType()) + return true; + } + return false; +} + +static bool getPrintfFormatArgumentNum(const CallExpr *CE, + const CheckerContext &C, + unsigned int &ArgNum) { + // Find if the function contains a format string argument. + // Handles: fprintf, printf, sprintf, snprintf, vfprintf, vprintf, vsprintf, + // vsnprintf, syslog, custom annotated functions. + const FunctionDecl *FDecl = C.getCalleeDecl(CE); + if (!FDecl) + return false; + for (specific_attr_iterator<FormatAttr> + i = FDecl->specific_attr_begin<FormatAttr>(), + e = FDecl->specific_attr_end<FormatAttr>(); i != e ; ++i) { + + const FormatAttr *Format = *i; + ArgNum = Format->getFormatIdx() - 1; + if ((Format->getType() == "printf") && CE->getNumArgs() > ArgNum) + return true; + } + + // Or if a function is named setproctitle (this is a heuristic). + if (C.getCalleeName(CE).find("setproctitle") != StringRef::npos) { + ArgNum = 0; + return true; + } + + return false; +} + +bool GenericTaintChecker::generateReportIfTainted(const Expr *E, + const char Msg[], + CheckerContext &C) const { + assert(E); + + // Check for taint. + ProgramStateRef State = C.getState(); + if (!State->isTainted(getPointedToSymbol(C, E)) && + !State->isTainted(E, C.getLocationContext())) + return false; + + // Generate diagnostic. + if (ExplodedNode *N = C.addTransition()) { + initBugType(); + BugReport *report = new BugReport(*BT, Msg, N); + report->addRange(E->getSourceRange()); + C.EmitReport(report); + return true; + } + return false; +} + +bool GenericTaintChecker::checkUncontrolledFormatString(const CallExpr *CE, + CheckerContext &C) const{ + // Check if the function contains a format string argument. + unsigned int ArgNum = 0; + if (!getPrintfFormatArgumentNum(CE, C, ArgNum)) + return false; + + // If either the format string content or the pointer itself are tainted, warn. + if (generateReportIfTainted(CE->getArg(ArgNum), + MsgUncontrolledFormatString, C)) + return true; + return false; +} + +bool GenericTaintChecker::checkSystemCall(const CallExpr *CE, + StringRef Name, + CheckerContext &C) const { + // TODO: It might make sense to run this check on demand. In some cases, + // we should check if the environment has been cleansed here. We also might + // need to know if the user was reset before these calls(seteuid). + unsigned ArgNum = llvm::StringSwitch<unsigned>(Name) + .Case("system", 0) + .Case("popen", 0) + .Case("execl", 0) + .Case("execle", 0) + .Case("execlp", 0) + .Case("execv", 0) + .Case("execvp", 0) + .Case("execvP", 0) + .Case("execve", 0) + .Case("dlopen", 0) + .Default(UINT_MAX); + + if (ArgNum == UINT_MAX || CE->getNumArgs() < (ArgNum + 1)) + return false; + + if (generateReportIfTainted(CE->getArg(ArgNum), + MsgSanitizeSystemArgs, C)) + return true; + + return false; +} + +// TODO: Should this check be a part of the CString checker? +// If yes, should taint be a global setting? +bool GenericTaintChecker::checkTaintedBufferSize(const CallExpr *CE, + const FunctionDecl *FDecl, + CheckerContext &C) const { + // If the function has a buffer size argument, set ArgNum. + unsigned ArgNum = InvalidArgIndex; + unsigned BId = 0; + if ( (BId = FDecl->getMemoryFunctionKind()) ) + switch(BId) { + case Builtin::BImemcpy: + case Builtin::BImemmove: + case Builtin::BIstrncpy: + ArgNum = 2; + break; + case Builtin::BIstrndup: + ArgNum = 1; + break; + default: + break; + }; + + if (ArgNum == InvalidArgIndex) { + if (C.isCLibraryFunction(FDecl, "malloc") || + C.isCLibraryFunction(FDecl, "calloc") || + C.isCLibraryFunction(FDecl, "alloca")) + ArgNum = 0; + else if (C.isCLibraryFunction(FDecl, "memccpy")) + ArgNum = 3; + else if (C.isCLibraryFunction(FDecl, "realloc")) + ArgNum = 1; + else if (C.isCLibraryFunction(FDecl, "bcopy")) + ArgNum = 2; + } + + if (ArgNum != InvalidArgIndex && CE->getNumArgs() > ArgNum && + generateReportIfTainted(CE->getArg(ArgNum), MsgTaintedBufferSize, C)) + return true; + + return false; +} + +void ento::registerGenericTaintChecker(CheckerManager &mgr) { + mgr.registerChecker<GenericTaintChecker>(); +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/IdempotentOperationChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/IdempotentOperationChecker.cpp index 5c257e5..c08f163 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/IdempotentOperationChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/IdempotentOperationChecker.cpp @@ -57,6 +57,7 @@ #include "clang/AST/Stmt.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/SmallSet.h" +#include "llvm/ADT/SmallString.h" #include "llvm/ADT/BitVector.h" #include "llvm/Support/ErrorHandling.h" @@ -82,16 +83,16 @@ private: // False positive reduction methods static bool isSelfAssign(const Expr *LHS, const Expr *RHS); - static bool isUnused(const Expr *E, AnalysisContext *AC); + static bool isUnused(const Expr *E, AnalysisDeclContext *AC); static bool isTruncationExtensionAssignment(const Expr *LHS, const Expr *RHS); - static bool pathWasCompletelyAnalyzed(AnalysisContext *AC, + static bool pathWasCompletelyAnalyzed(AnalysisDeclContext *AC, const CFGBlock *CB, const CoreEngine &CE); static bool CanVary(const Expr *Ex, - AnalysisContext *AC); + AnalysisDeclContext *AC); static bool isConstantOrPseudoConstant(const DeclRefExpr *DR, - AnalysisContext *AC); + AnalysisDeclContext *AC); static bool containsNonLocalVarDecl(const Stmt *S); // Hash table and related data structures @@ -116,7 +117,7 @@ void IdempotentOperationChecker::checkPreStmt(const BinaryOperator *B, // been created yet. BinaryOperatorData &Data = hash[B]; Assumption &A = Data.assumption; - AnalysisContext *AC = C.getCurrentAnalysisContext(); + AnalysisDeclContext *AC = C.getCurrentAnalysisDeclContext(); // If we already have visited this node on a path that does not contain an // idempotent operation, return immediately. @@ -141,10 +142,10 @@ void IdempotentOperationChecker::checkPreStmt(const BinaryOperator *B, || containsNonLocalVarDecl(RHS); } - const ProgramState *state = C.getState(); - - SVal LHSVal = state->getSVal(LHS); - SVal RHSVal = state->getSVal(RHS); + ProgramStateRef state = C.getState(); + const LocationContext *LCtx = C.getLocationContext(); + SVal LHSVal = state->getSVal(LHS, LCtx); + SVal RHSVal = state->getSVal(RHS, LCtx); // If either value is unknown, we can't be 100% sure of all paths. if (LHSVal.isUnknownOrUndef() || RHSVal.isUnknownOrUndef()) { @@ -366,8 +367,8 @@ void IdempotentOperationChecker::checkEndAnalysis(ExplodedGraph &G, // warning if (Eng.hasWorkRemaining()) { // If we can trace back - AnalysisContext *AC = (*ES.begin())->getLocationContext() - ->getAnalysisContext(); + AnalysisDeclContext *AC = (*ES.begin())->getLocationContext() + ->getAnalysisDeclContext(); if (!pathWasCompletelyAnalyzed(AC, AC->getCFGStmtMap()->getBlock(B), Eng.getCoreEngine())) @@ -375,7 +376,7 @@ void IdempotentOperationChecker::checkEndAnalysis(ExplodedGraph &G, } // Select the error message and SourceRanges to report. - llvm::SmallString<128> buf; + SmallString<128> buf; llvm::raw_svector_ostream os(buf); bool LHSRelevant = false, RHSRelevant = false; switch (A) { @@ -487,7 +488,7 @@ bool IdempotentOperationChecker::isSelfAssign(const Expr *LHS, const Expr *RHS) // Returns true if the Expr points to a VarDecl that is not read anywhere // outside of self-assignments. bool IdempotentOperationChecker::isUnused(const Expr *E, - AnalysisContext *AC) { + AnalysisDeclContext *AC) { if (!E) return false; @@ -531,7 +532,7 @@ bool IdempotentOperationChecker::isTruncationExtensionAssignment( // Returns false if a path to this block was not completely analyzed, or true // otherwise. bool -IdempotentOperationChecker::pathWasCompletelyAnalyzed(AnalysisContext *AC, +IdempotentOperationChecker::pathWasCompletelyAnalyzed(AnalysisDeclContext *AC, const CFGBlock *CB, const CoreEngine &CE) { @@ -615,7 +616,7 @@ IdempotentOperationChecker::pathWasCompletelyAnalyzed(AnalysisContext *AC, // expression may also involve a variable that behaves like a constant. The // function returns true if the expression varies, and false otherwise. bool IdempotentOperationChecker::CanVary(const Expr *Ex, - AnalysisContext *AC) { + AnalysisDeclContext *AC) { // Parentheses and casts are irrelevant here Ex = Ex->IgnoreParenCasts(); @@ -649,7 +650,6 @@ bool IdempotentOperationChecker::CanVary(const Expr *Ex, case Stmt::InitListExprClass: case Stmt::DesignatedInitExprClass: case Stmt::BlockExprClass: - case Stmt::BlockDeclRefExprClass: return false; // Cases requiring custom logic @@ -699,7 +699,7 @@ bool IdempotentOperationChecker::CanVary(const Expr *Ex, // Returns true if a DeclRefExpr is or behaves like a constant. bool IdempotentOperationChecker::isConstantOrPseudoConstant( const DeclRefExpr *DR, - AnalysisContext *AC) { + AnalysisDeclContext *AC) { // Check if the type of the Decl is const-qualified if (DR->getType().isConstQualified()) return true; diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/InterCheckerAPI.h b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/InterCheckerAPI.h new file mode 100644 index 0000000..e35557f --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/InterCheckerAPI.h @@ -0,0 +1,22 @@ +//==--- InterCheckerAPI.h ---------------------------------------*- C++ -*-==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// This file allows introduction of checker dependencies. It contains APIs for +// inter-checker communications. +//===----------------------------------------------------------------------===// + +#ifndef INTERCHECKERAPI_H_ +#define INTERCHECKERAPI_H_ +namespace clang { +namespace ento { + +/// Register the checker which evaluates CString API calls. +void registerCStringCheckerBasic(CheckerManager &Mgr); + +}} +#endif /* INTERCHECKERAPI_H_ */ diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/IteratorsChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/IteratorsChecker.cpp index fbc57d3..b0bac33 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/IteratorsChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/IteratorsChecker.cpp @@ -22,7 +22,7 @@ #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" #include "clang/AST/DeclCXX.h" -#include "clang/AST/Decl.h" +#include "clang/AST/ExprCXX.h" #include "clang/AST/Type.h" #include "clang/AST/PrettyPrinter.h" #include "llvm/ADT/SmallPtrSet.h" @@ -117,17 +117,17 @@ public: CheckerContext &C) const; private: - const ProgramState *handleAssign(const ProgramState *state, + ProgramStateRef handleAssign(ProgramStateRef state, const Expr *lexp, const Expr *rexp, const LocationContext *LC) const; - const ProgramState *handleAssign(const ProgramState *state, + ProgramStateRef handleAssign(ProgramStateRef state, const MemRegion *MR, const Expr *rexp, const LocationContext *LC) const; - const ProgramState *invalidateIterators(const ProgramState *state, + ProgramStateRef invalidateIterators(ProgramStateRef state, const MemRegion *MR, const MemberExpr *ME) const; @@ -135,7 +135,7 @@ private: void checkArgs(CheckerContext &C, const CallExpr *CE) const; - const MemRegion *getRegion(const ProgramState *state, + const MemRegion *getRegion(ProgramStateRef state, const Expr *E, const LocationContext *LC) const; @@ -227,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 ProgramState *IteratorsChecker::invalidateIterators(const ProgramState *state, +ProgramStateRef IteratorsChecker::invalidateIterators(ProgramStateRef state, const MemRegion *MR, const MemberExpr *ME) const { IteratorState::EntryMap Map = state->get<IteratorState>(); if (Map.isEmpty()) @@ -246,7 +246,7 @@ const ProgramState *IteratorsChecker::invalidateIterators(const ProgramState *st } // Handle assigning to an iterator where we don't have the LValue MemRegion. -const ProgramState *IteratorsChecker::handleAssign(const ProgramState *state, +ProgramStateRef IteratorsChecker::handleAssign(ProgramStateRef state, const Expr *lexp, const Expr *rexp, const LocationContext *LC) const { // Skip the cast if present. if (const MaterializeTemporaryExpr *M @@ -254,7 +254,7 @@ const ProgramState *IteratorsChecker::handleAssign(const ProgramState *state, lexp = M->GetTemporaryExpr(); if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(lexp)) lexp = ICE->getSubExpr(); - SVal sv = state->getSVal(lexp); + SVal sv = state->getSVal(lexp, LC); const MemRegion *MR = sv.getAsRegion(); if (!MR) return state; @@ -271,7 +271,7 @@ const ProgramState *IteratorsChecker::handleAssign(const ProgramState *state, } // handle assigning to an iterator -const ProgramState *IteratorsChecker::handleAssign(const ProgramState *state, +ProgramStateRef IteratorsChecker::handleAssign(ProgramStateRef 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()); @@ -376,7 +376,7 @@ const DeclRefExpr *IteratorsChecker::getDeclRefExpr(const Expr *E) const { } // Get the MemRegion associated with the expresssion. -const MemRegion *IteratorsChecker::getRegion(const ProgramState *state, +const MemRegion *IteratorsChecker::getRegion(ProgramStateRef state, const Expr *E, const LocationContext *LC) const { const DeclRefExpr *DRE = getDeclRefExpr(E); if (!DRE) @@ -394,9 +394,8 @@ const MemRegion *IteratorsChecker::getRegion(const ProgramState *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 ProgramState *state = C.getState(); - const MemRegion *MR = getRegion(state, E, - C.getPredecessor()->getLocationContext()); + ProgramStateRef state = C.getState(); + const MemRegion *MR = getRegion(state, E, C.getLocationContext()); if (!MR) return; @@ -405,7 +404,7 @@ void IteratorsChecker::checkExpr(CheckerContext &C, const Expr *E) const { if (!RS) return; if (RS->isInvalid()) { - if (ExplodedNode *N = C.generateNode()) { + if (ExplodedNode *N = C.addTransition()) { if (!BT_Invalid) // FIXME: We are eluding constness here. const_cast<IteratorsChecker*>(this)->BT_Invalid = new BuiltinBug(""); @@ -428,7 +427,7 @@ void IteratorsChecker::checkExpr(CheckerContext &C, const Expr *E) const { } } else if (RS->isUndefined()) { - if (ExplodedNode *N = C.generateNode()) { + if (ExplodedNode *N = C.addTransition()) { if (!BT_Undefined) // FIXME: We are eluding constness here. const_cast<IteratorsChecker*>(this)->BT_Undefined = @@ -466,8 +465,8 @@ void IteratorsChecker::checkPreStmt(const CallExpr *CE, void IteratorsChecker::checkPreStmt(const CXXOperatorCallExpr *OCE, CheckerContext &C) const { - const LocationContext *LC = C.getPredecessor()->getLocationContext(); - const ProgramState *state = C.getState(); + const LocationContext *LC = C.getLocationContext(); + ProgramStateRef state = C.getState(); OverloadedOperatorKind Kind = OCE->getOperator(); if (Kind == OO_Equal) { checkExpr(C, OCE->getArg(1)); @@ -497,7 +496,7 @@ void IteratorsChecker::checkPreStmt(const CXXOperatorCallExpr *OCE, if (!RS1) return; if (RS0->getMemRegion() != RS1->getMemRegion()) { - if (ExplodedNode *N = C.generateNode()) { + if (ExplodedNode *N = C.addTransition()) { if (!BT_Incompatible) const_cast<IteratorsChecker*>(this)->BT_Incompatible = new BuiltinBug( @@ -524,8 +523,8 @@ void IteratorsChecker::checkPreStmt(const DeclStmt *DS, return; // Get the MemRegion associated with the iterator and mark it as Undefined. - const ProgramState *state = C.getState(); - Loc VarLoc = state->getLValue(VD, C.getPredecessor()->getLocationContext()); + ProgramStateRef state = C.getState(); + Loc VarLoc = state->getLValue(VD, C.getLocationContext()); const MemRegion *MR = VarLoc.getAsRegion(); if (!MR) return; @@ -545,8 +544,7 @@ void IteratorsChecker::checkPreStmt(const DeclStmt *DS, E = M->GetTemporaryExpr(); if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(E)) InitEx = ICE->getSubExpr(); - state = handleAssign(state, MR, InitEx, - C.getPredecessor()->getLocationContext()); + state = handleAssign(state, MR, InitEx, C.getLocationContext()); } } } @@ -576,14 +574,14 @@ void IteratorsChecker::checkPreStmt(const CXXMemberCallExpr *MCE, const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(ME->getBase()); if (!DRE || getTemplateKind(DRE->getType()) != VectorKind) return; - SVal tsv = C.getState()->getSVal(DRE); + SVal tsv = C.getState()->getSVal(DRE, C.getLocationContext()); // Get the MemRegion associated with the container instance. const MemRegion *MR = tsv.getAsRegion(); if (!MR) return; // If we are calling a function that invalidates iterators, mark them // appropriately by finding matching instances. - const ProgramState *state = C.getState(); + ProgramStateRef state = C.getState(); StringRef mName = ME->getMemberDecl()->getName(); if (llvm::StringSwitch<bool>(mName) .Cases("insert", "reserve", "push_back", true) diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/LLVMConventionsChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/LLVMConventionsChecker.cpp index e398fcb..757a4ce 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/LLVMConventionsChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/LLVMConventionsChecker.cpp @@ -17,8 +17,7 @@ #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" #include "clang/AST/DeclTemplate.h" #include "clang/AST/StmtVisitor.h" -#include <string> -#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/SmallString.h" using namespace clang; using namespace ento; @@ -116,8 +115,10 @@ static bool IsSmallVector(QualType T) { namespace { class StringRefCheckerVisitor : public StmtVisitor<StringRefCheckerVisitor> { BugReporter &BR; + const Decl *DeclWithIssue; public: - StringRefCheckerVisitor(BugReporter &br) : BR(br) {} + StringRefCheckerVisitor(const Decl *declWithIssue, BugReporter &br) + : BR(br), DeclWithIssue(declWithIssue) {} void VisitChildren(Stmt *S) { for (Stmt::child_iterator I = S->child_begin(), E = S->child_end() ; I != E; ++I) @@ -132,7 +133,7 @@ private: } // end anonymous namespace static void CheckStringRefAssignedTemporary(const Decl *D, BugReporter &BR) { - StringRefCheckerVisitor walker(BR); + StringRefCheckerVisitor walker(D, BR); walker.Visit(D->getBody()); } @@ -177,7 +178,7 @@ void StringRefCheckerVisitor::VisitVarDecl(VarDecl *VD) { "std::string that it outlives"; PathDiagnosticLocation VDLoc = PathDiagnosticLocation::createBegin(VD, BR.getSourceManager()); - BR.EmitBasicReport(desc, "LLVM Conventions", desc, + BR.EmitBasicReport(DeclWithIssue, desc, "LLVM Conventions", desc, VDLoc, Init->getSourceRange()); } @@ -253,7 +254,7 @@ void ASTFieldVisitor::Visit(FieldDecl *D) { } void ASTFieldVisitor::ReportError(QualType T) { - llvm::SmallString<1024> buf; + SmallString<1024> buf; llvm::raw_svector_ostream os(buf); os << "AST class '" << Root->getName() << "' has a field '" @@ -282,7 +283,7 @@ void ASTFieldVisitor::ReportError(QualType T) { // 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", + BR.EmitBasicReport(Root, "AST node allocates heap memory", "LLVM Conventions", os.str(), L); } diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp index 2607db8..cb976e0 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp @@ -19,6 +19,7 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" +#include "llvm/ADT/SmallString.h" using namespace clang; using namespace ento; @@ -29,7 +30,7 @@ class MacOSKeychainAPIChecker : public Checker<check::PreStmt<CallExpr>, check::PostStmt<CallExpr>, check::EndPath, check::DeadSymbols> { - mutable llvm::OwningPtr<BugType> BT; + mutable OwningPtr<BugType> BT; public: /// AllocationState is a part of the checker specific state together with the @@ -58,7 +59,7 @@ public: 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; + void checkEndPath(CheckerContext &C) const; private: typedef std::pair<SymbolRef, const AllocationState*> AllocationPair; @@ -100,26 +101,38 @@ private: const Expr *ArgExpr, CheckerContext &C) const; + /// Find the allocation site for Sym on the path leading to the node N. + const Stmt *getAllocationSite(const ExplodedNode *N, SymbolRef Sym, + CheckerContext &C) const; + BugReport *generateAllocatedDataNotReleasedReport(const AllocationPair &AP, - ExplodedNode *N) const; + ExplodedNode *N, + CheckerContext &C) const; /// Check if RetSym evaluates to an error value in the current state. bool definitelyReturnedError(SymbolRef RetSym, - const ProgramState *State, + ProgramStateRef 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, + ProgramStateRef State, SValBuilder &Builder) const { return definitelyReturnedError(RetSym, State, Builder, true); } + + /// Mark an AllocationPair interesting for diagnostic reporting. + void markInteresting(BugReport *R, const AllocationPair &AP) const { + R->markInteresting(AP.first); + R->markInteresting(AP.second->Region); + } /// 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 { + class SecKeychainBugVisitor + : public BugReporterVisitorImpl<SecKeychainBugVisitor> { protected: // The allocated region symbol tracked by the main analysis. SymbolRef Sym; @@ -196,18 +209,9 @@ unsigned MacOSKeychainAPIChecker::getTrackedFunctionIndex(StringRef Name, 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 (!Arg) + return false; if (isa<AllocaRegion>(Arg) || isa<BlockDataRegion>(Arg) || isa<TypedRegion>(Arg)) { @@ -215,18 +219,19 @@ static bool isBadDeallocationArgument(const MemRegion *Arg) { } 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); + ProgramStateRef State = C.getState(); + SVal ArgV = State->getSVal(Expr, C.getLocationContext()); 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); + SymbolRef sym = SM.getBinding(State->getStore(), *X).getAsLocSymbol(); + if (sym) + return sym; } return 0; } @@ -238,14 +243,14 @@ static SymbolRef getAsPointeeSymbol(const Expr *Expr, // If noError, returns true iff (1). // If !noError, returns true iff (2). bool MacOSKeychainAPIChecker::definitelyReturnedError(SymbolRef RetSym, - const ProgramState *State, + ProgramStateRef 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); + ProgramStateRef ErrState = State->assume(NoErr, noError); if (ErrState == State) { return true; } @@ -259,14 +264,14 @@ void MacOSKeychainAPIChecker:: generateDeallocatorMismatchReport(const AllocationPair &AP, const Expr *ArgExpr, CheckerContext &C) const { - const ProgramState *State = C.getState(); + ProgramStateRef State = C.getState(); State = State->remove<AllocatedData>(AP.first); - ExplodedNode *N = C.generateNode(State); + ExplodedNode *N = C.addTransition(State); if (!N) return; initBugType(); - llvm::SmallString<80> sbuf; + SmallString<80> sbuf; llvm::raw_svector_ostream os(sbuf); unsigned int PDeallocIdx = FunctionsToTrack[AP.second->AllocatorIdx].DeallocatorIdx; @@ -276,23 +281,18 @@ void MacOSKeychainAPIChecker:: BugReport *Report = new BugReport(*BT, os.str(), N); Report->addVisitor(new SecKeychainBugVisitor(AP.first)); Report->addRange(ArgExpr->getSourceRange()); + markInteresting(Report, AP); 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; + ProgramStateRef State = C.getState(); - const FunctionDecl *funDecl = L.getAsFunctionDecl(); - if (!funDecl) - return; - IdentifierInfo *funI = funDecl->getIdentifier(); - if (!funI) + StringRef funName = C.getCalleeName(CE); + if (funName.empty()) return; - StringRef funName = funI->getName(); // If it is a call to an allocator function, it could be a double allocation. idx = getTrackedFunctionIndex(funName, true); @@ -304,11 +304,11 @@ void MacOSKeychainAPIChecker::checkPreStmt(const CallExpr *CE, // 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); + ExplodedNode *N = C.addTransition(State); if (!N) return; initBugType(); - llvm::SmallString<128> sbuf; + 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 " @@ -318,6 +318,7 @@ void MacOSKeychainAPIChecker::checkPreStmt(const CallExpr *CE, BugReport *Report = new BugReport(*BT, os.str(), N); Report->addVisitor(new SecKeychainBugVisitor(V)); Report->addRange(ArgExpr->getSourceRange()); + Report->markInteresting(AS->Region); C.EmitReport(Report); } } @@ -331,22 +332,22 @@ void MacOSKeychainAPIChecker::checkPreStmt(const CallExpr *CE, // Check the argument to the deallocator. const Expr *ArgExpr = CE->getArg(FunctionsToTrack[idx].Param); - SVal ArgSVal = State->getSVal(ArgExpr); + SVal ArgSVal = State->getSVal(ArgExpr, C.getLocationContext()); // Undef is reported by another checker. if (ArgSVal.isUndef()) return; - const MemRegion *Arg = ArgSVal.getAsRegion(); - if (!Arg) - return; + SymbolRef ArgSM = ArgSVal.getAsLocSymbol(); - 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; + bool RegionArgIsBad = false; + if (!ArgSM) { + if (!isBadDeallocationArgument(ArgSVal.getAsRegion())) + return; + RegionArgIsBad = true; + } // Is the argument to the call being tracked? const AllocationState *AS = State->get<AllocatedData>(ArgSM); @@ -362,13 +363,15 @@ void MacOSKeychainAPIChecker::checkPreStmt(const CallExpr *CE, if (isEnclosingFunctionParam(ArgExpr)) return; - ExplodedNode *N = C.generateNode(State); + ExplodedNode *N = C.addTransition(State); if (!N) return; initBugType(); BugReport *Report = new BugReport(*BT, "Trying to free data which has not been allocated.", N); Report->addRange(ArgExpr->getSourceRange()); + if (AS) + Report->markInteresting(AS->Region); C.EmitReport(Report); return; } @@ -420,16 +423,19 @@ void MacOSKeychainAPIChecker::checkPreStmt(const CallExpr *CE, 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 the buffer can be null and the return status can be an error, + // report a bad call to free. + if (State->assume(cast<DefinedSVal>(ArgSVal), false) && + !definitelyDidnotReturnError(AS->Region, State, C.getSValBuilder())) { + ExplodedNode *N = C.addTransition(State); if (!N) return; initBugType(); BugReport *Report = new BugReport(*BT, - "Call to free data when error was returned during allocation.", N); + "Only call free if a valid (non-NULL) buffer was returned.", N); Report->addVisitor(new SecKeychainBugVisitor(ArgSM)); Report->addRange(ArgExpr->getSourceRange()); + Report->markInteresting(AS->Region); C.EmitReport(Report); return; } @@ -439,17 +445,8 @@ void MacOSKeychainAPIChecker::checkPreStmt(const CallExpr *CE, 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(); + ProgramStateRef State = C.getState(); + StringRef funName = C.getCalleeName(CE); // If a value has been allocated, add it to the set for tracking. unsigned idx = getTrackedFunctionIndex(funName, true); @@ -459,7 +456,8 @@ void MacOSKeychainAPIChecker::checkPostStmt(const CallExpr *CE, 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)) + if (isEnclosingFunctionParam(ArgExpr) && + C.getLocationContext()->getParent() == 0) return; if (SymbolRef V = getAsPointeeSymbol(ArgExpr, C)) { @@ -475,7 +473,8 @@ void MacOSKeychainAPIChecker::checkPostStmt(const CallExpr *CE, // 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(); + SymbolRef RetStatusSymbol = + State->getSVal(CE, C.getLocationContext()).getAsSymbol(); C.getSymbolManager().addSymbolDependency(V, RetStatusSymbol); // Track the allocated value in the checker state. @@ -492,36 +491,76 @@ void MacOSKeychainAPIChecker::checkPreStmt(const ReturnStmt *S, if (!retExpr) return; + // If inside inlined call, skip it. + const LocationContext *LC = C.getLocationContext(); + if (LC->getParent() != 0) + return; + // Check if the value is escaping through the return. - const ProgramState *state = C.getState(); - const MemRegion *V = state->getSVal(retExpr).getAsRegion(); - if (!V) + ProgramStateRef state = C.getState(); + SymbolRef sym = state->getSVal(retExpr, LC).getAsLocSymbol(); + if (!sym) return; - state = state->remove<AllocatedData>(getSymbolForRegion(C, V)); + state = state->remove<AllocatedData>(sym); // Proceed from the new state. C.addTransition(state); } +// TODO: This logic is the same as in Malloc checker. +const Stmt * +MacOSKeychainAPIChecker::getAllocationSite(const ExplodedNode *N, + SymbolRef Sym, + CheckerContext &C) const { + const LocationContext *LeakContext = N->getLocationContext(); + // Walk the ExplodedGraph backwards and find the first node that referred to + // the tracked symbol. + const ExplodedNode *AllocNode = N; + + while (N) { + if (!N->getState()->get<AllocatedData>(Sym)) + break; + // Allocation node, is the last node in the current context in which the + // symbol was tracked. + if (N->getLocationContext() == LeakContext) + AllocNode = N; + N = N->pred_empty() ? NULL : *(N->pred_begin()); + } + + ProgramPoint P = AllocNode->getLocation(); + if (!isa<StmtPoint>(P)) + return 0; + return cast<clang::PostStmt>(P).getStmt(); +} + BugReport *MacOSKeychainAPIChecker:: generateAllocatedDataNotReleasedReport(const AllocationPair &AP, - ExplodedNode *N) const { + ExplodedNode *N, + CheckerContext &C) const { const ADFunctionInfo &FI = FunctionsToTrack[AP.second->AllocatorIdx]; initBugType(); - llvm::SmallString<70> sbuf; + 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); + + // Most bug reports are cached at the location where they occurred. + // With leaks, we want to unique them by the location where they were + // allocated, and only report a single path. + PathDiagnosticLocation LocUsedForUniqueing; + if (const Stmt *AllocStmt = getAllocationSite(N, AP.first, C)) + LocUsedForUniqueing = PathDiagnosticLocation::createBegin(AllocStmt, + C.getSourceManager(), N->getLocationContext()); + + BugReport *Report = new BugReport(*BT, os.str(), N, LocUsedForUniqueing); Report->addVisitor(new SecKeychainBugVisitor(AP.first)); - Report->addRange(SourceRange()); + markInteresting(Report, AP); return Report; } void MacOSKeychainAPIChecker::checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const { - const ProgramState *State = C.getState(); + ProgramStateRef State = C.getState(); AllocatedSetTy ASet = State->get<AllocatedData>(); if (ASet.isEmpty()) return; @@ -541,25 +580,33 @@ void MacOSKeychainAPIChecker::checkDeadSymbols(SymbolReaper &SR, continue; Errors.push_back(std::make_pair(I->first, &I->second)); } - if (!Changed) + if (!Changed) { + // Generate the new, cleaned up state. + C.addTransition(State); return; + } - // Generate the new, cleaned up state. - ExplodedNode *N = C.generateNode(State); - if (!N) - return; + static SimpleProgramPointTag Tag("MacOSKeychainAPIChecker : DeadSymbolsLeak"); + ExplodedNode *N = C.addTransition(C.getState(), C.getPredecessor(), &Tag); // Generate the error reports. for (AllocationPairVec::iterator I = Errors.begin(), E = Errors.end(); I != E; ++I) { - C.EmitReport(generateAllocatedDataNotReleasedReport(*I, N)); + C.EmitReport(generateAllocatedDataNotReleasedReport(*I, N, C)); } + + // Generate the new, cleaned up state. + C.addTransition(State, 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(); +void MacOSKeychainAPIChecker::checkEndPath(CheckerContext &C) const { + ProgramStateRef state = C.getState(); + + // If inside inlined call, skip it. + if (C.getLocationContext()->getParent() != 0) + return; + AllocatedSetTy AS = state->get<AllocatedData>(); if (AS.isEmpty()) return; @@ -575,26 +622,28 @@ void MacOSKeychainAPIChecker::checkEndPath(EndOfFunctionNodeBuilder &B, // allocation, do not report. if (state->getSymVal(I.getKey()) || definitelyReturnedError(I->second.Region, state, - Eng.getSValBuilder())) { + C.getSValBuilder())) { continue; } Errors.push_back(std::make_pair(I->first, &I->second)); } // If no change, do not generate a new state. - if (!Changed) + if (!Changed) { + C.addTransition(state); return; + } - ExplodedNode *N = B.generateNode(state); - if (!N) - return; + static SimpleProgramPointTag Tag("MacOSKeychainAPIChecker : EndPathLeak"); + ExplodedNode *N = C.addTransition(C.getState(), C.getPredecessor(), &Tag); // Generate the error reports. for (AllocationPairVec::iterator I = Errors.begin(), E = Errors.end(); I != E; ++I) { - Eng.getBugReporter().EmitReport( - generateAllocatedDataNotReleasedReport(*I, N)); + C.EmitReport(generateAllocatedDataNotReleasedReport(*I, N, C)); } + + C.addTransition(state, N); } diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MacOSXAPIChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MacOSXAPIChecker.cpp index 88d492e..cfdb55d 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MacOSXAPIChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MacOSXAPIChecker.cpp @@ -31,17 +31,17 @@ using namespace ento; namespace { class MacOSXAPIChecker : public Checker< check::PreStmt<CallExpr> > { - mutable llvm::OwningPtr<BugType> BT_dispatchOnce; + mutable OwningPtr<BugType> BT_dispatchOnce; public: void checkPreStmt(const CallExpr *CE, CheckerContext &C) const; void CheckDispatchOnce(CheckerContext &C, const CallExpr *CE, - const IdentifierInfo *FI) const; + StringRef FName) const; typedef void (MacOSXAPIChecker::*SubChecker)(CheckerContext &, const CallExpr *, - const IdentifierInfo *) const; + StringRef FName) const; }; } //end anonymous namespace @@ -50,14 +50,15 @@ public: //===----------------------------------------------------------------------===// void MacOSXAPIChecker::CheckDispatchOnce(CheckerContext &C, const CallExpr *CE, - const IdentifierInfo *FI) const { + StringRef FName) const { if (CE->getNumArgs() < 1) return; // Check if the first argument is stack allocated. If so, issue a warning // because that's likely to be bad news. - const ProgramState *state = C.getState(); - const MemRegion *R = state->getSVal(CE->getArg(0)).getAsRegion(); + ProgramStateRef state = C.getState(); + const MemRegion *R = + state->getSVal(CE->getArg(0), C.getLocationContext()).getAsRegion(); if (!R || !isa<StackSpaceRegion>(R->getMemorySpace())) return; @@ -69,9 +70,9 @@ void MacOSXAPIChecker::CheckDispatchOnce(CheckerContext &C, const CallExpr *CE, BT_dispatchOnce.reset(new BugType("Improper use of 'dispatch_once'", "Mac OS X API")); - llvm::SmallString<256> S; + SmallString<256> S; llvm::raw_svector_ostream os(S); - os << "Call to '" << FI->getName() << "' uses"; + os << "Call to '" << FName << "' uses"; if (const VarRegion *VR = dyn_cast<VarRegion>(R)) os << " the local variable '" << VR->getDecl()->getName() << '\''; else @@ -92,27 +93,18 @@ void MacOSXAPIChecker::CheckDispatchOnce(CheckerContext &C, const CallExpr *CE, 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 ProgramState *state = C.getState(); - const Expr *Callee = CE->getCallee(); - const FunctionDecl *Fn = state->getSVal(Callee).getAsFunctionDecl(); - - if (!Fn) - return; - - const IdentifierInfo *FI = Fn->getIdentifier(); - if (!FI) + StringRef Name = C.getCalleeName(CE); + if (Name.empty()) return; SubChecker SC = - llvm::StringSwitch<SubChecker>(FI->getName()) + llvm::StringSwitch<SubChecker>(Name) .Cases("dispatch_once", "dispatch_once_f", &MacOSXAPIChecker::CheckDispatchOnce) .Default(NULL); if (SC) - (this->*SC)(C, CE, FI); + (this->*SC)(C, CE, Name); } //===----------------------------------------------------------------------===// diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp index 5631802..8bce88a 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp @@ -13,14 +13,21 @@ //===----------------------------------------------------------------------===// #include "ClangSACheckers.h" +#include "InterCheckerAPI.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/BugType.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h" +#include "clang/Basic/SourceManager.h" #include "llvm/ADT/ImmutableMap.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/STLExtras.h" +#include <climits> + using namespace clang; using namespace ento; @@ -35,10 +42,9 @@ public: RefState(Kind k, const Stmt *s) : K(k), S(s) {} bool isAllocated() const { return K == AllocateUnchecked; } - //bool isFailed() const { return K == AllocateFailed; } bool isReleased() const { return K == Released; } - //bool isEscaped() const { return K == Escaped; } - //bool isRelinquished() const { return K == Relinquished; } + + const Stmt *getStmt() const { return S; } bool operator==(const RefState &X) const { return K == X.K && S == X.S; @@ -62,61 +68,222 @@ public: } }; -class RegionState {}; +struct ReallocPair { + SymbolRef ReallocatedSym; + bool IsFreeOnFailure; + ReallocPair(SymbolRef S, bool F) : ReallocatedSym(S), IsFreeOnFailure(F) {} + void Profile(llvm::FoldingSetNodeID &ID) const { + ID.AddInteger(IsFreeOnFailure); + ID.AddPointer(ReallocatedSym); + } + bool operator==(const ReallocPair &X) const { + return ReallocatedSym == X.ReallocatedSym && + IsFreeOnFailure == X.IsFreeOnFailure; + } +}; -class MallocChecker : public Checker<eval::Call, check::DeadSymbols, check::EndPath, check::PreStmt<ReturnStmt>, check::Location, - check::Bind, eval::Assume> { - mutable llvm::OwningPtr<BuiltinBug> BT_DoubleFree; - mutable llvm::OwningPtr<BuiltinBug> BT_Leak; - mutable llvm::OwningPtr<BuiltinBug> BT_UseFree; - mutable llvm::OwningPtr<BuiltinBug> BT_UseRelinquished; - mutable llvm::OwningPtr<BuiltinBug> BT_BadFree; - mutable IdentifierInfo *II_malloc, *II_free, *II_realloc, *II_calloc; +typedef std::pair<const Stmt*, const MemRegion*> LeakInfo; + +class MallocChecker : public Checker<check::DeadSymbols, + check::EndPath, + check::PreStmt<ReturnStmt>, + check::PreStmt<CallExpr>, + check::PostStmt<CallExpr>, + check::PostStmt<BlockExpr>, + check::Location, + check::Bind, + eval::Assume, + check::RegionChanges> +{ + mutable OwningPtr<BugType> BT_DoubleFree; + mutable OwningPtr<BugType> BT_Leak; + mutable OwningPtr<BugType> BT_UseFree; + mutable OwningPtr<BugType> BT_BadFree; + mutable IdentifierInfo *II_malloc, *II_free, *II_realloc, *II_calloc, + *II_valloc, *II_reallocf, *II_strndup, *II_strdup; public: - MallocChecker() : II_malloc(0), II_free(0), II_realloc(0), II_calloc(0) {} + MallocChecker() : II_malloc(0), II_free(0), II_realloc(0), II_calloc(0), + II_valloc(0), II_reallocf(0), II_strndup(0), II_strdup(0) {} + + /// In pessimistic mode, the checker assumes that it does not know which + /// functions might free the memory. + struct ChecksFilter { + DefaultBool CMallocPessimistic; + DefaultBool CMallocOptimistic; + }; - bool evalCall(const CallExpr *CE, CheckerContext &C) const; + ChecksFilter Filter; + + void checkPreStmt(const CallExpr *S, CheckerContext &C) const; + void checkPostStmt(const CallExpr *CE, CheckerContext &C) const; + void checkPostStmt(const BlockExpr *BE, CheckerContext &C) const; void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const; - void checkEndPath(EndOfFunctionNodeBuilder &B, ExprEngine &Eng) const; + void checkEndPath(CheckerContext &C) const; void checkPreStmt(const ReturnStmt *S, CheckerContext &C) const; - const ProgramState *evalAssume(const ProgramState *state, SVal Cond, + ProgramStateRef evalAssume(ProgramStateRef state, SVal Cond, bool Assumption) 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; + ProgramStateRef + checkRegionChanges(ProgramStateRef state, + const StoreManager::InvalidatedSymbols *invalidated, + ArrayRef<const MemRegion *> ExplicitRegions, + ArrayRef<const MemRegion *> Regions, + const CallOrObjCMessage *Call) const; + bool wantsRegionChangeUpdate(ProgramStateRef state) const { + return true; + } private: - static void MallocMem(CheckerContext &C, const CallExpr *CE); - static void MallocMemReturnsAttr(CheckerContext &C, const CallExpr *CE, - const OwnershipAttr* Att); - static const ProgramState *MallocMemAux(CheckerContext &C, const CallExpr *CE, + void initIdentifierInfo(ASTContext &C) const; + + /// Check if this is one of the functions which can allocate/reallocate memory + /// pointed to by one of its arguments. + bool isMemFunction(const FunctionDecl *FD, ASTContext &C) const; + + static ProgramStateRef MallocMemReturnsAttr(CheckerContext &C, + const CallExpr *CE, + const OwnershipAttr* Att); + static ProgramStateRef MallocMemAux(CheckerContext &C, const CallExpr *CE, const Expr *SizeEx, SVal Init, - const ProgramState *state) { - return MallocMemAux(C, CE, state->getSVal(SizeEx), Init, state); + ProgramStateRef state) { + return MallocMemAux(C, CE, + state->getSVal(SizeEx, C.getLocationContext()), + Init, state); } - static const ProgramState *MallocMemAux(CheckerContext &C, const CallExpr *CE, + + static ProgramStateRef MallocMemAux(CheckerContext &C, const CallExpr *CE, SVal SizeEx, SVal Init, - const ProgramState *state); + ProgramStateRef state); + + /// Update the RefState to reflect the new memory allocation. + static ProgramStateRef MallocUpdateRefState(CheckerContext &C, + const CallExpr *CE, + ProgramStateRef state); + + ProgramStateRef FreeMemAttr(CheckerContext &C, const CallExpr *CE, + const OwnershipAttr* Att) const; + ProgramStateRef FreeMemAux(CheckerContext &C, const CallExpr *CE, + ProgramStateRef state, unsigned Num, + bool Hold) const; + + ProgramStateRef ReallocMem(CheckerContext &C, const CallExpr *CE, + bool FreesMemOnFailure) const; + static ProgramStateRef CallocMem(CheckerContext &C, const CallExpr *CE); + + bool checkEscape(SymbolRef Sym, const Stmt *S, CheckerContext &C) const; + bool checkUseAfterFree(SymbolRef Sym, CheckerContext &C, + const Stmt *S = 0) const; - void FreeMem(CheckerContext &C, const CallExpr *CE) const; - void FreeMemAttr(CheckerContext &C, const CallExpr *CE, - const OwnershipAttr* Att) const; - const ProgramState *FreeMemAux(CheckerContext &C, const CallExpr *CE, - const ProgramState *state, unsigned Num, bool Hold) const; + /// Check if the function is not known to us. So, for example, we could + /// conservatively assume it can free/reallocate it's pointer arguments. + bool doesNotFreeMemory(const CallOrObjCMessage *Call, + ProgramStateRef State) const; - void ReallocMem(CheckerContext &C, const CallExpr *CE) const; - static void CallocMem(CheckerContext &C, const CallExpr *CE); - 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; + + /// Find the location of the allocation for Sym on the path leading to the + /// exploded node N. + LeakInfo getAllocationSite(const ExplodedNode *N, SymbolRef Sym, + CheckerContext &C) const; + + void reportLeak(SymbolRef Sym, ExplodedNode *N, CheckerContext &C) const; + + /// 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 MallocBugVisitor : public BugReporterVisitorImpl<MallocBugVisitor> { + protected: + enum NotificationMode { + Normal, + ReallocationFailed + }; + + // The allocated region symbol tracked by the main analysis. + SymbolRef Sym; + + // The mode we are in, i.e. what kind of diagnostics will be emitted. + NotificationMode Mode; + + // A symbol from when the primary region should have been reallocated. + SymbolRef FailedReallocSymbol; + + public: + MallocBugVisitor(SymbolRef S) + : Sym(S), Mode(Normal), FailedReallocSymbol(0) {} + + virtual ~MallocBugVisitor() {} + + void Profile(llvm::FoldingSetNodeID &ID) const { + static int X = 0; + ID.AddPointer(&X); + ID.AddPointer(Sym); + } + + inline bool isAllocated(const RefState *S, const RefState *SPrev, + const Stmt *Stmt) { + // Did not track -> allocated. Other state (released) -> allocated. + return (Stmt && isa<CallExpr>(Stmt) && + (S && S->isAllocated()) && (!SPrev || !SPrev->isAllocated())); + } + + inline bool isReleased(const RefState *S, const RefState *SPrev, + const Stmt *Stmt) { + // Did not track -> released. Other state (allocated) -> released. + return (Stmt && isa<CallExpr>(Stmt) && + (S && S->isReleased()) && (!SPrev || !SPrev->isReleased())); + } + + inline bool isReallocFailedCheck(const RefState *S, const RefState *SPrev, + const Stmt *Stmt) { + // If the expression is not a call, and the state change is + // released -> allocated, it must be the realloc return value + // check. If we have to handle more cases here, it might be cleaner just + // to track this extra bit in the state itself. + return ((!Stmt || !isa<CallExpr>(Stmt)) && + (S && S->isAllocated()) && (SPrev && !SPrev->isAllocated())); + } + + PathDiagnosticPiece *VisitNode(const ExplodedNode *N, + const ExplodedNode *PrevN, + BugReporterContext &BRC, + BugReport &BR); + private: + class StackHintGeneratorForReallocationFailed + : public StackHintGeneratorForSymbol { + public: + StackHintGeneratorForReallocationFailed(SymbolRef S, StringRef M) + : StackHintGeneratorForSymbol(S, M) {} + + virtual std::string getMessageForArg(const Expr *ArgE, unsigned ArgIndex) { + SmallString<200> buf; + llvm::raw_svector_ostream os(buf); + + os << "Reallocation of "; + // Printed parameters start at 1, not 0. + printOrdinal(++ArgIndex, os); + os << " parameter failed"; + + return os.str(); + } + + virtual std::string getMessageForReturn(const CallExpr *CallExpr) { + return "Reallocation of returned value failed"; + } + }; + }; }; } // end anonymous namespace typedef llvm::ImmutableMap<SymbolRef, RefState> RegionStateTy; - +typedef llvm::ImmutableMap<SymbolRef, ReallocPair > ReallocMap; +class RegionState {}; +class ReallocPairs {}; namespace clang { namespace ento { template <> @@ -124,178 +291,230 @@ namespace ento { : public ProgramStatePartialTrait<RegionStateTy> { static void *GDMIndex() { static int x; return &x; } }; + + template <> + struct ProgramStateTrait<ReallocPairs> + : public ProgramStatePartialTrait<ReallocMap> { + static void *GDMIndex() { static int x; return &x; } + }; } } -bool MallocChecker::evalCall(const CallExpr *CE, CheckerContext &C) const { - const ProgramState *state = C.getState(); - const Expr *Callee = CE->getCallee(); - SVal L = state->getSVal(Callee); +namespace { +class StopTrackingCallback : public SymbolVisitor { + ProgramStateRef state; +public: + StopTrackingCallback(ProgramStateRef st) : state(st) {} + ProgramStateRef getState() const { return state; } - const FunctionDecl *FD = L.getAsFunctionDecl(); - if (!FD) - return false; + bool VisitSymbol(SymbolRef sym) { + state = state->remove<RegionState>(sym); + return true; + } +}; +} // end anonymous namespace - ASTContext &Ctx = C.getASTContext(); +void MallocChecker::initIdentifierInfo(ASTContext &Ctx) const { if (!II_malloc) II_malloc = &Ctx.Idents.get("malloc"); if (!II_free) II_free = &Ctx.Idents.get("free"); if (!II_realloc) II_realloc = &Ctx.Idents.get("realloc"); + if (!II_reallocf) + II_reallocf = &Ctx.Idents.get("reallocf"); if (!II_calloc) II_calloc = &Ctx.Idents.get("calloc"); + if (!II_valloc) + II_valloc = &Ctx.Idents.get("valloc"); + if (!II_strdup) + II_strdup = &Ctx.Idents.get("strdup"); + if (!II_strndup) + II_strndup = &Ctx.Idents.get("strndup"); +} - if (FD->getIdentifier() == II_malloc) { - MallocMem(C, CE); - return true; - } +bool MallocChecker::isMemFunction(const FunctionDecl *FD, ASTContext &C) const { + if (!FD) + return false; + IdentifierInfo *FunI = FD->getIdentifier(); + if (!FunI) + return false; - if (FD->getIdentifier() == II_free) { - FreeMem(C, CE); - return true; - } + initIdentifierInfo(C); - if (FD->getIdentifier() == II_realloc) { - ReallocMem(C, CE); + if (FunI == II_malloc || FunI == II_free || FunI == II_realloc || + FunI == II_reallocf || FunI == II_calloc || FunI == II_valloc || + FunI == II_strdup || FunI == II_strndup) return true; - } - if (FD->getIdentifier() == II_calloc) { - CallocMem(C, CE); + if (Filter.CMallocOptimistic && FD->hasAttrs() && + FD->specific_attr_begin<OwnershipAttr>() != + FD->specific_attr_end<OwnershipAttr>()) return true; - } - // Check all the attributes, if there are any. - // There can be multiple of these attributes. - bool rv = false; - if (FD->hasAttrs()) { - for (specific_attr_iterator<OwnershipAttr> - i = FD->specific_attr_begin<OwnershipAttr>(), - e = FD->specific_attr_end<OwnershipAttr>(); - i != e; ++i) { - switch ((*i)->getOwnKind()) { - case OwnershipAttr::Returns: { - MallocMemReturnsAttr(C, CE, *i); - rv = true; - break; - } - case OwnershipAttr::Takes: - case OwnershipAttr::Holds: { - FreeMemAttr(C, CE, *i); - rv = true; - break; - } - default: - break; - } - } - } - return rv; + + return false; } -void MallocChecker::MallocMem(CheckerContext &C, const CallExpr *CE) { - const ProgramState *state = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), - C.getState()); - C.addTransition(state); +void MallocChecker::checkPostStmt(const CallExpr *CE, CheckerContext &C) const { + const FunctionDecl *FD = C.getCalleeDecl(CE); + if (!FD) + return; + + initIdentifierInfo(C.getASTContext()); + IdentifierInfo *FunI = FD->getIdentifier(); + if (!FunI) + return; + + ProgramStateRef State = C.getState(); + if (FunI == II_malloc || FunI == II_valloc) { + if (CE->getNumArgs() < 1) + return; + State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State); + } else if (FunI == II_realloc) { + State = ReallocMem(C, CE, false); + } else if (FunI == II_reallocf) { + State = ReallocMem(C, CE, true); + } else if (FunI == II_calloc) { + State = CallocMem(C, CE); + } else if (FunI == II_free) { + State = FreeMemAux(C, CE, C.getState(), 0, false); + } else if (FunI == II_strdup) { + State = MallocUpdateRefState(C, CE, State); + } else if (FunI == II_strndup) { + State = MallocUpdateRefState(C, CE, State); + } else if (Filter.CMallocOptimistic) { + // Check all the attributes, if there are any. + // There can be multiple of these attributes. + if (FD->hasAttrs()) + for (specific_attr_iterator<OwnershipAttr> + i = FD->specific_attr_begin<OwnershipAttr>(), + e = FD->specific_attr_end<OwnershipAttr>(); + i != e; ++i) { + switch ((*i)->getOwnKind()) { + case OwnershipAttr::Returns: + State = MallocMemReturnsAttr(C, CE, *i); + break; + case OwnershipAttr::Takes: + case OwnershipAttr::Holds: + State = FreeMemAttr(C, CE, *i); + break; + } + } + } + C.addTransition(State); } -void MallocChecker::MallocMemReturnsAttr(CheckerContext &C, const CallExpr *CE, - const OwnershipAttr* Att) { +ProgramStateRef MallocChecker::MallocMemReturnsAttr(CheckerContext &C, + const CallExpr *CE, + const OwnershipAttr* Att) { if (Att->getModule() != "malloc") - return; + return 0; OwnershipAttr::args_iterator I = Att->args_begin(), E = Att->args_end(); if (I != E) { - const ProgramState *state = - MallocMemAux(C, CE, CE->getArg(*I), UndefinedVal(), C.getState()); - C.addTransition(state); - return; + return MallocMemAux(C, CE, CE->getArg(*I), UndefinedVal(), C.getState()); } - const ProgramState *state = MallocMemAux(C, CE, UnknownVal(), UndefinedVal(), - C.getState()); - C.addTransition(state); + return MallocMemAux(C, CE, UnknownVal(), UndefinedVal(), C.getState()); } -const ProgramState *MallocChecker::MallocMemAux(CheckerContext &C, +ProgramStateRef MallocChecker::MallocMemAux(CheckerContext &C, const CallExpr *CE, SVal Size, SVal Init, - const ProgramState *state) { - unsigned Count = C.getCurrentBlockCount(); - SValBuilder &svalBuilder = C.getSValBuilder(); + ProgramStateRef state) { + // Get the return value. + SVal retVal = state->getSVal(CE, C.getLocationContext()); - // Set the return value. - SVal retVal = svalBuilder.getConjuredSymbolVal(NULL, CE, CE->getType(), Count); - state = state->BindExpr(CE, retVal); + // We expect the malloc functions to return a pointer. + if (!isa<Loc>(retVal)) + return 0; // Fill the region with the initialization value. state = state->bindDefault(retVal, Init); // Set the region's extent equal to the Size parameter. - const SymbolicRegion *R = cast<SymbolicRegion>(retVal.getAsRegion()); - DefinedOrUnknownSVal Extent = R->getExtent(svalBuilder); - DefinedOrUnknownSVal DefinedSize = cast<DefinedOrUnknownSVal>(Size); - DefinedOrUnknownSVal extentMatchesSize = - svalBuilder.evalEQ(state, Extent, DefinedSize); - - state = state->assume(extentMatchesSize, true); - assert(state); + const SymbolicRegion *R = + dyn_cast_or_null<SymbolicRegion>(retVal.getAsRegion()); + if (!R) + return 0; + if (isa<DefinedOrUnknownSVal>(Size)) { + SValBuilder &svalBuilder = C.getSValBuilder(); + DefinedOrUnknownSVal Extent = R->getExtent(svalBuilder); + DefinedOrUnknownSVal DefinedSize = cast<DefinedOrUnknownSVal>(Size); + DefinedOrUnknownSVal extentMatchesSize = + svalBuilder.evalEQ(state, Extent, DefinedSize); + + state = state->assume(extentMatchesSize, true); + assert(state); + } + return MallocUpdateRefState(C, CE, state); +} + +ProgramStateRef MallocChecker::MallocUpdateRefState(CheckerContext &C, + const CallExpr *CE, + ProgramStateRef state) { + // Get the return value. + SVal retVal = state->getSVal(CE, C.getLocationContext()); + + // We expect the malloc functions to return a pointer. + if (!isa<Loc>(retVal)) + return 0; + SymbolRef Sym = retVal.getAsLocSymbol(); assert(Sym); // Set the symbol's state to Allocated. return state->set<RegionState>(Sym, RefState::getAllocateUnchecked(CE)); -} -void MallocChecker::FreeMem(CheckerContext &C, const CallExpr *CE) const { - const ProgramState *state = FreeMemAux(C, CE, C.getState(), 0, false); - - if (state) - C.addTransition(state); } -void MallocChecker::FreeMemAttr(CheckerContext &C, const CallExpr *CE, - const OwnershipAttr* Att) const { +ProgramStateRef MallocChecker::FreeMemAttr(CheckerContext &C, + const CallExpr *CE, + const OwnershipAttr* Att) const { if (Att->getModule() != "malloc") - return; + return 0; + + ProgramStateRef State = C.getState(); for (OwnershipAttr::args_iterator I = Att->args_begin(), E = Att->args_end(); I != E; ++I) { - const ProgramState *state = FreeMemAux(C, CE, C.getState(), *I, - Att->getOwnKind() == OwnershipAttr::Holds); - if (state) - C.addTransition(state); + ProgramStateRef StateI = FreeMemAux(C, CE, State, *I, + Att->getOwnKind() == OwnershipAttr::Holds); + if (StateI) + State = StateI; } + return State; } -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); +ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C, + const CallExpr *CE, + ProgramStateRef state, + unsigned Num, + bool Hold) const { + if (CE->getNumArgs() < (Num + 1)) + return 0; + const Expr *ArgExpr = CE->getArg(Num); + SVal ArgVal = state->getSVal(ArgExpr, C.getLocationContext()); + if (!isa<DefinedOrUnknownSVal>(ArgVal)) + return 0; DefinedOrUnknownSVal location = cast<DefinedOrUnknownSVal>(ArgVal); // Check for null dereferences. if (!isa<Loc>(location)) - return state; - - // FIXME: Technically using 'Assume' here can result in a path - // bifurcation. In such cases we need to return two states, not just one. - const ProgramState *notNullState, *nullState; - llvm::tie(notNullState, nullState) = state->assume(location); + return 0; // The explicit NULL case, no operation is performed. + ProgramStateRef notNullState, nullState; + llvm::tie(notNullState, nullState) = state->assume(location); if (nullState && !notNullState) - return nullState; - - assert(notNullState); + return 0; // Unknown values could easily be okay // Undefined values are handled elsewhere if (ArgVal.isUnknownOrUndef()) - return notNullState; + return 0; const MemRegion *R = ArgVal.getAsRegion(); @@ -303,7 +522,7 @@ const ProgramState *MallocChecker::FreeMemAux(CheckerContext &C, const CallExpr // Non-region locations (labels and fixed addresses) also shouldn't be freed. if (!R) { ReportBadFree(C, ArgVal, ArgExpr->getSourceRange()); - return NULL; + return 0; } R = R->StripCasts(); @@ -311,7 +530,7 @@ const ProgramState *MallocChecker::FreeMemAux(CheckerContext &C, const CallExpr // Blocks might show up as heap data, but should not be free()d if (isa<BlockDataRegion>(R)) { ReportBadFree(C, ArgVal, ArgExpr->getSourceRange()); - return NULL; + return 0; } const MemSpaceRegion *MS = R->getMemorySpace(); @@ -327,14 +546,14 @@ const ProgramState *MallocChecker::FreeMemAux(CheckerContext &C, const CallExpr // False negatives are better than false positives. ReportBadFree(C, ArgVal, ArgExpr->getSourceRange()); - return NULL; + return 0; } const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(R); // Various cases could lead to non-symbol values here. // For now, ignore them. if (!SR) - return notNullState; + return 0; SymbolRef Sym = SR->getSymbol(); const RefState *RS = state->get<RegionState>(Sym); @@ -343,27 +562,28 @@ const ProgramState *MallocChecker::FreeMemAux(CheckerContext &C, const CallExpr // called on a pointer that does not get its pointee directly from malloc(). // Full support of this requires inter-procedural analysis. if (!RS) - return notNullState; + return 0; // Check double free. if (RS->isReleased()) { if (ExplodedNode *N = C.generateSink()) { if (!BT_DoubleFree) BT_DoubleFree.reset( - new BuiltinBug("Double free", - "Try to free a memory block that has been released")); - // FIXME: should find where it's freed last time. + new BugType("Double free", "Memory Error")); BugReport *R = new BugReport(*BT_DoubleFree, - BT_DoubleFree->getDescription(), N); + "Attempt to free released memory", N); + R->addRange(ArgExpr->getSourceRange()); + R->markInteresting(Sym); + R->addVisitor(new MallocBugVisitor(Sym)); C.EmitReport(R); } - return NULL; + return 0; } // Normal free. if (Hold) - return notNullState->set<RegionState>(Sym, RefState::getRelinquished(CE)); - return notNullState->set<RegionState>(Sym, RefState::getReleased(CE)); + return state->set<RegionState>(Sym, RefState::getRelinquished(CE)); + return state->set<RegionState>(Sym, RefState::getReleased(CE)); } bool MallocChecker::SummarizeValue(raw_ostream &os, SVal V) { @@ -400,8 +620,7 @@ bool MallocChecker::SummarizeRegion(raw_ostream &os, default: { const MemSpaceRegion *MS = MR->getMemorySpace(); - switch (MS->getKind()) { - case MemRegion::StackLocalsSpaceRegionKind: { + if (isa<StackLocalsSpaceRegion>(MS)) { const VarRegion *VR = dyn_cast<VarRegion>(MR); const VarDecl *VD; if (VR) @@ -415,7 +634,8 @@ bool MallocChecker::SummarizeRegion(raw_ostream &os, os << "the address of a local stack variable"; return true; } - case MemRegion::StackArgumentsSpaceRegionKind: { + + if (isa<StackArgumentsSpaceRegion>(MS)) { const VarRegion *VR = dyn_cast<VarRegion>(MR); const VarDecl *VD; if (VR) @@ -429,8 +649,8 @@ bool MallocChecker::SummarizeRegion(raw_ostream &os, os << "the address of a parameter"; return true; } - case MemRegion::NonStaticGlobalSpaceRegionKind: - case MemRegion::StaticGlobalSpaceRegionKind: { + + if (isa<GlobalsSpaceRegion>(MS)) { const VarRegion *VR = dyn_cast<VarRegion>(MR); const VarDecl *VD; if (VR) @@ -447,9 +667,8 @@ bool MallocChecker::SummarizeRegion(raw_ostream &os, os << "the address of a global variable"; return true; } - default: - return false; - } + + return false; } } } @@ -458,9 +677,9 @@ void MallocChecker::ReportBadFree(CheckerContext &C, SVal ArgVal, SourceRange range) const { if (ExplodedNode *N = C.generateSink()) { if (!BT_BadFree) - BT_BadFree.reset(new BuiltinBug("Bad free")); + BT_BadFree.reset(new BugType("Bad free", "Memory Error")); - llvm::SmallString<100> buf; + SmallString<100> buf; llvm::raw_svector_ostream os(buf); const MemRegion *MR = ArgVal.getAsRegion(); @@ -487,16 +706,25 @@ void MallocChecker::ReportBadFree(CheckerContext &C, SVal ArgVal, } BugReport *R = new BugReport(*BT_BadFree, os.str(), N); + R->markInteresting(MR); R->addRange(range); C.EmitReport(R); } } -void MallocChecker::ReallocMem(CheckerContext &C, const CallExpr *CE) const { - const ProgramState *state = C.getState(); +ProgramStateRef MallocChecker::ReallocMem(CheckerContext &C, + const CallExpr *CE, + bool FreesOnFail) const { + if (CE->getNumArgs() < 2) + return 0; + + ProgramStateRef state = C.getState(); const Expr *arg0Expr = CE->getArg(0); - DefinedOrUnknownSVal arg0Val - = cast<DefinedOrUnknownSVal>(state->getSVal(arg0Expr)); + const LocationContext *LCtx = C.getLocationContext(); + SVal Arg0Val = state->getSVal(arg0Expr, LCtx); + if (!isa<DefinedOrUnknownSVal>(Arg0Val)) + return 0; + DefinedOrUnknownSVal arg0Val = cast<DefinedOrUnknownSVal>(Arg0Val); SValBuilder &svalBuilder = C.getSValBuilder(); @@ -506,65 +734,166 @@ void MallocChecker::ReallocMem(CheckerContext &C, const CallExpr *CE) const { // Get the size argument. If there is no size arg then give up. const Expr *Arg1 = CE->getArg(1); if (!Arg1) - return; + return 0; // Get the value of the size argument. - DefinedOrUnknownSVal Arg1Val = - cast<DefinedOrUnknownSVal>(state->getSVal(Arg1)); + SVal Arg1ValG = state->getSVal(Arg1, LCtx); + if (!isa<DefinedOrUnknownSVal>(Arg1ValG)) + return 0; + DefinedOrUnknownSVal Arg1Val = cast<DefinedOrUnknownSVal>(Arg1ValG); // Compare the size argument to 0. DefinedOrUnknownSVal SizeZero = svalBuilder.evalEQ(state, Arg1Val, svalBuilder.makeIntValWithPtrWidth(0, false)); + ProgramStateRef StatePtrIsNull, StatePtrNotNull; + llvm::tie(StatePtrIsNull, StatePtrNotNull) = state->assume(PtrEQ); + ProgramStateRef StateSizeIsZero, StateSizeNotZero; + llvm::tie(StateSizeIsZero, StateSizeNotZero) = state->assume(SizeZero); + // We only assume exceptional states if they are definitely true; if the + // state is under-constrained, assume regular realloc behavior. + bool PrtIsNull = StatePtrIsNull && !StatePtrNotNull; + bool SizeIsZero = StateSizeIsZero && !StateSizeNotZero; + // If the ptr is NULL and the size is not 0, the call is equivalent to // malloc(size). - 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., - // CheckedNull, CheckedNonNull. - - SymbolRef Sym = arg0Val.getAsLocSymbol(); - if (Sym) - stateEqual = stateEqual->set<RegionState>(Sym, RefState::getReleased(CE)); - - const ProgramState *stateMalloc = MallocMemAux(C, CE, CE->getArg(1), - UndefinedVal(), stateEqual); - C.addTransition(stateMalloc); + if ( PrtIsNull && !SizeIsZero) { + ProgramStateRef stateMalloc = MallocMemAux(C, CE, CE->getArg(1), + UndefinedVal(), StatePtrIsNull); + return stateMalloc; } - if (const ProgramState *stateNotEqual = state->assume(PtrEQ, false)) { - // If the size is 0, free the memory. - if (const ProgramState *stateSizeZero = stateNotEqual->assume(SizeZero, true)) - if (const ProgramState *stateFree = - FreeMemAux(C, CE, stateSizeZero, 0, false)) { + if (PrtIsNull && SizeIsZero) + return 0; + + // Get the from and to pointer symbols as in toPtr = realloc(fromPtr, size). + assert(!PrtIsNull); + SymbolRef FromPtr = arg0Val.getAsSymbol(); + SVal RetVal = state->getSVal(CE, LCtx); + SymbolRef ToPtr = RetVal.getAsSymbol(); + if (!FromPtr || !ToPtr) + return 0; + + // If the size is 0, free the memory. + if (SizeIsZero) + if (ProgramStateRef stateFree = FreeMemAux(C, CE, StateSizeIsZero,0,false)){ + // The semantics of the return value are: + // If size was equal to 0, either NULL or a pointer suitable to be passed + // to free() is returned. + stateFree = stateFree->set<ReallocPairs>(ToPtr, + ReallocPair(FromPtr, FreesOnFail)); + C.getSymbolManager().addSymbolDependency(ToPtr, FromPtr); + return stateFree; + } - // Bind the return value to NULL because it is now free. - C.addTransition(stateFree->BindExpr(CE, svalBuilder.makeNull(), true)); - } - 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 ProgramState *stateRealloc = MallocMemAux(C, CE, CE->getArg(1), - UnknownVal(), stateFree); - C.addTransition(stateRealloc); - } + // Default behavior. + if (ProgramStateRef stateFree = FreeMemAux(C, CE, state, 0, false)) { + // FIXME: We should copy the content of the original buffer. + ProgramStateRef stateRealloc = MallocMemAux(C, CE, CE->getArg(1), + UnknownVal(), stateFree); + if (!stateRealloc) + return 0; + stateRealloc = stateRealloc->set<ReallocPairs>(ToPtr, + ReallocPair(FromPtr, FreesOnFail)); + C.getSymbolManager().addSymbolDependency(ToPtr, FromPtr); + return stateRealloc; } + return 0; } -void MallocChecker::CallocMem(CheckerContext &C, const CallExpr *CE) { - const ProgramState *state = C.getState(); - SValBuilder &svalBuilder = C.getSValBuilder(); +ProgramStateRef MallocChecker::CallocMem(CheckerContext &C, const CallExpr *CE){ + if (CE->getNumArgs() < 2) + return 0; - SVal count = state->getSVal(CE->getArg(0)); - SVal elementSize = state->getSVal(CE->getArg(1)); + ProgramStateRef state = C.getState(); + SValBuilder &svalBuilder = C.getSValBuilder(); + const LocationContext *LCtx = C.getLocationContext(); + SVal count = state->getSVal(CE->getArg(0), LCtx); + SVal elementSize = state->getSVal(CE->getArg(1), LCtx); SVal TotalSize = svalBuilder.evalBinOp(state, BO_Mul, count, elementSize, svalBuilder.getContext().getSizeType()); SVal zeroVal = svalBuilder.makeZeroVal(svalBuilder.getContext().CharTy); - C.addTransition(MallocMemAux(C, CE, TotalSize, zeroVal, state)); + return MallocMemAux(C, CE, TotalSize, zeroVal, state); +} + +LeakInfo +MallocChecker::getAllocationSite(const ExplodedNode *N, SymbolRef Sym, + CheckerContext &C) const { + const LocationContext *LeakContext = N->getLocationContext(); + // Walk the ExplodedGraph backwards and find the first node that referred to + // the tracked symbol. + const ExplodedNode *AllocNode = N; + const MemRegion *ReferenceRegion = 0; + + while (N) { + ProgramStateRef State = N->getState(); + if (!State->get<RegionState>(Sym)) + break; + + // Find the most recent expression bound to the symbol in the current + // context. + if (!ReferenceRegion) { + if (const MemRegion *MR = C.getLocationRegionIfPostStore(N)) { + SVal Val = State->getSVal(MR); + if (Val.getAsLocSymbol() == Sym) + ReferenceRegion = MR; + } + } + + // Allocation node, is the last node in the current context in which the + // symbol was tracked. + if (N->getLocationContext() == LeakContext) + AllocNode = N; + N = N->pred_empty() ? NULL : *(N->pred_begin()); + } + + ProgramPoint P = AllocNode->getLocation(); + const Stmt *AllocationStmt = 0; + if (isa<StmtPoint>(P)) + AllocationStmt = cast<StmtPoint>(P).getStmt(); + + return LeakInfo(AllocationStmt, ReferenceRegion); +} + +void MallocChecker::reportLeak(SymbolRef Sym, ExplodedNode *N, + CheckerContext &C) const { + assert(N); + if (!BT_Leak) { + BT_Leak.reset(new BugType("Memory leak", "Memory Error")); + // Leaks should not be reported if they are post-dominated by a sink: + // (1) Sinks are higher importance bugs. + // (2) NoReturnFunctionChecker uses sink nodes to represent paths ending + // with __noreturn functions such as assert() or exit(). We choose not + // to report leaks on such paths. + BT_Leak->setSuppressOnSink(true); + } + + // Most bug reports are cached at the location where they occurred. + // With leaks, we want to unique them by the location where they were + // allocated, and only report a single path. + PathDiagnosticLocation LocUsedForUniqueing; + const Stmt *AllocStmt = 0; + const MemRegion *Region = 0; + llvm::tie(AllocStmt, Region) = getAllocationSite(N, Sym, C); + if (AllocStmt) + LocUsedForUniqueing = PathDiagnosticLocation::createBegin(AllocStmt, + C.getSourceManager(), N->getLocationContext()); + + SmallString<200> buf; + llvm::raw_svector_ostream os(buf); + os << "Memory is never released; potential leak"; + if (Region) { + os << " of memory pointed to by '"; + Region->dumpPretty(os); + os <<'\''; + } + + BugReport *R = new BugReport(*BT_Leak, os.str(), N, LocUsedForUniqueing); + R->markInteresting(Sym); + R->addVisitor(new MallocBugVisitor(Sym)); + C.EmitReport(R); } void MallocChecker::checkDeadSymbols(SymbolReaper &SymReaper, @@ -573,174 +902,562 @@ void MallocChecker::checkDeadSymbols(SymbolReaper &SymReaper, if (!SymReaper.hasDeadSymbols()) return; - const ProgramState *state = C.getState(); + ProgramStateRef state = C.getState(); RegionStateTy RS = state->get<RegionState>(); RegionStateTy::Factory &F = state->get_context<RegionState>(); bool generateReport = false; - + llvm::SmallVector<SymbolRef, 2> Errors; for (RegionStateTy::iterator I = RS.begin(), E = RS.end(); I != E; ++I) { if (SymReaper.isDead(I->first)) { - if (I->second.isAllocated()) + if (I->second.isAllocated()) { generateReport = true; - + Errors.push_back(I->first); + } // Remove the dead symbol from the map. RS = F.remove(RS, I->first); } } - 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); + // Cleanup the Realloc Pairs Map. + ReallocMap RP = state->get<ReallocPairs>(); + for (ReallocMap::iterator I = RP.begin(), E = RP.end(); I != E; ++I) { + if (SymReaper.isDead(I->first) || + SymReaper.isDead(I->second.ReallocatedSym)) { + state = state->remove<ReallocPairs>(I->first); + } + } + + // Generate leak node. + static SimpleProgramPointTag Tag("MallocChecker : DeadSymbolsLeak"); + ExplodedNode *N = C.addTransition(C.getState(), C.getPredecessor(), &Tag); + + if (generateReport) { + for (llvm::SmallVector<SymbolRef, 2>::iterator + I = Errors.begin(), E = Errors.end(); I != E; ++I) { + reportLeak(*I, N, C); + } } + C.addTransition(state->set<RegionState>(RS), N); } -void MallocChecker::checkEndPath(EndOfFunctionNodeBuilder &B, - ExprEngine &Eng) const { - const ProgramState *state = B.getState(); +void MallocChecker::checkEndPath(CheckerContext &C) const { + ProgramStateRef state = C.getState(); RegionStateTy M = state->get<RegionState>(); + // If inside inlined call, skip it. + if (C.getLocationContext()->getParent() != 0) + return; + for (RegionStateTy::iterator I = M.begin(), E = M.end(); I != E; ++I) { RefState RS = I->second; if (RS.isAllocated()) { - ExplodedNode *N = B.generateNode(state); - if (N) { - if (!BT_Leak) - BT_Leak.reset(new BuiltinBug("Memory leak", - "Allocated memory never released. Potential memory leak.")); - BugReport *R = new BugReport(*BT_Leak, BT_Leak->getDescription(), N); - Eng.getBugReporter().EmitReport(R); - } + ExplodedNode *N = C.addTransition(state); + if (N) + reportLeak(I->first, N, C); } } } -void MallocChecker::checkPreStmt(const ReturnStmt *S, CheckerContext &C) const { - const Expr *retExpr = S->getRetValue(); - if (!retExpr) +bool MallocChecker::checkEscape(SymbolRef Sym, const Stmt *S, + CheckerContext &C) const { + ProgramStateRef state = C.getState(); + const RefState *RS = state->get<RegionState>(Sym); + if (!RS) + return false; + + if (RS->isAllocated()) { + state = state->set<RegionState>(Sym, RefState::getEscaped(S)); + C.addTransition(state); + return true; + } + return false; +} + +void MallocChecker::checkPreStmt(const CallExpr *CE, CheckerContext &C) const { + if (isMemFunction(C.getCalleeDecl(CE), C.getASTContext())) return; - const ProgramState *state = C.getState(); + // Check use after free, when a freed pointer is passed to a call. + ProgramStateRef State = C.getState(); + for (CallExpr::const_arg_iterator I = CE->arg_begin(), + E = CE->arg_end(); I != E; ++I) { + const Expr *A = *I; + if (A->getType().getTypePtr()->isAnyPointerType()) { + SymbolRef Sym = State->getSVal(A, C.getLocationContext()).getAsSymbol(); + if (!Sym) + continue; + if (checkUseAfterFree(Sym, C, A)) + return; + } + } +} - SymbolRef Sym = state->getSVal(retExpr).getAsSymbol(); - if (!Sym) +void MallocChecker::checkPreStmt(const ReturnStmt *S, CheckerContext &C) const { + const Expr *E = S->getRetValue(); + if (!E) return; - const RefState *RS = state->get<RegionState>(Sym); - if (!RS) + // Check if we are returning a symbol. + SVal RetVal = C.getState()->getSVal(E, C.getLocationContext()); + SymbolRef Sym = RetVal.getAsSymbol(); + if (!Sym) + // If we are returning a field of the allocated struct or an array element, + // the callee could still free the memory. + // TODO: This logic should be a part of generic symbol escape callback. + if (const MemRegion *MR = RetVal.getAsRegion()) + if (isa<FieldRegion>(MR) || isa<ElementRegion>(MR)) + if (const SymbolicRegion *BMR = + dyn_cast<SymbolicRegion>(MR->getBaseRegion())) + Sym = BMR->getSymbol(); + if (!Sym) return; - // FIXME: check other cases. - if (RS->isAllocated()) - state = state->set<RegionState>(Sym, RefState::getEscaped(S)); + // Check if we are returning freed memory. + if (checkUseAfterFree(Sym, C, E)) + return; - C.addTransition(state); + // If this function body is not inlined, check if the symbol is escaping. + if (C.getLocationContext()->getParent() == 0) + checkEscape(Sym, E, C); } -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. +// TODO: Blocks should be either inlined or should call invalidate regions +// upon invocation. After that's in place, special casing here will not be +// needed. +void MallocChecker::checkPostStmt(const BlockExpr *BE, + CheckerContext &C) const { - RegionStateTy RS = state->get<RegionState>(); + // Scan the BlockDecRefExprs for any object the retain count checker + // may be tracking. + if (!BE->getBlockDecl()->hasCaptures()) + return; - for (RegionStateTy::iterator I = RS.begin(), E = RS.end(); I != E; ++I) { - // If the symbol is assumed to NULL, this will return an APSInt*. - if (state->getSymVal(I.getKey())) - state = state->set<RegionState>(I.getKey(),RefState::getAllocateFailed()); + ProgramStateRef state = C.getState(); + const BlockDataRegion *R = + cast<BlockDataRegion>(state->getSVal(BE, + C.getLocationContext()).getAsRegion()); + + BlockDataRegion::referenced_vars_iterator I = R->referenced_vars_begin(), + E = R->referenced_vars_end(); + + if (I == E) + return; + + SmallVector<const MemRegion*, 10> Regions; + const LocationContext *LC = C.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); } - return state; + state = + state->scanReachableSymbols<StopTrackingCallback>(Regions.data(), + Regions.data() + Regions.size()).getState(); + C.addTransition(state); +} + +bool MallocChecker::checkUseAfterFree(SymbolRef Sym, CheckerContext &C, + const Stmt *S) const { + assert(Sym); + const RefState *RS = C.getState()->get<RegionState>(Sym); + if (RS && RS->isReleased()) { + if (ExplodedNode *N = C.generateSink()) { + if (!BT_UseFree) + BT_UseFree.reset(new BugType("Use-after-free", "Memory Error")); + + BugReport *R = new BugReport(*BT_UseFree, + "Use of memory after it is freed",N); + if (S) + R->addRange(S->getSourceRange()); + R->markInteresting(Sym); + R->addVisitor(new MallocBugVisitor(Sym)); + C.EmitReport(R); + return true; + } + } + return false; } // Check if the location is a freed symbolic region. 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); - if (RS && RS->isReleased()) { - if (ExplodedNode *N = C.generateNode()) { - if (!BT_UseFree) - BT_UseFree.reset(new BuiltinBug("Use dynamically allocated memory " - "after it is freed.")); - - BugReport *R = new BugReport(*BT_UseFree, BT_UseFree->getDescription(), - N); - C.EmitReport(R); + if (Sym) + checkUseAfterFree(Sym, C); +} + +//===----------------------------------------------------------------------===// +// Check various ways a symbol can be invalidated. +// TODO: This logic (the next 3 functions) is copied/similar to the +// RetainRelease checker. We might want to factor this out. +//===----------------------------------------------------------------------===// + +// Stop tracking symbols when a value escapes as a result of checkBind. +// A value escapes in three possible cases: +// (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. +void MallocChecker::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; + ProgramStateRef 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 (!escapes) { + // Case 4: We do not currently model what happens when a symbol is + // assigned to a struct field, so be conservative here and let the symbol + // go. TODO: This could definitely be improved upon. + escapes = !isa<VarRegion>(regionLoc->getRegion()); + } + } + + // 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); +} + +// If a symbolic region is assumed to NULL (or another constant), stop tracking +// it - assuming that allocation failed on this path. +ProgramStateRef MallocChecker::evalAssume(ProgramStateRef state, + SVal Cond, + bool Assumption) const { + RegionStateTy RS = state->get<RegionState>(); + for (RegionStateTy::iterator I = RS.begin(), E = RS.end(); I != E; ++I) { + // If the symbol is assumed to NULL or another constant, this will + // return an APSInt*. + if (state->getSymVal(I.getKey())) + state = state->remove<RegionState>(I.getKey()); + } + + // Realloc returns 0 when reallocation fails, which means that we should + // restore the state of the pointer being reallocated. + ReallocMap RP = state->get<ReallocPairs>(); + for (ReallocMap::iterator I = RP.begin(), E = RP.end(); I != E; ++I) { + // If the symbol is assumed to NULL or another constant, this will + // return an APSInt*. + if (state->getSymVal(I.getKey())) { + SymbolRef ReallocSym = I.getData().ReallocatedSym; + const RefState *RS = state->get<RegionState>(ReallocSym); + if (RS) { + if (RS->isReleased() && ! I.getData().IsFreeOnFailure) + state = state->set<RegionState>(ReallocSym, + RefState::getAllocateUnchecked(RS->getStmt())); } + state = state->remove<ReallocPairs>(I.getKey()); } } + + return state; } -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. +// Check if the function is known to us. So, for example, we could +// conservatively assume it can free/reallocate it's pointer arguments. +// (We assume that the pointers cannot escape through calls to system +// functions not handled by this checker.) +bool MallocChecker::doesNotFreeMemory(const CallOrObjCMessage *Call, + ProgramStateRef State) const { + if (!Call) + return false; - const ProgramState *state = C.getState(); - DefinedOrUnknownSVal l = cast<DefinedOrUnknownSVal>(location); + // For now, assume that any C++ call can free memory. + // TODO: If we want to be more optimistic here, we'll need to make sure that + // regions escape to C++ containers. They seem to do that even now, but for + // mysterious reasons. + if (Call->isCXXCall()) + return false; - // Check for null dereferences. - if (!isa<Loc>(l)) - return; + const Decl *D = Call->getDecl(); + if (!D) + return false; - // Before checking if the state is null, check if 'val' has a RefState. - // Only then should we check for null and bifurcate the state. - SymbolRef Sym = val.getLocSymbolInBase(); - if (Sym) { - if (const RefState *RS = state->get<RegionState>(Sym)) { - // If ptr is NULL, no operation is performed. - const ProgramState *notNullState, *nullState; - llvm::tie(notNullState, nullState) = state->assume(l); - - // Generate a transition for 'nullState' to record the assumption - // that the state was null. - if (nullState) - C.addTransition(nullState); - - if (!notNullState) - return; + ASTContext &ASTC = State->getStateManager().getContext(); + + // If it's one of the allocation functions we can reason about, we model + // its behavior explicitly. + if (isa<FunctionDecl>(D) && isMemFunction(cast<FunctionDecl>(D), ASTC)) { + return true; + } + + // If it's not a system call, assume it frees memory. + SourceManager &SM = ASTC.getSourceManager(); + if (!SM.isInSystemHeader(D->getLocation())) + return false; + + // Process C/ObjC functions. + if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) { + // White list the system functions whose arguments escape. + const IdentifierInfo *II = FD->getIdentifier(); + if (!II) + return true; + StringRef FName = II->getName(); + + // White list thread local storage. + if (FName.equals("pthread_setspecific")) + return false; - if (RS->isAllocated()) { - // Something we presently own is being assigned somewhere. - const MemRegion *AR = location.getAsRegion(); - if (!AR) - return; - AR = AR->StripCasts()->getBaseRegion(); - do { - // If it is on the stack, we still own it. - if (AR->hasStackNonParametersStorage()) - break; - - // If the state can't represent this binding, we still own it. - if (notNullState == (notNullState->bindLoc(cast<Loc>(location), - UnknownVal()))) - break; - - // We no longer own this pointer. - notNullState = - notNullState->set<RegionState>(Sym, - RefState::getRelinquished(BindS)); + // White list the 'XXXNoCopy' ObjC functions. + if (FName.endswith("NoCopy")) { + // Look for the deallocator argument. We know that the memory ownership + // is not transfered only if the deallocator argument is + // 'kCFAllocatorNull'. + for (unsigned i = 1; i < Call->getNumArgs(); ++i) { + const Expr *ArgE = Call->getArg(i)->IgnoreParenCasts(); + if (const DeclRefExpr *DE = dyn_cast<DeclRefExpr>(ArgE)) { + StringRef DeallocatorName = DE->getFoundDecl()->getName(); + if (DeallocatorName == "kCFAllocatorNull") + return true; } - while (false); } - C.addTransition(notNullState); + return false; + } + + // PR12101 + // Many CoreFoundation and CoreGraphics might allow a tracked object + // to escape. + if (Call->isCFCGAllowingEscape(FName)) + return false; + + // Associating streams with malloced buffers. The pointer can escape if + // 'closefn' is specified (and if that function does free memory). + // Currently, we do not inspect the 'closefn' function (PR12101). + if (FName == "funopen") + if (Call->getNumArgs() >= 4 && !Call->getArgSVal(4).isConstant(0)) + return false; + + // Do not warn on pointers passed to 'setbuf' when used with std streams, + // these leaks might be intentional when setting the buffer for stdio. + // http://stackoverflow.com/questions/2671151/who-frees-setvbuf-buffer + if (FName == "setbuf" || FName =="setbuffer" || + FName == "setlinebuf" || FName == "setvbuf") { + if (Call->getNumArgs() >= 1) + if (const DeclRefExpr *Arg = + dyn_cast<DeclRefExpr>(Call->getArg(0)->IgnoreParenCasts())) + if (const VarDecl *D = dyn_cast<VarDecl>(Arg->getDecl())) + if (D->getCanonicalDecl()->getName().find("std") + != StringRef::npos) + return false; + } + + // A bunch of other functions, which take ownership of a pointer (See retain + // release checker). Not all the parameters here are invalidated, but the + // Malloc checker cannot differentiate between them. The right way of doing + // this would be to implement a pointer escapes callback. + if (FName == "CVPixelBufferCreateWithBytes" || + FName == "CGBitmapContextCreateWithData" || + FName == "CVPixelBufferCreateWithPlanarBytes" || + FName == "OSAtomicEnqueue") { + return false; + } + + // Whitelist NSXXInsertXX, for example NSMapInsertIfAbsent, since they can + // be deallocated by NSMapRemove. + if (FName.startswith("NS") && (FName.find("Insert") != StringRef::npos)) + return false; + + // Otherwise, assume that the function does not free memory. + // Most system calls, do not free the memory. + return true; + + // Process ObjC functions. + } else if (const ObjCMethodDecl * ObjCD = dyn_cast<ObjCMethodDecl>(D)) { + Selector S = ObjCD->getSelector(); + + // White list the ObjC functions which do free memory. + // - Anything containing 'freeWhenDone' param set to 1. + // Ex: dataWithBytesNoCopy:length:freeWhenDone. + for (unsigned i = 1; i < S.getNumArgs(); ++i) { + if (S.getNameForSlot(i).equals("freeWhenDone")) { + if (Call->getArgSVal(i).isConstant(1)) + return false; + else + return true; + } } + + // If the first selector ends with NoCopy, assume that the ownership is + // transfered as well. + // Ex: [NSData dataWithBytesNoCopy:bytes length:10]; + if (S.getNameForSlot(0).endswith("NoCopy")) { + return false; + } + + // Otherwise, assume that the function does not free memory. + // Most system calls, do not free the memory. + return true; } + + // Otherwise, assume that the function can free memory. + return false; + } -void ento::registerMallocChecker(CheckerManager &mgr) { - mgr.registerChecker<MallocChecker>(); +// If the symbol we are tracking is invalidated, but not explicitly (ex: the &p +// escapes, when we are tracking p), do not track the symbol as we cannot reason +// about it anymore. +ProgramStateRef +MallocChecker::checkRegionChanges(ProgramStateRef State, + const StoreManager::InvalidatedSymbols *invalidated, + ArrayRef<const MemRegion *> ExplicitRegions, + ArrayRef<const MemRegion *> Regions, + const CallOrObjCMessage *Call) const { + if (!invalidated || invalidated->empty()) + return State; + llvm::SmallPtrSet<SymbolRef, 8> WhitelistedSymbols; + + // If it's a call which might free or reallocate memory, we assume that all + // regions (explicit and implicit) escaped. + + // Otherwise, whitelist explicit pointers; we still can track them. + if (!Call || doesNotFreeMemory(Call, State)) { + for (ArrayRef<const MemRegion *>::iterator I = ExplicitRegions.begin(), + E = ExplicitRegions.end(); I != E; ++I) { + if (const SymbolicRegion *R = (*I)->StripCasts()->getAs<SymbolicRegion>()) + WhitelistedSymbols.insert(R->getSymbol()); + } + } + + for (StoreManager::InvalidatedSymbols::const_iterator I=invalidated->begin(), + E = invalidated->end(); I!=E; ++I) { + SymbolRef sym = *I; + if (WhitelistedSymbols.count(sym)) + continue; + // The symbol escaped. + if (const RefState *RS = State->get<RegionState>(sym)) + State = State->set<RegionState>(sym, RefState::getEscaped(RS->getStmt())); + } + return State; +} + +static SymbolRef findFailedReallocSymbol(ProgramStateRef currState, + ProgramStateRef prevState) { + ReallocMap currMap = currState->get<ReallocPairs>(); + ReallocMap prevMap = prevState->get<ReallocPairs>(); + + for (ReallocMap::iterator I = prevMap.begin(), E = prevMap.end(); + I != E; ++I) { + SymbolRef sym = I.getKey(); + if (!currMap.lookup(sym)) + return sym; + } + + return NULL; } + +PathDiagnosticPiece * +MallocChecker::MallocBugVisitor::VisitNode(const ExplodedNode *N, + const ExplodedNode *PrevN, + BugReporterContext &BRC, + BugReport &BR) { + ProgramStateRef state = N->getState(); + ProgramStateRef statePrev = PrevN->getState(); + + const RefState *RS = state->get<RegionState>(Sym); + const RefState *RSPrev = statePrev->get<RegionState>(Sym); + if (!RS && !RSPrev) + return 0; + + const Stmt *S = 0; + const char *Msg = 0; + StackHintGeneratorForSymbol *StackHint = 0; + + // Retrieve the associated statement. + ProgramPoint ProgLoc = N->getLocation(); + if (isa<StmtPoint>(ProgLoc)) + S = cast<StmtPoint>(ProgLoc).getStmt(); + // If an assumption was made on a branch, it should be caught + // here by looking at the state transition. + if (isa<BlockEdge>(ProgLoc)) { + const CFGBlock *srcBlk = cast<BlockEdge>(ProgLoc).getSrc(); + S = srcBlk->getTerminator(); + } + if (!S) + return 0; + + // Find out if this is an interesting point and what is the kind. + if (Mode == Normal) { + if (isAllocated(RS, RSPrev, S)) { + Msg = "Memory is allocated"; + StackHint = new StackHintGeneratorForSymbol(Sym, + "Returned allocated memory"); + } else if (isReleased(RS, RSPrev, S)) { + Msg = "Memory is released"; + StackHint = new StackHintGeneratorForSymbol(Sym, + "Returned released memory"); + } else if (isReallocFailedCheck(RS, RSPrev, S)) { + Mode = ReallocationFailed; + Msg = "Reallocation failed"; + StackHint = new StackHintGeneratorForReallocationFailed(Sym, + "Reallocation failed"); + + if (SymbolRef sym = findFailedReallocSymbol(state, statePrev)) { + // Is it possible to fail two reallocs WITHOUT testing in between? + assert((!FailedReallocSymbol || FailedReallocSymbol == sym) && + "We only support one failed realloc at a time."); + BR.markInteresting(sym); + FailedReallocSymbol = sym; + } + } + + // We are in a special mode if a reallocation failed later in the path. + } else if (Mode == ReallocationFailed) { + assert(FailedReallocSymbol && "No symbol to look for."); + + // Is this is the first appearance of the reallocated symbol? + if (!statePrev->get<RegionState>(FailedReallocSymbol)) { + // If we ever hit this assert, that means BugReporter has decided to skip + // node pairs or visit them out of order. + assert(state->get<RegionState>(FailedReallocSymbol) && + "Missed the reallocation point"); + + // We're at the reallocation point. + Msg = "Attempt to reallocate memory"; + StackHint = new StackHintGeneratorForSymbol(Sym, + "Returned reallocated memory"); + FailedReallocSymbol = NULL; + Mode = Normal; + } + } + + if (!Msg) + return 0; + assert(StackHint); + + // Generate the extra diagnostic. + PathDiagnosticLocation Pos(S, BRC.getSourceManager(), + N->getLocationContext()); + return new PathDiagnosticEventPiece(Pos, Msg, true, StackHint); +} + + +#define REGISTER_CHECKER(name) \ +void ento::register##name(CheckerManager &mgr) {\ + registerCStringCheckerBasic(mgr); \ + mgr.registerChecker<MallocChecker>()->Filter.C##name = true;\ +} + +REGISTER_CHECKER(MallocPessimistic) +REGISTER_CHECKER(MallocOptimistic) diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MallocOverflowSecurityChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MallocOverflowSecurityChecker.cpp index cf5501a..daec418 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MallocOverflowSecurityChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MallocOverflowSecurityChecker.cpp @@ -205,7 +205,7 @@ void MallocOverflowSecurityChecker::OutputPossibleOverflows( // Delete any possible overflows which have a comparison. CheckOverflowOps c(PossibleMallocOverflows, BR.getContext()); - c.Visit(mgr.getAnalysisContext(D)->getBody()); + c.Visit(mgr.getAnalysisDeclContext(D)->getBody()); // Output warnings for all overflows that are left. for (CheckOverflowOps::theVecType::iterator @@ -214,11 +214,10 @@ void MallocOverflowSecurityChecker::OutputPossibleOverflows( i != e; ++i) { SourceRange R = i->mulop->getSourceRange(); - BR.EmitBasicReport("MallocOverflowSecurityChecker", + BR.EmitBasicReport(D, "malloc() size overflow", categories::UnixAPI, "the computation of the size of the memory allocation may overflow", PathDiagnosticLocation::createOperatorLoc(i->mulop, - BR.getSourceManager()), - &R, 1); + BR.getSourceManager()), &R, 1); } } diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MallocSizeofChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MallocSizeofChecker.cpp new file mode 100644 index 0000000..08a9da1 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MallocSizeofChecker.cpp @@ -0,0 +1,211 @@ +// MallocSizeofChecker.cpp - Check for dubious malloc arguments ---*- C++ -*-=// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Reports inconsistencies between the casted type of the return value of a +// malloc/calloc/realloc call and the operand of any sizeof expressions +// contained within its argument(s). +// +//===----------------------------------------------------------------------===// + +#include "ClangSACheckers.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" +#include "clang/AST/StmtVisitor.h" +#include "clang/AST/TypeLoc.h" +#include "llvm/ADT/SmallString.h" + +using namespace clang; +using namespace ento; + +namespace { + +typedef std::pair<const TypeSourceInfo *, const CallExpr *> TypeCallPair; +typedef llvm::PointerUnion<const Stmt *, const VarDecl *> ExprParent; + +class CastedAllocFinder + : public ConstStmtVisitor<CastedAllocFinder, TypeCallPair> { + IdentifierInfo *II_malloc, *II_calloc, *II_realloc; + +public: + struct CallRecord { + ExprParent CastedExprParent; + const Expr *CastedExpr; + const TypeSourceInfo *ExplicitCastType; + const CallExpr *AllocCall; + + CallRecord(ExprParent CastedExprParent, const Expr *CastedExpr, + const TypeSourceInfo *ExplicitCastType, + const CallExpr *AllocCall) + : CastedExprParent(CastedExprParent), CastedExpr(CastedExpr), + ExplicitCastType(ExplicitCastType), AllocCall(AllocCall) {} + }; + + typedef std::vector<CallRecord> CallVec; + CallVec Calls; + + CastedAllocFinder(ASTContext *Ctx) : + II_malloc(&Ctx->Idents.get("malloc")), + II_calloc(&Ctx->Idents.get("calloc")), + II_realloc(&Ctx->Idents.get("realloc")) {} + + void VisitChild(ExprParent Parent, const Stmt *S) { + TypeCallPair AllocCall = Visit(S); + if (AllocCall.second && AllocCall.second != S) + Calls.push_back(CallRecord(Parent, cast<Expr>(S), AllocCall.first, + AllocCall.second)); + } + + void VisitChildren(const Stmt *S) { + for (Stmt::const_child_iterator I = S->child_begin(), E = S->child_end(); + I!=E; ++I) + if (const Stmt *child = *I) + VisitChild(S, child); + } + + TypeCallPair VisitCastExpr(const CastExpr *E) { + return Visit(E->getSubExpr()); + } + + TypeCallPair VisitExplicitCastExpr(const ExplicitCastExpr *E) { + return TypeCallPair(E->getTypeInfoAsWritten(), + Visit(E->getSubExpr()).second); + } + + TypeCallPair VisitParenExpr(const ParenExpr *E) { + return Visit(E->getSubExpr()); + } + + TypeCallPair VisitStmt(const Stmt *S) { + VisitChildren(S); + return TypeCallPair(); + } + + TypeCallPair VisitCallExpr(const CallExpr *E) { + VisitChildren(E); + const FunctionDecl *FD = E->getDirectCallee(); + if (FD) { + IdentifierInfo *II = FD->getIdentifier(); + if (II == II_malloc || II == II_calloc || II == II_realloc) + return TypeCallPair((const TypeSourceInfo *)0, E); + } + return TypeCallPair(); + } + + TypeCallPair VisitDeclStmt(const DeclStmt *S) { + for (DeclStmt::const_decl_iterator I = S->decl_begin(), E = S->decl_end(); + I!=E; ++I) + if (const VarDecl *VD = dyn_cast<VarDecl>(*I)) + if (const Expr *Init = VD->getInit()) + VisitChild(VD, Init); + return TypeCallPair(); + } +}; + +class SizeofFinder : public ConstStmtVisitor<SizeofFinder> { +public: + std::vector<const UnaryExprOrTypeTraitExpr *> Sizeofs; + + void VisitBinMul(const BinaryOperator *E) { + Visit(E->getLHS()); + Visit(E->getRHS()); + } + + void VisitBinAdd(const BinaryOperator *E) { + Visit(E->getLHS()); + Visit(E->getRHS()); + } + + void VisitImplicitCastExpr(const ImplicitCastExpr *E) { + return Visit(E->getSubExpr()); + } + + void VisitParenExpr(const ParenExpr *E) { + return Visit(E->getSubExpr()); + } + + void VisitUnaryExprOrTypeTraitExpr(const UnaryExprOrTypeTraitExpr *E) { + if (E->getKind() != UETT_SizeOf) + return; + + Sizeofs.push_back(E); + } +}; + +class MallocSizeofChecker : public Checker<check::ASTCodeBody> { +public: + void checkASTCodeBody(const Decl *D, AnalysisManager& mgr, + BugReporter &BR) const { + AnalysisDeclContext *ADC = mgr.getAnalysisDeclContext(D); + CastedAllocFinder Finder(&BR.getContext()); + Finder.Visit(D->getBody()); + for (CastedAllocFinder::CallVec::iterator i = Finder.Calls.begin(), + e = Finder.Calls.end(); i != e; ++i) { + QualType CastedType = i->CastedExpr->getType(); + if (!CastedType->isPointerType()) + continue; + QualType PointeeType = CastedType->getAs<PointerType>()->getPointeeType(); + if (PointeeType->isVoidType()) + continue; + + for (CallExpr::const_arg_iterator ai = i->AllocCall->arg_begin(), + ae = i->AllocCall->arg_end(); ai != ae; ++ai) { + if (!(*ai)->getType()->isIntegerType()) + continue; + + SizeofFinder SFinder; + SFinder.Visit(*ai); + if (SFinder.Sizeofs.size() != 1) + continue; + + QualType SizeofType = SFinder.Sizeofs[0]->getTypeOfArgument(); + if (!BR.getContext().hasSameUnqualifiedType(PointeeType, SizeofType)) { + const TypeSourceInfo *TSI = 0; + if (i->CastedExprParent.is<const VarDecl *>()) { + TSI = + i->CastedExprParent.get<const VarDecl *>()->getTypeSourceInfo(); + } else { + TSI = i->ExplicitCastType; + } + + SmallString<64> buf; + llvm::raw_svector_ostream OS(buf); + + OS << "Result of '" + << i->AllocCall->getDirectCallee()->getIdentifier()->getName() + << "' is converted to type '" + << CastedType.getAsString() << "', whose pointee type '" + << PointeeType.getAsString() << "' is incompatible with " + << "sizeof operand type '" << SizeofType.getAsString() << "'"; + llvm::SmallVector<SourceRange, 4> Ranges; + Ranges.push_back(i->AllocCall->getCallee()->getSourceRange()); + Ranges.push_back(SFinder.Sizeofs[0]->getSourceRange()); + if (TSI) + Ranges.push_back(TSI->getTypeLoc().getSourceRange()); + + PathDiagnosticLocation L = + PathDiagnosticLocation::createBegin(i->AllocCall->getCallee(), + BR.getSourceManager(), ADC); + + BR.EmitBasicReport(D, "allocator sizeof operand mismatch", + categories::UnixAPI, + OS.str(), + L, Ranges.data(), Ranges.size()); + } + } + } + } +}; + +} + +void ento::registerMallocSizeofChecker(CheckerManager &mgr) { + mgr.registerChecker<MallocSizeofChecker>(); +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NSAutoreleasePoolChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NSAutoreleasePoolChecker.cpp index 7f74a7d..4989ba8 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NSAutoreleasePoolChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NSAutoreleasePoolChecker.cpp @@ -19,6 +19,7 @@ #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h" @@ -31,7 +32,7 @@ using namespace ento; namespace { class NSAutoreleasePoolChecker : public Checker<check::PreObjCMessage> { - + mutable OwningPtr<BugType> BT; mutable Selector releaseS; public: @@ -65,20 +66,24 @@ void NSAutoreleasePoolChecker::checkPreObjCMessage(ObjCMessage msg, // Sending 'release' message? if (msg.getSelector() != releaseS) 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", L, &R, 1); + + if (!BT) + BT.reset(new BugType("Use -drain instead of -release", + "API Upgrade (Apple)")); + + ExplodedNode *N = C.addTransition(); + if (!N) { + assert(0); + return; + } + + BugReport *Report = new BugReport(*BT, "Use -drain instead of -release when " + "using NSAutoreleasePool and garbage collection", N); + Report->addRange(msg.getSourceRange()); + C.EmitReport(Report); } void ento::registerNSAutoreleasePoolChecker(CheckerManager &mgr) { - if (mgr.getLangOptions().getGC() != LangOptions::NonGC) + if (mgr.getLangOpts().getGC() != LangOptions::NonGC) mgr.registerChecker<NSAutoreleasePoolChecker>(); } diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp index 5678998..f826573 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp @@ -74,7 +74,7 @@ void NSErrorMethodChecker::checkASTDecl(const ObjCMethodDecl *D, "error occurred"; PathDiagnosticLocation L = PathDiagnosticLocation::create(D, BR.getSourceManager()); - BR.EmitBasicReport("Bad return type when passing NSError**", + BR.EmitBasicReport(D, "Bad return type when passing NSError**", "Coding conventions (Apple)", err, L); } } @@ -122,7 +122,7 @@ void CFErrorFunctionChecker::checkASTDecl(const FunctionDecl *D, "error occurred"; PathDiagnosticLocation L = PathDiagnosticLocation::create(D, BR.getSourceManager()); - BR.EmitBasicReport("Bad return type when passing CFErrorRef*", + BR.EmitBasicReport(D, "Bad return type when passing CFErrorRef*", "Coding conventions (Apple)", err, L); } } @@ -182,7 +182,7 @@ namespace ento { } template <typename T> -static bool hasFlag(SVal val, const ProgramState *state) { +static bool hasFlag(SVal val, ProgramStateRef state) { if (SymbolRef sym = val.getAsSymbol()) if (const unsigned *attachedFlags = state->get<T>(sym)) return *attachedFlags; @@ -190,7 +190,7 @@ static bool hasFlag(SVal val, const ProgramState *state) { } template <typename T> -static void setFlag(const ProgramState *state, SVal val, CheckerContext &C) { +static void setFlag(ProgramStateRef 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)); @@ -198,7 +198,7 @@ static void setFlag(const ProgramState *state, SVal val, CheckerContext &C) { static QualType parameterTypeFromSVal(SVal val, CheckerContext &C) { const StackFrameContext * - SFC = C.getPredecessor()->getLocationContext()->getCurrentStackFrame(); + SFC = C.getLocationContext()->getCurrentStackFrame(); if (const loc::MemRegionVal* X = dyn_cast<loc::MemRegionVal>(&val)) { const MemRegion* R = X->getRegion(); if (const VarRegion *VR = R->getAs<VarRegion>()) @@ -220,7 +220,7 @@ void NSOrCFErrorDerefChecker::checkLocation(SVal loc, bool isLoad, return; ASTContext &Ctx = C.getASTContext(); - const ProgramState *state = C.getState(); + ProgramStateRef 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 @@ -253,7 +253,7 @@ void NSOrCFErrorDerefChecker::checkEvent(ImplicitNullDerefEvent event) const { return; SVal loc = event.Location; - const ProgramState *state = event.SinkNode->getState(); + ProgramStateRef state = event.SinkNode->getState(); BugReporter &BR = *event.BR; bool isNSError = hasFlag<NSErrorOut>(loc, state); diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NoReturnFunctionChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NoReturnFunctionChecker.cpp index 81f1924..c2d7c09 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NoReturnFunctionChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NoReturnFunctionChecker.cpp @@ -36,13 +36,13 @@ public: void NoReturnFunctionChecker::checkPostStmt(const CallExpr *CE, CheckerContext &C) const { - const ProgramState *state = C.getState(); + ProgramStateRef state = C.getState(); const Expr *Callee = CE->getCallee(); bool BuildSinks = getFunctionExtInfo(Callee->getType()).getNoReturn(); if (!BuildSinks) { - SVal L = state->getSVal(Callee); + SVal L = state->getSVal(Callee, C.getLocationContext()); const FunctionDecl *FD = L.getAsFunctionDecl(); if (!FD) return; diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/OSAtomicChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/OSAtomicChecker.cpp index f426265..7b724d2 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/OSAtomicChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/OSAtomicChecker.cpp @@ -32,31 +32,32 @@ private: ExprEngine &Eng, ExplodedNode *Pred, ExplodedNodeSet &Dst) const; - - ExplodedNode *generateNode(const ProgramState *State, - ExplodedNode *Pred, const CallExpr *Statement, - StmtNodeBuilder &B, ExplodedNodeSet &Dst) const; }; } +static StringRef getCalleeName(ProgramStateRef State, + const CallExpr *CE, + const LocationContext *LCtx) { + const Expr *Callee = CE->getCallee(); + SVal L = State->getSVal(Callee, LCtx); + const FunctionDecl *funDecl = L.getAsFunctionDecl(); + if (!funDecl) + return StringRef(); + IdentifierInfo *funI = funDecl->getIdentifier(); + if (!funI) + return StringRef(); + return funI->getName(); +} + 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(); - if (!FD) + StringRef FName = getCalleeName(Pred->getState(), + CE, Pred->getLocationContext()); + if (FName.empty()) return false; - const IdentifierInfo *II = FD->getIdentifier(); - if (!II) - return false; - - StringRef FName(II->getName()); - // Check for compare and swap. if (FName.startswith("OSAtomicCompareAndSwap") || FName.startswith("objc_atomicCompareAndSwap")) @@ -66,17 +67,6 @@ bool OSAtomicChecker::inlineCall(const CallExpr *CE, return false; } -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, @@ -85,7 +75,6 @@ bool OSAtomicChecker::evalOSAtomicCompareAndSwap(const CallExpr *CE, if (CE->getNumArgs() != 3) return false; - StmtNodeBuilder &Builder = Eng.getBuilder(); ASTContext &Ctx = Eng.getContext(); const Expr *oldValueExpr = CE->getArg(0); QualType oldValueType = Ctx.getCanonicalType(oldValueExpr->getType()); @@ -115,9 +104,10 @@ bool OSAtomicChecker::evalOSAtomicCompareAndSwap(const CallExpr *CE, static SimpleProgramPointTag OSAtomicStoreTag("OSAtomicChecker : Store"); // Load 'theValue'. - const ProgramState *state = Pred->getState(); + ProgramStateRef state = Pred->getState(); + const LocationContext *LCtx = Pred->getLocationContext(); ExplodedNodeSet Tmp; - SVal location = state->getSVal(theValueExpr); + SVal location = state->getSVal(theValueExpr, LCtx); // Here we should use the value type of the region as the load type, because // we are simulating the semantics of the function, not the semantics of // passing argument. So the type of theValue expr is not we are loading. @@ -130,15 +120,12 @@ bool OSAtomicChecker::evalOSAtomicCompareAndSwap(const CallExpr *CE, dyn_cast_or_null<TypedValueRegion>(location.getAsRegion())) { LoadTy = TR->getValueType(); } - Eng.evalLoad(Tmp, theValueExpr, Pred, - state, location, &OSAtomicLoadTag, LoadTy); + Eng.evalLoad(Tmp, CE, 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. - Builder.BuildSinks = true; + // If no nodes were generated, other checkers must have generated sinks. + // We return an empty Dst. return true; } @@ -146,14 +133,14 @@ bool OSAtomicChecker::evalOSAtomicCompareAndSwap(const CallExpr *CE, I != E; ++I) { ExplodedNode *N = *I; - const ProgramState *stateLoad = N->getState(); + ProgramStateRef 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 // to bind a value. - SVal theValueVal_untested = stateLoad->getSVal(theValueExpr, true); + SVal theValueVal_untested = stateLoad->getSVal(theValueExpr, LCtx, true); - SVal oldValueVal_untested = stateLoad->getSVal(oldValueExpr); + SVal oldValueVal_untested = stateLoad->getSVal(oldValueExpr, LCtx); // FIXME: Issue an error. if (theValueVal_untested.isUndef() || oldValueVal_untested.isUndef()) { @@ -171,13 +158,13 @@ bool OSAtomicChecker::evalOSAtomicCompareAndSwap(const CallExpr *CE, DefinedOrUnknownSVal Cmp = svalBuilder.evalEQ(stateLoad,theValueVal,oldValueVal); - const ProgramState *stateEqual = stateLoad->assume(Cmp, true); + ProgramStateRef stateEqual = stateLoad->assume(Cmp, true); // Were they equal? if (stateEqual) { // Perform the store. ExplodedNodeSet TmpStore; - SVal val = stateEqual->getSVal(newValueExpr); + SVal val = stateEqual->getSVal(newValueExpr, LCtx); // Handle implicit value casts. if (const TypedValueRegion *R = @@ -185,40 +172,41 @@ bool OSAtomicChecker::evalOSAtomicCompareAndSwap(const CallExpr *CE, val = svalBuilder.evalCast(val,R->getValueType(), newValueExpr->getType()); } - Eng.evalStore(TmpStore, NULL, theValueExpr, N, - stateEqual, location, val, &OSAtomicStoreTag); + Eng.evalStore(TmpStore, CE, 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. - Builder.BuildSinks = true; + // If no nodes were generated, other checkers must have generated sinks. + // We return an empty Dst. return true; } - + + StmtNodeBuilder B(TmpStore, Dst, Eng.getBuilderContext()); // Now bind the result of the comparison. for (ExplodedNodeSet::iterator I2 = TmpStore.begin(), E2 = TmpStore.end(); I2 != E2; ++I2) { ExplodedNode *predNew = *I2; - const ProgramState *stateNew = predNew->getState(); + ProgramStateRef 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 = Eng.getSValBuilder().makeTruthVal(true, T); - generateNode(stateNew->BindExpr(CE, Res), predNew, CE, Builder, Dst); + B.generateNode(CE, predNew, stateNew->BindExpr(CE, LCtx, Res), + false, this); } } // Were they not equal? - if (const ProgramState *stateNotEqual = stateLoad->assume(Cmp, false)) { + if (ProgramStateRef 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 = Eng.getSValBuilder().makeTruthVal(false, CE->getType()); - generateNode(stateNotEqual->BindExpr(CE, Res), N, CE, Builder, Dst); + StmtNodeBuilder B(N, Dst, Eng.getBuilderContext()); + B.generateNode(CE, N, stateNotEqual->BindExpr(CE, LCtx, Res), + false, this); } } diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCAtSyncChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCAtSyncChecker.cpp index 3e4e07b..777e9ea 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCAtSyncChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCAtSyncChecker.cpp @@ -13,6 +13,7 @@ //===----------------------------------------------------------------------===// #include "ClangSACheckers.h" +#include "clang/AST/StmtObjC.h" #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" @@ -26,8 +27,8 @@ using namespace ento; namespace { class ObjCAtSyncChecker : public Checker< check::PreStmt<ObjCAtSynchronizedStmt> > { - mutable llvm::OwningPtr<BuiltinBug> BT_null; - mutable llvm::OwningPtr<BuiltinBug> BT_undef; + mutable OwningPtr<BuiltinBug> BT_null; + mutable OwningPtr<BuiltinBug> BT_undef; public: void checkPreStmt(const ObjCAtSynchronizedStmt *S, CheckerContext &C) const; @@ -38,8 +39,8 @@ void ObjCAtSyncChecker::checkPreStmt(const ObjCAtSynchronizedStmt *S, CheckerContext &C) const { const Expr *Ex = S->getSynchExpr(); - const ProgramState *state = C.getState(); - SVal V = state->getSVal(Ex); + ProgramStateRef state = C.getState(); + SVal V = state->getSVal(Ex, C.getLocationContext()); // Uninitialized value used for the mutex? if (isa<UndefinedVal>(V)) { @@ -49,7 +50,8 @@ void ObjCAtSyncChecker::checkPreStmt(const ObjCAtSynchronizedStmt *S, "for @synchronized")); BugReport *report = new BugReport(*BT_undef, BT_undef->getDescription(), N); - report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, Ex)); + report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, Ex, + report)); C.EmitReport(report); } return; @@ -59,20 +61,21 @@ void ObjCAtSyncChecker::checkPreStmt(const ObjCAtSynchronizedStmt *S, return; // Check for null mutexes. - const ProgramState *notNullState, *nullState; + ProgramStateRef notNullState, nullState; llvm::tie(notNullState, nullState) = state->assume(cast<DefinedSVal>(V)); if (nullState) { if (!notNullState) { // Generate an error node. This isn't a sink since // a null mutex just means no synchronization occurs. - if (ExplodedNode *N = C.generateNode(nullState)) { + if (ExplodedNode *N = C.addTransition(nullState)) { if (!BT_null) BT_null.reset(new BuiltinBug("Nil value used as mutex for @synchronized() " "(no synchronization will occur)")); BugReport *report = new BugReport(*BT_null, BT_null->getDescription(), N); - report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, Ex)); + report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, Ex, + report)); C.EmitReport(report); return; @@ -88,6 +91,6 @@ void ObjCAtSyncChecker::checkPreStmt(const ObjCAtSynchronizedStmt *S, } void ento::registerObjCAtSyncChecker(CheckerManager &mgr) { - if (mgr.getLangOptions().ObjC2) + if (mgr.getLangOpts().ObjC2) mgr.registerChecker<ObjCAtSyncChecker>(); } diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCContainersASTChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCContainersASTChecker.cpp new file mode 100644 index 0000000..f2929c0 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCContainersASTChecker.cpp @@ -0,0 +1,174 @@ +//== ObjCContainersASTChecker.cpp - CoreFoundation containers API *- C++ -*-==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// An AST checker that looks for common pitfalls when using 'CFArray', +// 'CFDictionary', 'CFSet' APIs. +// +//===----------------------------------------------------------------------===// +#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/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/raw_ostream.h" + +using namespace clang; +using namespace ento; + +namespace { +class WalkAST : public StmtVisitor<WalkAST> { + BugReporter &BR; + AnalysisDeclContext* AC; + ASTContext &ASTC; + uint64_t PtrWidth; + + static const unsigned InvalidArgIndex = UINT_MAX; + + /// Check if the type has pointer size (very conservative). + inline bool isPointerSize(const Type *T) { + if (!T) + return true; + if (T->isIncompleteType()) + return true; + return (ASTC.getTypeSize(T) == PtrWidth); + } + + /// Check if the type is a pointer/array to pointer sized values. + inline bool hasPointerToPointerSizedType(const Expr *E) { + QualType T = E->getType(); + + // The type could be either a pointer or array. + const Type *TP = T.getTypePtr(); + QualType PointeeT = TP->getPointeeType(); + if (!PointeeT.isNull()) { + // If the type is a pointer to an array, check the size of the array + // elements. To avoid false positives coming from assumption that the + // values x and &x are equal when x is an array. + if (const Type *TElem = PointeeT->getArrayElementTypeNoTypeQual()) + if (isPointerSize(TElem)) + return true; + + // Else, check the pointee size. + return isPointerSize(PointeeT.getTypePtr()); + } + + if (const Type *TElem = TP->getArrayElementTypeNoTypeQual()) + return isPointerSize(TElem); + + // The type must be an array/pointer type. + + // This could be a null constant, which is allowed. + if (E->isNullPointerConstant(ASTC, Expr::NPC_ValueDependentIsNull)) + return true; + return false; + } + +public: + WalkAST(BugReporter &br, AnalysisDeclContext* ac) + : BR(br), AC(ac), ASTC(AC->getASTContext()), + PtrWidth(ASTC.getTargetInfo().getPointerWidth(0)) {} + + // Statement visitor methods. + void VisitChildren(Stmt *S); + void VisitStmt(Stmt *S) { VisitChildren(S); } + void VisitCallExpr(CallExpr *CE); +}; +} // end anonymous namespace + +static StringRef getCalleeName(CallExpr *CE) { + const FunctionDecl *FD = CE->getDirectCallee(); + if (!FD) + return StringRef(); + + IdentifierInfo *II = FD->getIdentifier(); + if (!II) // if no identifier, not a simple C function + return StringRef(); + + return II->getName(); +} + +void WalkAST::VisitCallExpr(CallExpr *CE) { + StringRef Name = getCalleeName(CE); + if (Name.empty()) + return; + + const Expr *Arg = 0; + unsigned ArgNum = InvalidArgIndex; + + if (Name.equals("CFArrayCreate") || Name.equals("CFSetCreate")) { + ArgNum = 1; + Arg = CE->getArg(ArgNum)->IgnoreParenCasts(); + if (hasPointerToPointerSizedType(Arg)) + return; + } + + if (Arg == 0 && Name.equals("CFDictionaryCreate")) { + // Check first argument. + ArgNum = 1; + Arg = CE->getArg(ArgNum)->IgnoreParenCasts(); + if (hasPointerToPointerSizedType(Arg)) { + // Check second argument. + ArgNum = 2; + Arg = CE->getArg(ArgNum)->IgnoreParenCasts(); + if (hasPointerToPointerSizedType(Arg)) + // Both are good, return. + return; + } + } + + if (ArgNum != InvalidArgIndex) { + assert(ArgNum == 1 || ArgNum == 2); + + SmallString<256> BufName; + llvm::raw_svector_ostream OsName(BufName); + assert(ArgNum == 1 || ArgNum == 2); + OsName << " Invalid use of '" << Name << "'" ; + + SmallString<256> Buf; + llvm::raw_svector_ostream Os(Buf); + Os << " The "<< ((ArgNum == 1) ? "first" : "second") << " argument to '" + << Name << "' must be a C array of pointer-sized values, not '" + << Arg->getType().getAsString() << "'"; + + SourceRange R = Arg->getSourceRange(); + PathDiagnosticLocation CELoc = + PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); + BR.EmitBasicReport(AC->getDecl(), + OsName.str(), categories::CoreFoundationObjectiveC, + Os.str(), CELoc, &R, 1); + } + + // Recurse and check children. + VisitChildren(CE); +} + +void WalkAST::VisitChildren(Stmt *S) { + for (Stmt::child_iterator I = S->child_begin(), E = S->child_end(); I!=E; ++I) + if (Stmt *child = *I) + Visit(child); +} + +namespace { +class ObjCContainersASTChecker : public Checker<check::ASTCodeBody> { +public: + + void checkASTCodeBody(const Decl *D, AnalysisManager& Mgr, + BugReporter &BR) const { + WalkAST walker(BR, Mgr.getAnalysisDeclContext(D)); + walker.Visit(D->getBody()); + } +}; +} + +void ento::registerObjCContainersASTChecker(CheckerManager &mgr) { + mgr.registerChecker<ObjCContainersASTChecker>(); +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCContainersChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCContainersChecker.cpp new file mode 100644 index 0000000..f4655b6 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCContainersChecker.cpp @@ -0,0 +1,159 @@ +//== ObjCContainersChecker.cpp - Path sensitive checker for CFArray *- C++ -*=// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Performs path sensitive checks of Core Foundation static containers like +// CFArray. +// 1) Check for buffer overflows: +// In CFArrayGetArrayAtIndex( myArray, index), if the index is outside the +// index space of theArray (0 to N-1 inclusive (where N is the count of +// theArray), the behavior is undefined. +// +//===----------------------------------------------------------------------===// + +#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/PathSensitive/ProgramStateTrait.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/AST/ParentMap.h" + +using namespace clang; +using namespace ento; + +namespace { +class ObjCContainersChecker : public Checker< check::PreStmt<CallExpr>, + check::PostStmt<CallExpr> > { + mutable OwningPtr<BugType> BT; + inline void initBugType() const { + if (!BT) + BT.reset(new BugType("CFArray API", + categories::CoreFoundationObjectiveC)); + } + + inline SymbolRef getArraySym(const Expr *E, CheckerContext &C) const { + SVal ArrayRef = C.getState()->getSVal(E, C.getLocationContext()); + SymbolRef ArraySym = ArrayRef.getAsSymbol(); + return ArraySym; + } + + void addSizeInfo(const Expr *Array, const Expr *Size, + CheckerContext &C) const; + +public: + /// A tag to id this checker. + static void *getTag() { static int Tag; return &Tag; } + + void checkPostStmt(const CallExpr *CE, CheckerContext &C) const; + void checkPreStmt(const CallExpr *CE, CheckerContext &C) const; +}; +} // end anonymous namespace + +// ProgramState trait - a map from array symbol to it's state. +typedef llvm::ImmutableMap<SymbolRef, DefinedSVal> ArraySizeM; + +namespace { struct ArraySizeMap {}; } +namespace clang { namespace ento { +template<> struct ProgramStateTrait<ArraySizeMap> + : public ProgramStatePartialTrait<ArraySizeM > { + static void *GDMIndex() { return ObjCContainersChecker::getTag(); } +}; +}} + +void ObjCContainersChecker::addSizeInfo(const Expr *Array, const Expr *Size, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + SVal SizeV = State->getSVal(Size, C.getLocationContext()); + // Undefined is reported by another checker. + if (SizeV.isUnknownOrUndef()) + return; + + // Get the ArrayRef symbol. + SVal ArrayRef = State->getSVal(Array, C.getLocationContext()); + SymbolRef ArraySym = ArrayRef.getAsSymbol(); + if (!ArraySym) + return; + + C.addTransition(State->set<ArraySizeMap>(ArraySym, cast<DefinedSVal>(SizeV))); + return; +} + +void ObjCContainersChecker::checkPostStmt(const CallExpr *CE, + CheckerContext &C) const { + StringRef Name = C.getCalleeName(CE); + if (Name.empty() || CE->getNumArgs() < 1) + return; + + // Add array size information to the state. + if (Name.equals("CFArrayCreate")) { + if (CE->getNumArgs() < 3) + return; + // Note, we can visit the Create method in the post-visit because + // the CFIndex parameter is passed in by value and will not be invalidated + // by the call. + addSizeInfo(CE, CE->getArg(2), C); + return; + } + + if (Name.equals("CFArrayGetCount")) { + addSizeInfo(CE->getArg(0), CE, C); + return; + } +} + +void ObjCContainersChecker::checkPreStmt(const CallExpr *CE, + CheckerContext &C) const { + StringRef Name = C.getCalleeName(CE); + if (Name.empty() || CE->getNumArgs() < 2) + return; + + // Check the array access. + if (Name.equals("CFArrayGetValueAtIndex")) { + ProgramStateRef State = C.getState(); + // Retrieve the size. + // Find out if we saw this array symbol before and have information about it. + const Expr *ArrayExpr = CE->getArg(0); + SymbolRef ArraySym = getArraySym(ArrayExpr, C); + if (!ArraySym) + return; + + const DefinedSVal *Size = State->get<ArraySizeMap>(ArraySym); + + if (!Size) + return; + + // Get the index. + const Expr *IdxExpr = CE->getArg(1); + SVal IdxVal = State->getSVal(IdxExpr, C.getLocationContext()); + if (IdxVal.isUnknownOrUndef()) + return; + DefinedSVal Idx = cast<DefinedSVal>(IdxVal); + + // Now, check if 'Idx in [0, Size-1]'. + const QualType T = IdxExpr->getType(); + ProgramStateRef StInBound = State->assumeInBound(Idx, *Size, true, T); + ProgramStateRef StOutBound = State->assumeInBound(Idx, *Size, false, T); + if (StOutBound && !StInBound) { + ExplodedNode *N = C.generateSink(StOutBound); + if (!N) + return; + initBugType(); + BugReport *R = new BugReport(*BT, "Index is out of bounds", N); + R->addRange(IdxExpr->getSourceRange()); + C.EmitReport(R); + return; + } + } +} + +/// Register checker. +void ento::registerObjCContainersChecker(CheckerManager &mgr) { + mgr.registerChecker<ObjCContainersChecker>(); +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp index 2fb9944..d15c8ba 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp @@ -34,18 +34,8 @@ // receives a reference to 'self', the checker keeps track and passes the flags // for 1) and 2) to the new object that 'self' points to after the call. // -// FIXME (rdar://7937506): In the case of: -// [super init]; -// return self; -// Have an extra PathDiagnosticPiece in the path that says "called [super init], -// but didn't assign the result to self." - //===----------------------------------------------------------------------===// -// FIXME: Somehow stick the link to Apple's documentation about initializing -// objects in the diagnostics. -// http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/ObjectiveC/Articles/ocAllocInit.html - #include "ClangSACheckers.h" #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" @@ -64,7 +54,7 @@ static bool isInitMessage(const ObjCMessage &msg); static bool isSelfVar(SVal location, CheckerContext &C); namespace { -class ObjCSelfInitChecker : public Checker< +class ObjCSelfInitChecker : public Checker< check::PreObjCMessage, check::PostObjCMessage, check::PostStmt<ObjCIvarRefExpr>, check::PreStmt<ReturnStmt>, @@ -72,6 +62,7 @@ class ObjCSelfInitChecker : public Checker< check::PostStmt<CallExpr>, check::Location > { public: + void checkPreObjCMessage(ObjCMessage msg, CheckerContext &C) const; void checkPostObjCMessage(ObjCMessage msg, CheckerContext &C) const; void checkPostStmt(const ObjCIvarRefExpr *E, CheckerContext &C) const; void checkPreStmt(const ReturnStmt *S, CheckerContext &C) const; @@ -79,6 +70,10 @@ public: void checkPostStmt(const CallExpr *CE, CheckerContext &C) const; void checkLocation(SVal location, bool isLoad, const Stmt *S, CheckerContext &C) const; + + void checkPreStmt(const CallOrObjCMessage &CE, CheckerContext &C) const; + void checkPostStmt(const CallOrObjCMessage &CE, CheckerContext &C) const; + }; } // end anonymous namespace @@ -87,8 +82,8 @@ namespace { class InitSelfBug : public BugType { const std::string desc; public: - InitSelfBug() : BugType("missing \"self = [(super or self) init...]\"", - "missing \"self = [(super or self) init...]\"") {} + InitSelfBug() : BugType("Missing \"self = [(super or self) init...]\"", + categories::CoreFoundationObjectiveC) {} }; } // end anonymous namespace @@ -130,7 +125,7 @@ namespace ento { } } -static SelfFlagEnum getSelfFlags(SVal val, const ProgramState *state) { +static SelfFlagEnum getSelfFlags(SVal val, ProgramStateRef state) { if (SymbolRef sym = val.getAsSymbol()) if (const unsigned *attachedFlags = state->get<SelfFlag>(sym)) return (SelfFlagEnum)*attachedFlags; @@ -141,7 +136,7 @@ static SelfFlagEnum getSelfFlags(SVal val, CheckerContext &C) { return getSelfFlags(val, C.getState()); } -static void addSelfFlag(const ProgramState *state, SVal val, +static void addSelfFlag(ProgramStateRef state, SVal val, SelfFlagEnum flag, CheckerContext &C) { // We tag the symbol that the SVal wraps. if (SymbolRef sym = val.getAsSymbol()) @@ -156,7 +151,7 @@ static bool hasSelfFlag(SVal val, SelfFlagEnum flag, CheckerContext &C) { /// points to and is an object that did not come from the result of calling /// an initializer. static bool isInvalidSelf(const Expr *E, CheckerContext &C) { - SVal exprVal = C.getState()->getSVal(E); + SVal exprVal = C.getState()->getSVal(E, C.getLocationContext()); if (!hasSelfFlag(exprVal, SelfFlag_Self, C)) return false; // value did not come from 'self'. if (hasSelfFlag(exprVal, SelfFlag_InitRes, C)) @@ -188,25 +183,28 @@ static void checkForInvalidSelf(const Expr *E, CheckerContext &C, void ObjCSelfInitChecker::checkPostObjCMessage(ObjCMessage msg, CheckerContext &C) const { + CallOrObjCMessage MsgWrapper(msg, C.getState(), C.getLocationContext()); + checkPostStmt(MsgWrapper, C); + // When encountering a message that does initialization (init rule), // tag the return value so that we know later on that if self has this value // then it is properly initialized. // FIXME: A callback should disable checkers at the start of functions. if (!shouldRunOnFunctionOrMethod(dyn_cast<NamedDecl>( - C.getCurrentAnalysisContext()->getDecl()))) + C.getCurrentAnalysisDeclContext()->getDecl()))) return; if (isInitMessage(msg)) { // Tag the return value as the result of an initializer. - const ProgramState *state = C.getState(); + ProgramStateRef 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 // value out when we return from this method. state = state->set<CalledInit>(true); - SVal V = state->getSVal(msg.getOriginExpr()); + SVal V = state->getSVal(msg.getMessageExpr(), C.getLocationContext()); addSelfFlag(state, V, SelfFlag_InitRes, C); return; } @@ -221,7 +219,7 @@ void ObjCSelfInitChecker::checkPostStmt(const ObjCIvarRefExpr *E, CheckerContext &C) const { // FIXME: A callback should disable checkers at the start of functions. if (!shouldRunOnFunctionOrMethod(dyn_cast<NamedDecl>( - C.getCurrentAnalysisContext()->getDecl()))) + C.getCurrentAnalysisDeclContext()->getDecl()))) return; checkForInvalidSelf(E->getBase(), C, @@ -233,7 +231,7 @@ void ObjCSelfInitChecker::checkPreStmt(const ReturnStmt *S, CheckerContext &C) const { // FIXME: A callback should disable checkers at the start of functions. if (!shouldRunOnFunctionOrMethod(dyn_cast<NamedDecl>( - C.getCurrentAnalysisContext()->getDecl()))) + C.getCurrentAnalysisDeclContext()->getDecl()))) return; checkForInvalidSelf(S->getRetValue(), C, @@ -259,10 +257,28 @@ void ObjCSelfInitChecker::checkPreStmt(const ReturnStmt *S, void ObjCSelfInitChecker::checkPreStmt(const CallExpr *CE, CheckerContext &C) const { - 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); + CallOrObjCMessage CEWrapper(CE, C.getState(), C.getLocationContext()); + checkPreStmt(CEWrapper, C); +} + +void ObjCSelfInitChecker::checkPostStmt(const CallExpr *CE, + CheckerContext &C) const { + CallOrObjCMessage CEWrapper(CE, C.getState(), C.getLocationContext()); + checkPostStmt(CEWrapper, C); +} + +void ObjCSelfInitChecker::checkPreObjCMessage(ObjCMessage Msg, + CheckerContext &C) const { + CallOrObjCMessage MsgWrapper(Msg, C.getState(), C.getLocationContext()); + checkPreStmt(MsgWrapper, C); +} + +void ObjCSelfInitChecker::checkPreStmt(const CallOrObjCMessage &CE, + CheckerContext &C) const { + ProgramStateRef state = C.getState(); + unsigned NumArgs = CE.getNumArgs(); + for (unsigned i = 0; i < NumArgs; ++i) { + SVal argV = CE.getArgSVal(i); if (isSelfVar(argV, C)) { unsigned selfFlags = getSelfFlags(state->getSVal(cast<Loc>(argV)), C); C.addTransition(state->set<PreCallSelfFlags>(selfFlags)); @@ -275,12 +291,12 @@ void ObjCSelfInitChecker::checkPreStmt(const CallExpr *CE, } } -void ObjCSelfInitChecker::checkPostStmt(const CallExpr *CE, +void ObjCSelfInitChecker::checkPostStmt(const CallOrObjCMessage &CE, CheckerContext &C) const { - 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); + ProgramStateRef state = C.getState(); + unsigned NumArgs = CE.getNumArgs(); + for (unsigned i = 0; i < NumArgs; ++i) { + SVal argV = CE.getArgSVal(i); if (isSelfVar(argV, C)) { SelfFlagEnum prevFlags = (SelfFlagEnum)state->get<PreCallSelfFlags>(); state = state->remove<PreCallSelfFlags>(); @@ -289,7 +305,7 @@ void ObjCSelfInitChecker::checkPostStmt(const CallExpr *CE, } else if (hasSelfFlag(argV, SelfFlag_Self, C)) { SelfFlagEnum prevFlags = (SelfFlagEnum)state->get<PreCallSelfFlags>(); state = state->remove<PreCallSelfFlags>(); - addSelfFlag(state, state->getSVal(CE), prevFlags, C); + addSelfFlag(state, state->getSVal(cast<Loc>(argV)), prevFlags, C); return; } } @@ -300,7 +316,7 @@ void ObjCSelfInitChecker::checkLocation(SVal location, bool isLoad, CheckerContext &C) const { // Tag the result of a load from 'self' so that we can easily know that the // value is the object that 'self' points to. - const ProgramState *state = C.getState(); + ProgramStateRef state = C.getState(); if (isSelfVar(location, C)) addSelfFlag(state, state->getSVal(cast<Loc>(location)), SelfFlag_Self, C); } @@ -335,7 +351,7 @@ static bool shouldRunOnFunctionOrMethod(const NamedDecl *ND) { /// \brief Returns true if the location is 'self'. static bool isSelfVar(SVal location, CheckerContext &C) { - AnalysisContext *analCtx = C.getCurrentAnalysisContext(); + AnalysisDeclContext *analCtx = C.getCurrentAnalysisDeclContext(); if (!analCtx->getSelfDecl()) return false; if (!isa<loc::MemRegionVal>(location)) diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCUnusedIVarsChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCUnusedIVarsChecker.cpp index bbc262f..4718dc7 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCUnusedIVarsChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCUnusedIVarsChecker.cpp @@ -161,7 +161,7 @@ static void checkObjCUnusedIvar(const ObjCImplementationDecl *D, PathDiagnosticLocation L = PathDiagnosticLocation::create(I->first, BR.getSourceManager()); - BR.EmitBasicReport("Unused instance variable", "Optimization", + BR.EmitBasicReport(D, "Unused instance variable", "Optimization", os.str(), L); } } diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/PointerArithChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/PointerArithChecker.cpp index 202522b..fe4845b 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/PointerArithChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/PointerArithChecker.cpp @@ -24,7 +24,7 @@ using namespace ento; namespace { class PointerArithChecker : public Checker< check::PreStmt<BinaryOperator> > { - mutable llvm::OwningPtr<BuiltinBug> BT; + mutable OwningPtr<BuiltinBug> BT; public: void checkPreStmt(const BinaryOperator *B, CheckerContext &C) const; @@ -36,9 +36,10 @@ void PointerArithChecker::checkPreStmt(const BinaryOperator *B, if (B->getOpcode() != BO_Sub && B->getOpcode() != BO_Add) return; - const ProgramState *state = C.getState(); - SVal LV = state->getSVal(B->getLHS()); - SVal RV = state->getSVal(B->getRHS()); + ProgramStateRef state = C.getState(); + const LocationContext *LCtx = C.getLocationContext(); + SVal LV = state->getSVal(B->getLHS(), LCtx); + SVal RV = state->getSVal(B->getRHS(), LCtx); const MemRegion *LR = LV.getAsRegion(); @@ -50,7 +51,7 @@ void PointerArithChecker::checkPreStmt(const BinaryOperator *B, if (isa<VarRegion>(LR) || isa<CodeTextRegion>(LR) || isa<CompoundLiteralRegion>(LR)) { - if (ExplodedNode *N = C.generateNode()) { + if (ExplodedNode *N = C.addTransition()) { if (!BT) BT.reset(new BuiltinBug("Dangerous pointer arithmetic", "Pointer arithmetic done on non-array variables " diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/PointerSubChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/PointerSubChecker.cpp index 924c7f2..fa5c6a3 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/PointerSubChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/PointerSubChecker.cpp @@ -25,7 +25,7 @@ using namespace ento; namespace { class PointerSubChecker : public Checker< check::PreStmt<BinaryOperator> > { - mutable llvm::OwningPtr<BuiltinBug> BT; + mutable OwningPtr<BuiltinBug> BT; public: void checkPreStmt(const BinaryOperator *B, CheckerContext &C) const; @@ -39,9 +39,10 @@ void PointerSubChecker::checkPreStmt(const BinaryOperator *B, if (B->getOpcode() != BO_Sub) return; - const ProgramState *state = C.getState(); - SVal LV = state->getSVal(B->getLHS()); - SVal RV = state->getSVal(B->getRHS()); + ProgramStateRef state = C.getState(); + const LocationContext *LCtx = C.getLocationContext(); + SVal LV = state->getSVal(B->getLHS(), LCtx); + SVal RV = state->getSVal(B->getRHS(), LCtx); const MemRegion *LR = LV.getAsRegion(); const MemRegion *RR = RV.getAsRegion(); @@ -59,7 +60,7 @@ void PointerSubChecker::checkPreStmt(const BinaryOperator *B, if (isa<SymbolicRegion>(BaseLR) || isa<SymbolicRegion>(BaseRR)) return; - if (ExplodedNode *N = C.generateNode()) { + if (ExplodedNode *N = C.addTransition()) { if (!BT) BT.reset(new BuiltinBug("Pointer subtraction", "Subtraction of two pointers that do not point to " diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp index c02b5b1..2d018ef 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp @@ -25,8 +25,8 @@ using namespace ento; namespace { class PthreadLockChecker : public Checker< check::PostStmt<CallExpr> > { - mutable llvm::OwningPtr<BugType> BT_doublelock; - mutable llvm::OwningPtr<BugType> BT_lor; + mutable OwningPtr<BugType> BT_doublelock; + mutable OwningPtr<BugType> BT_lor; enum LockingSemantics { NotApplicable = 0, PthreadSemantics, @@ -56,18 +56,11 @@ template <> struct ProgramStateTrait<LockSet> : void PthreadLockChecker::checkPostStmt(const CallExpr *CE, CheckerContext &C) const { - const ProgramState *state = C.getState(); - const Expr *Callee = CE->getCallee(); - const FunctionDecl *FD = state->getSVal(Callee).getAsFunctionDecl(); - - if (!FD) - return; - - // Get the name of the callee. - IdentifierInfo *II = FD->getIdentifier(); - if (!II) // if no identifier, not a simple C function + ProgramStateRef state = C.getState(); + const LocationContext *LCtx = C.getLocationContext(); + StringRef FName = C.getCalleeName(CE); + if (FName.empty()) return; - StringRef FName = II->getName(); if (CE->getNumArgs() != 1) return; @@ -75,24 +68,28 @@ void PthreadLockChecker::checkPostStmt(const CallExpr *CE, if (FName == "pthread_mutex_lock" || FName == "pthread_rwlock_rdlock" || FName == "pthread_rwlock_wrlock") - AcquireLock(C, CE, state->getSVal(CE->getArg(0)), false, PthreadSemantics); + AcquireLock(C, CE, state->getSVal(CE->getArg(0), LCtx), + 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); + AcquireLock(C, CE, state->getSVal(CE->getArg(0), LCtx), + 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); + AcquireLock(C, CE, state->getSVal(CE->getArg(0), LCtx), + 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); + AcquireLock(C, CE, state->getSVal(CE->getArg(0), LCtx), + 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))); + ReleaseLock(C, CE, state->getSVal(CE->getArg(0), LCtx)); } void PthreadLockChecker::AcquireLock(CheckerContext &C, const CallExpr *CE, @@ -103,9 +100,9 @@ void PthreadLockChecker::AcquireLock(CheckerContext &C, const CallExpr *CE, if (!lockR) return; - const ProgramState *state = C.getState(); + ProgramStateRef state = C.getState(); - SVal X = state->getSVal(CE); + SVal X = state->getSVal(CE, C.getLocationContext()); if (X.isUnknownOrUndef()) return; @@ -125,10 +122,10 @@ void PthreadLockChecker::AcquireLock(CheckerContext &C, const CallExpr *CE, return; } - const ProgramState *lockSucc = state; + ProgramStateRef lockSucc = state; if (isTryLock) { // Bifurcate the state, and allow a mode where the lock acquisition fails. - const ProgramState *lockFail; + ProgramStateRef lockFail; switch (semantics) { case PthreadSemantics: llvm::tie(lockFail, lockSucc) = state->assume(retVal); @@ -138,7 +135,6 @@ void PthreadLockChecker::AcquireLock(CheckerContext &C, const CallExpr *CE, break; default: llvm_unreachable("Unknown tryLock locking semantics"); - break; } assert(lockFail && lockSucc); C.addTransition(lockFail); @@ -166,7 +162,7 @@ void PthreadLockChecker::ReleaseLock(CheckerContext &C, const CallExpr *CE, if (!lockR) return; - const ProgramState *state = C.getState(); + ProgramStateRef state = C.getState(); llvm::ImmutableList<const MemRegion*> LS = state->get<LockSet>(); // FIXME: Better analysis requires IPA for wrappers. diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp index 93e0fe5..b569e41 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp @@ -18,12 +18,12 @@ #include "clang/Basic/LangOptions.h" #include "clang/Basic/SourceManager.h" #include "clang/Analysis/DomainSpecific/CocoaConventions.h" +#include "clang/AST/ParentMap.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/Core/PathSensitive/CheckerContext.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngineBuilders.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h" @@ -31,6 +31,7 @@ #include "llvm/ADT/FoldingSet.h" #include "llvm/ADT/ImmutableList.h" #include "llvm/ADT/ImmutableMap.h" +#include "llvm/ADT/SmallString.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringExtras.h" #include <cstdarg> @@ -45,21 +46,14 @@ namespace { class GenericNodeBuilderRefCount { CheckerContext *C; const ProgramPointTag *tag; - EndOfFunctionNodeBuilder *ENB; public: GenericNodeBuilderRefCount(CheckerContext &c, - const ProgramPointTag *t) - : C(&c), tag(t), ENB(0) {} + const ProgramPointTag *t = 0) + : C(&c), tag(t){} - GenericNodeBuilderRefCount(EndOfFunctionNodeBuilder &enb) - : C(0), tag(0), ENB(&enb) {} - - ExplodedNode *MakeNode(const ProgramState *state, ExplodedNode *Pred) { - if (C) - return C->generateNode(state, Pred, tag, false); - - assert(ENB); - return ENB->generateNode(state, Pred); + ExplodedNode *MakeNode(ProgramStateRef state, ExplodedNode *Pred, + bool MarkAsSink = false) { + return C->addTransition(state, Pred, tag, MarkAsSink); } }; } // end anonymous namespace @@ -138,6 +132,11 @@ public: static RetEffect MakeNoRet() { return RetEffect(NoRet); } + + void Profile(llvm::FoldingSetNodeID& ID) const { + ID.AddInteger((unsigned) K); + ID.AddInteger((unsigned) O); + } }; //===----------------------------------------------------------------------===// @@ -358,22 +357,22 @@ struct ProgramStateTrait<RefBindings> namespace { class RetainSummary { - /// Args - an ordered vector of (index, ArgEffect) pairs, where index + /// Args - a map of (index, ArgEffect) pairs, where index /// specifies the argument (starting from 0). This can be sparsely /// populated; arguments with no entry in Args use 'DefaultArgEffect'. ArgEffects Args; /// DefaultArgEffect - The default ArgEffect to apply to arguments that /// do not have an entry in Args. - ArgEffect DefaultArgEffect; + ArgEffect DefaultArgEffect; /// Receiver - If this summary applies to an Objective-C message expression, /// this is the effect applied to the state of the receiver. - ArgEffect Receiver; + ArgEffect Receiver; /// Ret - The effect on the return value. Used to indicate if the /// function/method call returns a new tracked symbol. - RetEffect Ret; + RetEffect Ret; public: RetainSummary(ArgEffects A, RetEffect R, ArgEffect defaultEff, @@ -419,6 +418,19 @@ public: return Args == Other.Args && DefaultArgEffect == Other.DefaultArgEffect && Receiver == Other.Receiver && Ret == Other.Ret; } + + /// Profile this summary for inclusion in a FoldingSet. + void Profile(llvm::FoldingSetNodeID& ID) const { + ID.Add(Args); + ID.Add(DefaultArgEffect); + ID.Add(Receiver); + ID.Add(Ret); + } + + /// A retain summary is simple if it has no ArgEffects other than the default. + bool isSimple() const { + return Args.isEmpty(); + } }; } // end anonymous namespace @@ -443,7 +455,7 @@ public: ObjCSummaryKey(Selector s) : II(0), S(s) {} - IdentifierInfo* getIdentifier() const { return II; } + IdentifierInfo *getIdentifier() const { return II; } Selector getSelector() const { return S; } }; } @@ -523,7 +535,7 @@ public: return Summ; } - const 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)); @@ -560,6 +572,8 @@ class RetainSummaryManager { typedef ObjCSummaryCache ObjCMethodSummariesTy; + typedef llvm::FoldingSetNodeWrapper<RetainSummary> CachedSummaryNode; + //==-----------------------------------------------------------------==// // Data. //==-----------------------------------------------------------------==// @@ -591,7 +605,7 @@ class RetainSummaryManager { ArgEffects::Factory AF; /// ScratchArgs - A holding buffer for construct ArgEffects. - ArgEffects ScratchArgs; + ArgEffects ScratchArgs; /// ObjCAllocRetE - Default return effect for methods returning Objective-C /// objects. @@ -601,8 +615,9 @@ class RetainSummaryManager { /// Objective-C objects. RetEffect ObjCInitRetE; - RetainSummary DefaultSummary; - const RetainSummary *StopSummary; + /// SimpleSummaries - Used for uniquing summaries that don't have special + /// effects. + llvm::FoldingSet<CachedSummaryNode> SimpleSummaries; //==-----------------------------------------------------------------==// // Methods. @@ -616,39 +631,32 @@ class RetainSummaryManager { public: RetEffect getObjAllocRetEffect() const { return ObjCAllocRetE; } - - const RetainSummary *getDefaultSummary() { - return &DefaultSummary; - } - const RetainSummary * getUnarySummary(const FunctionType* FT, + const RetainSummary *getUnarySummary(const FunctionType* FT, UnaryFuncKind func); - const RetainSummary * getCFSummaryCreateRule(const FunctionDecl *FD); - const RetainSummary * getCFSummaryGetRule(const FunctionDecl *FD); - const RetainSummary * getCFCreateGetRuleSummary(const FunctionDecl *FD); + const RetainSummary *getCFSummaryCreateRule(const FunctionDecl *FD); + const RetainSummary *getCFSummaryGetRule(const FunctionDecl *FD); + const RetainSummary *getCFCreateGetRuleSummary(const FunctionDecl *FD); - const RetainSummary * getPersistentSummary(ArgEffects AE, RetEffect RetEff, - ArgEffect ReceiverEff = DoNothing, - ArgEffect DefaultEff = MayEscape); + const RetainSummary *getPersistentSummary(const RetainSummary &OldSumm); - const RetainSummary * getPersistentSummary(RetEffect RE, + const RetainSummary *getPersistentSummary(RetEffect RetEff, ArgEffect ReceiverEff = DoNothing, ArgEffect DefaultEff = MayEscape) { - return getPersistentSummary(getArgEffects(), RE, ReceiverEff, DefaultEff); + RetainSummary Summ(getArgEffects(), RetEff, DefaultEff, ReceiverEff); + return getPersistentSummary(Summ); } - const RetainSummary *getPersistentStopSummary() { - if (StopSummary) - return StopSummary; - - StopSummary = getPersistentSummary(RetEffect::MakeNoRet(), - StopTracking, StopTracking); - - return StopSummary; + const RetainSummary *getDefaultSummary() { + return getPersistentSummary(RetEffect::MakeNoRet(), + DoNothing, MayEscape); } - const RetainSummary *getInitMethodSummary(QualType RetTy); + const RetainSummary *getPersistentStopSummary() { + return getPersistentSummary(RetEffect::MakeNoRet(), + StopTracking, StopTracking); + } void InitializeClassMethodSummaries(); void InitializeMethodSummaries(); @@ -661,10 +669,11 @@ private: ObjCMethodSummaries[S] = Summ; } - void addClassMethSummary(const char* Cls, const char* nullaryName, - const RetainSummary *Summ) { + void addClassMethSummary(const char* Cls, const char* name, + const RetainSummary *Summ, bool isNullary = true) { IdentifierInfo* ClsII = &Ctx.Idents.get(Cls); - Selector S = GetNullarySelector(nullaryName, Ctx); + Selector S = isNullary ? GetNullarySelector(name, Ctx) + : GetUnarySelector(name, Ctx); ObjCClassMethodSummaries[ObjCSummaryKey(ClsII, S)] = Summ; } @@ -725,50 +734,37 @@ public: ObjCInitRetE(gcenabled ? RetEffect::MakeGCNotOwned() : (usesARC ? RetEffect::MakeARCNotOwned() - : RetEffect::MakeOwnedWhenTrackedReceiver())), - DefaultSummary(AF.getEmptyMap() /* per-argument effects (none) */, - RetEffect::MakeNoRet() /* return effect */, - MayEscape, /* default argument effect */ - DoNothing /* receiver effect */), - StopSummary(0) { - + : RetEffect::MakeOwnedWhenTrackedReceiver())) { InitializeClassMethodSummaries(); InitializeMethodSummaries(); } - const RetainSummary * getSummary(const FunctionDecl *FD); + const RetainSummary *getSummary(const FunctionDecl *FD); + + const RetainSummary *getMethodSummary(Selector S, IdentifierInfo *ClsName, + const ObjCInterfaceDecl *ID, + const ObjCMethodDecl *MD, + QualType RetTy, + ObjCMethodSummariesTy &CachedSummaries); const RetainSummary *getInstanceMethodSummary(const ObjCMessage &msg, - const ProgramState *state, + ProgramStateRef state, const LocationContext *LC); - const RetainSummary * getInstanceMethodSummary(const ObjCMessage &msg, + const RetainSummary *getInstanceMethodSummary(const ObjCMessage &msg, const ObjCInterfaceDecl *ID) { - return getInstanceMethodSummary(msg.getSelector(), 0, - ID, msg.getMethodDecl(), msg.getType(Ctx)); + return getMethodSummary(msg.getSelector(), 0, ID, msg.getMethodDecl(), + msg.getType(Ctx), ObjCMethodSummaries); } - const RetainSummary * getInstanceMethodSummary(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); - const RetainSummary *getClassMethodSummary(const ObjCMessage &msg) { const ObjCInterfaceDecl *Class = 0; if (!msg.isInstanceMessage()) Class = msg.getReceiverInterface(); - return getClassMethodSummary(msg.getSelector(), - Class? Class->getIdentifier() : 0, - Class, - msg.getMethodDecl(), msg.getType(Ctx)); + return getMethodSummary(msg.getSelector(), Class->getIdentifier(), + Class, msg.getMethodDecl(), msg.getType(Ctx), + ObjCClassMethodSummaries); } /// getMethodSummary - This version of getMethodSummary is used to query @@ -780,13 +776,16 @@ public: IdentifierInfo *ClsName = ID->getIdentifier(); QualType ResultTy = MD->getResultType(); + ObjCMethodSummariesTy *CachedSummaries; if (MD->isInstanceMethod()) - return getInstanceMethodSummary(S, ClsName, ID, MD, ResultTy); + CachedSummaries = &ObjCMethodSummaries; else - return getClassMethodSummary(S, ClsName, ID, MD, ResultTy); + CachedSummaries = &ObjCClassMethodSummaries; + + return getMethodSummary(S, ClsName, ID, MD, ResultTy, *CachedSummaries); } - const RetainSummary * getCommonMethodSummary(const ObjCMethodDecl *MD, + const RetainSummary *getStandardMethodSummary(const ObjCMethodDecl *MD, Selector S, QualType RetTy); void updateSummaryFromAnnotations(const RetainSummary *&Summ, @@ -800,12 +799,6 @@ public: bool isARCEnabled() const { return ARCEnabled; } bool isARCorGCEnabled() const { return GCEnabled || ARCEnabled; } - - 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 @@ -815,23 +808,17 @@ public: 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), + RetainSummaryTemplate(const RetainSummary *&real, const RetainSummary &base, + RetainSummaryManager &mgr) + : Manager(mgr), RealSummary(real), ScratchSummary(real ? *real : base), Accessed(false) {} ~RetainSummaryTemplate() { if (Accessed) - RealSummary = Manager.copySummary(&ScratchSummary); - else if (!RealSummary) - RealSummary = BaseSummary; + RealSummary = Manager.getPersistentSummary(ScratchSummary); } RetainSummary &operator*() { @@ -858,12 +845,26 @@ ArgEffects RetainSummaryManager::getArgEffects() { } const RetainSummary * -RetainSummaryManager::getPersistentSummary(ArgEffects AE, RetEffect RetEff, - ArgEffect ReceiverEff, - ArgEffect DefaultEff) { - // Create the summary and return it. +RetainSummaryManager::getPersistentSummary(const RetainSummary &OldSumm) { + // Unique "simple" summaries -- those without ArgEffects. + if (OldSumm.isSimple()) { + llvm::FoldingSetNodeID ID; + OldSumm.Profile(ID); + + void *Pos; + CachedSummaryNode *N = SimpleSummaries.FindNodeOrInsertPos(ID, Pos); + + if (!N) { + N = (CachedSummaryNode *) BPAlloc.Allocate<CachedSummaryNode>(); + new (N) CachedSummaryNode(OldSumm); + SimpleSummaries.InsertNode(N, Pos); + } + + return &N->getValue(); + } + RetainSummary *Summ = (RetainSummary *) BPAlloc.Allocate<RetainSummary>(); - new (Summ) RetainSummary(AE, RetEff, DefaultEff, ReceiverEff); + new (Summ) RetainSummary(OldSumm); return Summ; } @@ -984,6 +985,21 @@ const RetainSummary * RetainSummaryManager::getSummary(const FunctionDecl *FD) { // correctly. ScratchArgs = AF.add(ScratchArgs, 12, StopTracking); S = getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, DoNothing); + } else if (FName == "dispatch_set_context") { + // <rdar://problem/11059275> - The analyzer currently doesn't have + // a good way to reason about the finalizer function for libdispatch. + // If we pass a context object that is memory managed, stop tracking it. + // FIXME: this hack should possibly go away once we can handle + // libdispatch finalizers. + ScratchArgs = AF.add(ScratchArgs, 1, StopTracking); + S = getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, DoNothing); + } else if (FName.startswith("NS") && + (FName.find("Insert") != StringRef::npos)) { + // Whitelist NSXXInsertXX, for example NSMapInsertIfAbsent, since they can + // be deallocated by NSMapRemove. (radar://11152419) + ScratchArgs = AF.add(ScratchArgs, 1, StopTracking); + ScratchArgs = AF.add(ScratchArgs, 2, StopTracking); + S = getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, DoNothing); } // Did we get a summary? @@ -1106,7 +1122,6 @@ RetainSummaryManager::getUnarySummary(const FunctionType* FT, 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); @@ -1131,25 +1146,13 @@ RetainSummaryManager::getCFSummaryGetRule(const FunctionDecl *FD) { // Summary creation for Selectors. //===----------------------------------------------------------------------===// -const RetainSummary * -RetainSummaryManager::getInitMethodSummary(QualType RetTy) { - assert(ScratchArgs.isEmpty()); - // 'init' methods conceptually return a newly allocated object and claim - // the receiver. - if (cocoa::isCocoaObjectRef(RetTy) || - coreFoundation::isCFObjectRef(RetTy)) - return getPersistentSummary(ObjCInitRetE, DecRefMsg); - - return getDefaultSummary(); -} - void RetainSummaryManager::updateSummaryFromAnnotations(const RetainSummary *&Summ, const FunctionDecl *FD) { if (!FD) return; - RetainSummaryTemplate Template(Summ, DefaultSummary, *this); + RetainSummaryTemplate Template(Summ, *getDefaultSummary(), *this); // Effects on the parameters. unsigned parm_idx = 0; @@ -1197,8 +1200,7 @@ RetainSummaryManager::updateSummaryFromAnnotations(const RetainSummary *&Summ, if (!MD) return; - RetainSummaryTemplate Template(Summ, DefaultSummary, *this); - + RetainSummaryTemplate Template(Summ, *getDefaultSummary(), *this); bool isTrackedLoc = false; // Effects on the receiver. @@ -1247,8 +1249,8 @@ RetainSummaryManager::updateSummaryFromAnnotations(const RetainSummary *&Summ, } const RetainSummary * -RetainSummaryManager::getCommonMethodSummary(const ObjCMethodDecl *MD, - Selector S, QualType RetTy) { +RetainSummaryManager::getStandardMethodSummary(const ObjCMethodDecl *MD, + Selector S, QualType RetTy) { if (MD) { // Scan the method decl for 'void*' arguments. These should be treated @@ -1265,8 +1267,74 @@ RetainSummaryManager::getCommonMethodSummary(const ObjCMethodDecl *MD, } } - // Any special effect for the receiver? + // Any special effects? ArgEffect ReceiverEff = DoNothing; + RetEffect ResultEff = RetEffect::MakeNoRet(); + + // Check the method family, and apply any default annotations. + switch (MD ? MD->getMethodFamily() : S.getMethodFamily()) { + case OMF_None: + case OMF_performSelector: + // Assume all Objective-C methods follow Cocoa Memory Management rules. + // FIXME: Does the non-threaded performSelector family really belong here? + // The selector could be, say, @selector(copy). + if (cocoa::isCocoaObjectRef(RetTy)) + ResultEff = RetEffect::MakeNotOwned(RetEffect::ObjC); + else if (coreFoundation::isCFObjectRef(RetTy)) { + // ObjCMethodDecl currently doesn't consider CF objects as valid return + // values for alloc, new, copy, or mutableCopy, so we have to + // double-check with the selector. This is ugly, but there aren't that + // many Objective-C methods that return CF objects, right? + if (MD) { + switch (S.getMethodFamily()) { + case OMF_alloc: + case OMF_new: + case OMF_copy: + case OMF_mutableCopy: + ResultEff = RetEffect::MakeOwned(RetEffect::CF, true); + break; + default: + ResultEff = RetEffect::MakeNotOwned(RetEffect::CF); + break; + } + } else { + ResultEff = RetEffect::MakeNotOwned(RetEffect::CF); + } + } + break; + case OMF_init: + ResultEff = ObjCInitRetE; + ReceiverEff = DecRefMsg; + break; + case OMF_alloc: + case OMF_new: + case OMF_copy: + case OMF_mutableCopy: + if (cocoa::isCocoaObjectRef(RetTy)) + ResultEff = ObjCAllocRetE; + else if (coreFoundation::isCFObjectRef(RetTy)) + ResultEff = RetEffect::MakeOwned(RetEffect::CF, true); + break; + case OMF_autorelease: + ReceiverEff = Autorelease; + break; + case OMF_retain: + ReceiverEff = IncRefMsg; + break; + case OMF_release: + ReceiverEff = DecRefMsg; + break; + case OMF_dealloc: + ReceiverEff = Dealloc; + break; + case OMF_self: + // -self is handled specially by the ExprEngine to propagate the receiver. + break; + case OMF_retainCount: + case OMF_finalize: + // These methods don't return objects. + break; + } // If one of the arguments in the selector has the keyword 'delegate' we // should stop tracking the reference count for the receiver. This is @@ -1279,34 +1347,16 @@ RetainSummaryManager::getCommonMethodSummary(const ObjCMethodDecl *MD, ReceiverEff = StopTracking; } - // Look for methods that return an owned object. - if (cocoa::isCocoaObjectRef(RetTy)) { - // EXPERIMENTAL: assume the Cocoa conventions for all objects returned - // by instance methods. - RetEffect E = cocoa::followsFundamentalRule(S, MD) - ? ObjCAllocRetE : RetEffect::MakeNotOwned(RetEffect::ObjC); - - return getPersistentSummary(E, ReceiverEff, MayEscape); - } - - // Look for methods that return an owned core foundation object. - if (coreFoundation::isCFObjectRef(RetTy)) { - RetEffect E = cocoa::followsFundamentalRule(S, MD) - ? RetEffect::MakeOwned(RetEffect::CF, true) - : RetEffect::MakeNotOwned(RetEffect::CF); - - return getPersistentSummary(E, ReceiverEff, MayEscape); - } - - if (ScratchArgs.isEmpty() && ReceiverEff == DoNothing) + if (ScratchArgs.isEmpty() && ReceiverEff == DoNothing && + ResultEff.getKind() == RetEffect::NoRet) return getDefaultSummary(); - return getPersistentSummary(RetEffect::MakeNoRet(), ReceiverEff, MayEscape); + return getPersistentSummary(ResultEff, ReceiverEff, MayEscape); } const RetainSummary * RetainSummaryManager::getInstanceMethodSummary(const ObjCMessage &msg, - const ProgramState *state, + ProgramStateRef state, const LocationContext *LC) { // We need the type-information of the tracked receiver object @@ -1319,7 +1369,7 @@ RetainSummaryManager::getInstanceMethodSummary(const ObjCMessage &msg, SVal receiverV; if (Receiver) { - receiverV = state->getSValAsScalarOrLoc(Receiver); + receiverV = state->getSValAsScalarOrLoc(Receiver, LC); // FIXME: Eventually replace the use of state->get<RefBindings> with // a generic API for reasoning about the Objective-C types of symbolic @@ -1348,51 +1398,22 @@ RetainSummaryManager::getInstanceMethodSummary(const ObjCMessage &msg, } const RetainSummary * -RetainSummaryManager::getInstanceMethodSummary(Selector S, - IdentifierInfo *ClsName, - const ObjCInterfaceDecl *ID, - const ObjCMethodDecl *MD, - QualType RetTy) { +RetainSummaryManager::getMethodSummary(Selector S, IdentifierInfo *ClsName, + const ObjCInterfaceDecl *ID, + const ObjCMethodDecl *MD, QualType RetTy, + ObjCMethodSummariesTy &CachedSummaries) { // Look up a summary in our summary cache. - const RetainSummary *Summ = ObjCMethodSummaries.find(ID, ClsName, S); + const RetainSummary *Summ = CachedSummaries.find(ID, ClsName, S); if (!Summ) { - assert(ScratchArgs.isEmpty()); - - // "initXXX": pass-through for receiver. - if (cocoa::deriveNamingConvention(S, MD) == cocoa::InitRule) - Summ = getInitMethodSummary(RetTy); - else - Summ = getCommonMethodSummary(MD, S, RetTy); + Summ = getStandardMethodSummary(MD, S, RetTy); // Annotations override defaults. updateSummaryFromAnnotations(Summ, MD); // Memoize the summary. - ObjCMethodSummaries[ObjCSummaryKey(ID, ClsName, S)] = Summ; - } - - return Summ; -} - -const RetainSummary * -RetainSummaryManager::getClassMethodSummary(Selector S, IdentifierInfo *ClsName, - const ObjCInterfaceDecl *ID, - const ObjCMethodDecl *MD, - QualType RetTy) { - - assert(ClsName && "Class name must be specified."); - const RetainSummary *Summ = ObjCClassMethodSummaries.find(ID, ClsName, S); - - if (!Summ) { - Summ = getCommonMethodSummary(MD, S, RetTy); - - // Annotations override defaults. - updateSummaryFromAnnotations(Summ, MD); - - // Memoize the summary. - ObjCClassMethodSummaries[ObjCSummaryKey(ID, ClsName, S)] = Summ; + CachedSummaries[ObjCSummaryKey(ID, ClsName, S)] = Summ; } return Summ; @@ -1515,6 +1536,7 @@ void RetainSummaryManager::InitializeMethodSummaries() { // Don't track allocated autorelease pools yet, as it is okay to prematurely // exit a method. addClassMethSummary("NSAutoreleasePool", "alloc", NoTrackYet); + addClassMethSummary("NSAutoreleasePool", "allocWithZone", NoTrackYet, false); // Create summaries QCRenderer/QCView -createSnapShotImageOfType: addInstMethSummary("QCRenderer", AllocSumm, @@ -1561,13 +1583,13 @@ template<> struct ProgramStateTrait<AutoreleasePoolContents> } // end GR namespace } // end clang namespace -static SymbolRef GetCurrentAutoreleasePool(const ProgramState *state) { +static SymbolRef GetCurrentAutoreleasePool(ProgramStateRef state) { ARStack stack = state->get<AutoreleaseStack>(); return stack.isEmpty() ? SymbolRef() : stack.getHead(); } -static const ProgramState * -SendAutorelease(const ProgramState *state, +static ProgramStateRef +SendAutorelease(ProgramStateRef state, ARCounts::Factory &F, SymbolRef sym) { SymbolRef pool = GetCurrentAutoreleasePool(state); @@ -1598,7 +1620,7 @@ namespace { class CFRefBug : public BugType { protected: CFRefBug(StringRef name) - : BugType(name, "Memory (Core Foundation/Objective-C)") {} + : BugType(name, categories::MemoryCoreFoundationObjectiveC) {} public: // FIXME: Eventually remove. @@ -1698,7 +1720,7 @@ namespace { // Bug Reports. // //===---------===// - class CFRefReportVisitor : public BugReporterVisitor { + class CFRefReportVisitor : public BugReporterVisitorImpl<CFRefReportVisitor> { protected: SymbolRef Sym; const SummaryLogTy &SummaryLog; @@ -1733,6 +1755,15 @@ namespace { PathDiagnosticPiece *getEndPath(BugReporterContext &BRC, const ExplodedNode *N, BugReport &BR); + + virtual BugReporterVisitor *clone() const { + // The curiously-recurring template pattern only works for one level of + // subclassing. Rather than make a new template base for + // CFRefReportVisitor, we simply override clone() to do the right thing. + // This could be trouble someday if BugReporterVisitorImpl is ever + // used for something else besides a convenient implementation of clone(). + return new CFRefLeakReportVisitor(*this); + } }; class CFRefReport : public BugReport { @@ -1771,7 +1802,7 @@ namespace { public: CFRefLeakReport(CFRefBug &D, const LangOptions &LOpts, bool GCEnabled, const SummaryLogTy &Log, ExplodedNode *n, SymbolRef sym, - ExprEngine &Eng); + CheckerContext &Ctx); PathDiagnosticLocation getLocation(const SourceManager &SM) const { assert(Location.isValid()); @@ -1823,6 +1854,20 @@ static inline bool contains(const SmallVectorImpl<ArgEffect>& V, return false; } +static bool isPropertyAccess(const Stmt *S, ParentMap &PM) { + unsigned maxDepth = 4; + while (S && maxDepth) { + if (const PseudoObjectExpr *PO = dyn_cast<PseudoObjectExpr>(S)) { + if (!isa<ObjCMessageExpr>(PO->getSyntacticForm())) + return true; + return false; + } + S = PM.getParent(S); + --maxDepth; + } + return false; +} + PathDiagnosticPiece *CFRefReportVisitor::VisitNode(const ExplodedNode *N, const ExplodedNode *PrevN, BugReporterContext &BRC, @@ -1832,8 +1877,9 @@ PathDiagnosticPiece *CFRefReportVisitor::VisitNode(const ExplodedNode *N, return NULL; // Check if the type state has changed. - const ProgramState *PrevSt = PrevN->getState(); - const ProgramState *CurrSt = N->getState(); + ProgramStateRef PrevSt = PrevN->getState(); + ProgramStateRef CurrSt = N->getState(); + const LocationContext *LCtx = N->getLocationContext(); const RefVal* CurrT = CurrSt->get<RefBindings>(Sym); if (!CurrT) return NULL; @@ -1851,40 +1897,49 @@ PathDiagnosticPiece *CFRefReportVisitor::VisitNode(const ExplodedNode *N, if (!PrevT) { 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 << '\''; - else - os << "function call"; + if (isa<ObjCArrayLiteral>(S)) { + os << "NSArray literal is an object with a +0 retain count"; } - else if (isa<ObjCMessageExpr>(S)) { - os << "Method"; - } else { - os << "Property"; + else if (isa<ObjCDictionaryLiteral>(S)) { + os << "NSDictionary literal is an object with a +0 retain count"; } + else { + if (const CallExpr *CE = dyn_cast<CallExpr>(S)) { + // Get the name of the callee (if it is available). + SVal X = CurrSt->getSValAsScalarOrLoc(CE->getCallee(), LCtx); + if (const FunctionDecl *FD = X.getAsFunctionDecl()) + os << "Call to function '" << *FD << '\''; + else + os << "function call"; + } + else { + assert(isa<ObjCMessageExpr>(S)); + // The message expression may have between written directly or as + // a property access. Lazily determine which case we are looking at. + os << (isPropertyAccess(S, N->getParentMap()) ? "Property" : "Method"); + } - if (CurrV.getObjKind() == RetEffect::CF) { - os << " returns a Core Foundation object with a "; - } - else { - assert (CurrV.getObjKind() == RetEffect::ObjC); - os << " returns an Objective-C object with a "; - } + if (CurrV.getObjKind() == RetEffect::CF) { + os << " returns a Core Foundation object with a "; + } + else { + assert (CurrV.getObjKind() == RetEffect::ObjC); + os << " returns an Objective-C object with a "; + } - if (CurrV.isOwned()) { - os << "+1 retain count"; + if (CurrV.isOwned()) { + os << "+1 retain count"; - if (GCEnabled) { - assert(CurrV.getObjKind() == RetEffect::CF); - os << ". " - "Core Foundation objects are not automatically garbage collected."; + if (GCEnabled) { + assert(CurrV.getObjKind() == RetEffect::CF); + os << ". " + "Core Foundation objects are not automatically garbage collected."; + } + } + else { + assert (CurrV.isNotOwned()); + os << "+0 retain count"; } - } - else { - assert (CurrV.isNotOwned()); - os << "+0 retain count"; } PathDiagnosticLocation Pos(S, BRC.getSourceManager(), @@ -1912,7 +1967,7 @@ PathDiagnosticPiece *CFRefReportVisitor::VisitNode(const ExplodedNode *N, // Retrieve the value of the argument. Is it the symbol // we are interested in? - if (CurrSt->getSValAsScalarOrLoc(*AI).getAsLocSymbol() != Sym) + if (CurrSt->getSValAsScalarOrLoc(*AI, LCtx).getAsLocSymbol() != Sym) continue; // We have an argument. Get the effect! @@ -1921,7 +1976,8 @@ PathDiagnosticPiece *CFRefReportVisitor::VisitNode(const ExplodedNode *N, } else if (const ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(S)) { if (const Expr *receiver = ME->getInstanceReceiver()) - if (CurrSt->getSValAsScalarOrLoc(receiver).getAsLocSymbol() == Sym) { + if (CurrSt->getSValAsScalarOrLoc(receiver, LCtx) + .getAsLocSymbol() == Sym) { // The symbol we are tracking is the receiver. AEffects.push_back(Summ->getReceiverEffect()); } @@ -1949,7 +2005,8 @@ PathDiagnosticPiece *CFRefReportVisitor::VisitNode(const ExplodedNode *N, if (contains(AEffects, MakeCollectable)) { // Get the name of the function. const Stmt *S = cast<StmtPoint>(N->getLocation()).getStmt(); - SVal X = CurrSt->getSValAsScalarOrLoc(cast<CallExpr>(S)->getCallee()); + SVal X = + CurrSt->getSValAsScalarOrLoc(cast<CallExpr>(S)->getCallee(), LCtx); const FunctionDecl *FD = X.getAsFunctionDecl(); if (GCEnabled) { @@ -2004,8 +2061,8 @@ PathDiagnosticPiece *CFRefReportVisitor::VisitNode(const ExplodedNode *N, if (PrevV.getKind() == RefVal::Released) { assert(GCEnabled && CurrV.getCount() > 0); - os << " The object is not eligible for garbage collection until the " - "retain count reaches 0 again."; + os << " The object is not eligible for garbage collection until " + "the retain count reaches 0 again."; } break; @@ -2015,8 +2072,12 @@ PathDiagnosticPiece *CFRefReportVisitor::VisitNode(const ExplodedNode *N, break; case RefVal::ReturnedOwned: - os << "Object returned to caller as an owning reference (single retain " - "count transferred to caller)"; + // Autoreleases can be applied after marking a node ReturnedOwned. + if (CurrV.getAutoreleaseCount()) + return NULL; + + os << "Object returned to caller as an owning reference (single " + "retain count transferred to caller)"; break; case RefVal::ReturnedNotOwned: @@ -2061,7 +2122,7 @@ PathDiagnosticPiece *CFRefReportVisitor::VisitNode(const ExplodedNode *N, 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 (CurrSt->getSValAsScalarOrLoc(Exp).getAsLocSymbol() == Sym) { + if (CurrSt->getSValAsScalarOrLoc(Exp, LCtx).getAsLocSymbol() == Sym) { P->addRange(Exp->getSourceRange()); break; } @@ -2069,62 +2130,42 @@ PathDiagnosticPiece *CFRefReportVisitor::VisitNode(const ExplodedNode *N, return P; } -namespace { - class FindUniqueBinding : - public StoreManager::BindingsHandler { - SymbolRef Sym; - const MemRegion* Binding; - bool First; - - public: - FindUniqueBinding(SymbolRef sym) : Sym(sym), Binding(0), First(true) {} - - bool HandleBinding(StoreManager& SMgr, Store store, const MemRegion* R, - SVal val) { - - SymbolRef SymV = val.getAsSymbol(); - if (!SymV || SymV != Sym) - return true; - - if (Binding) { - First = false; - return false; - } - else - Binding = R; - - return true; - } - - operator bool() { return First && Binding; } - const MemRegion* getRegion() { return Binding; } - }; -} - +// Find the first node in the current function context that referred to the +// tracked symbol and the memory location that value was stored to. Note, the +// value is only reported if the allocation occurred in the same function as +// the leak. static std::pair<const ExplodedNode*,const MemRegion*> 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 MemRegion* FirstBinding = 0; + const LocationContext *LeakContext = N->getLocationContext(); while (N) { - const ProgramState *St = N->getState(); + ProgramStateRef St = N->getState(); RefBindings B = St->get<RefBindings>(); if (!B.lookup(Sym)) break; - FindUniqueBinding FB(Sym); + StoreManager::FindUniqueBinding FB(Sym); StateMgr.iterBindings(St, FB); if (FB) FirstBinding = FB.getRegion(); - Last = N; + // Allocation node, is the last node in the current context in which the + // symbol was tracked. + if (N->getLocationContext() == LeakContext) + Last = N; + N = N->pred_empty() ? NULL : *(N->pred_begin()); } + // If allocation happened in a function different from the leak node context, + // do not report the binding. + if (N->getLocationContext() != LeakContext) { + FirstBinding = 0; + } + return std::make_pair(Last, FirstBinding); } @@ -2132,9 +2173,7 @@ PathDiagnosticPiece* 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); + BR.markInteresting(Sym); return BugReporterVisitor::getDefaultEndPath(BRC, EndN, BR); } @@ -2145,7 +2184,7 @@ CFRefLeakReportVisitor::getEndPath(BugReporterContext &BRC, // Tell the BugReporterContext to report cases when the tracked symbol is // assigned to different variables, etc. - BRC.addNotableSymbol(Sym); + BR.markInteresting(Sym); // 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 @@ -2193,10 +2232,10 @@ CFRefLeakReportVisitor::getEndPath(BugReporterContext &BRC, } else { const FunctionDecl *FD = cast<FunctionDecl>(D); - os << " is return from a function whose name ('" - << FD->getNameAsString() + os << " is returned from a function whose name ('" + << *FD << "') does not contain 'Copy' or 'Create'. This violates the naming" - " convention rules given the Memory Management Guide for Core" + " convention rules given in the Memory Management Guide for Core" " Foundation"; } } @@ -2218,7 +2257,7 @@ CFRefLeakReportVisitor::getEndPath(BugReporterContext &BRC, CFRefLeakReport::CFRefLeakReport(CFRefBug &D, const LangOptions &LOpts, bool GCEnabled, const SummaryLogTy &Log, ExplodedNode *n, SymbolRef sym, - ExprEngine &Eng) + CheckerContext &Ctx) : CFRefReport(D, LOpts, GCEnabled, Log, n, sym, false) { // Most bug reports are cached at the location where they occurred. @@ -2231,10 +2270,10 @@ CFRefLeakReport::CFRefLeakReport(CFRefBug &D, const LangOptions &LOpts, // same SourceLocation. const ExplodedNode *AllocNode = 0; - const SourceManager& SMgr = Eng.getContext().getSourceManager(); + const SourceManager& SMgr = Ctx.getSourceManager(); llvm::tie(AllocNode, AllocBinding) = // Set AllocBinding. - GetAllocationSite(Eng.getStateManager(), getErrorNode(), sym); + GetAllocationSite(Ctx.getStateManager(), getErrorNode(), sym); // Get the SourceLocation for the allocation site. ProgramPoint P = AllocNode->getLocation(); @@ -2244,15 +2283,14 @@ CFRefLeakReport::CFRefLeakReport(CFRefBug &D, const LangOptions &LOpts, // Fill in the description of the bug. Description.clear(); llvm::raw_string_ostream os(Description); - unsigned AllocLine = SMgr.getExpansionLineNumber(AllocStmt->getLocStart()); os << "Potential leak "; if (GCEnabled) os << "(when using garbage collection) "; - os << "of an object allocated on line " << AllocLine; + os << "of an object"; // FIXME: AllocBinding doesn't get populated for RegionStore yet. if (AllocBinding) - os << " and stored into '" << AllocBinding->getString() << '\''; + os << " stored into '" << AllocBinding->getString() << '\''; addVisitor(new CFRefLeakReportVisitor(sym, GCEnabled, Log)); } @@ -2271,24 +2309,26 @@ class RetainCountChecker check::PostStmt<CastExpr>, check::PostStmt<CallExpr>, check::PostStmt<CXXConstructExpr>, + check::PostStmt<ObjCArrayLiteral>, + check::PostStmt<ObjCDictionaryLiteral>, 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; + mutable OwningPtr<CFRefBug> useAfterRelease, releaseNotOwned; + mutable OwningPtr<CFRefBug> deallocGC, deallocNotOwned; + mutable OwningPtr<CFRefBug> overAutorelease, returnNotOwnedForOwned; + mutable OwningPtr<CFRefBug> leakWithinFunction, leakAtReturn; + mutable 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 OwningPtr<RetainSummaryManager> Summaries; + mutable OwningPtr<RetainSummaryManager> SummariesGC; mutable ARCounts::Factory ARCountFactory; @@ -2386,7 +2426,7 @@ public: 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; + bool ARCEnabled = (bool)Ctx.getLangOpts().ObjCAutoRefCount; if (GCEnabled) { if (!SummariesGC) SummariesGC.reset(new RetainSummaryManager(Ctx, true, ARCEnabled)); @@ -2406,7 +2446,7 @@ public: return getSummaryManager(C.getASTContext(), C.isObjCGCEnabled()); } - void printState(raw_ostream &Out, const ProgramState *State, + void printState(raw_ostream &Out, ProgramStateRef State, const char *NL, const char *Sep) const; void checkBind(SVal loc, SVal val, const Stmt *S, CheckerContext &C) const; @@ -2415,66 +2455,72 @@ public: void checkPostStmt(const CallExpr *CE, CheckerContext &C) const; void checkPostStmt(const CXXConstructExpr *CE, CheckerContext &C) const; + void checkPostStmt(const ObjCArrayLiteral *AL, CheckerContext &C) const; + void checkPostStmt(const ObjCDictionaryLiteral *DL, 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, + ProgramStateRef evalAssume(ProgramStateRef state, SVal Cond, bool Assumption) const; - const ProgramState * - checkRegionChanges(const ProgramState *state, + ProgramStateRef + checkRegionChanges(ProgramStateRef state, const StoreManager::InvalidatedSymbols *invalidated, ArrayRef<const MemRegion *> ExplicitRegions, - ArrayRef<const MemRegion *> Regions) const; + ArrayRef<const MemRegion *> Regions, + const CallOrObjCMessage *Call) const; - bool wantsRegionChangeUpdate(const ProgramState *state) const { + bool wantsRegionChangeUpdate(ProgramStateRef 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; + SymbolRef Sym, ProgramStateRef state) const; void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const; - void checkEndPath(EndOfFunctionNodeBuilder &Builder, ExprEngine &Eng) const; + void checkEndPath(CheckerContext &C) const; - const ProgramState *updateSymbol(const ProgramState *state, SymbolRef sym, + ProgramStateRef updateSymbol(ProgramStateRef state, SymbolRef sym, RefVal V, ArgEffect E, RefVal::Kind &hasErr, CheckerContext &C) const; - void processNonLeakError(const ProgramState *St, SourceRange ErrorRange, + void processNonLeakError(ProgramStateRef St, SourceRange ErrorRange, RefVal::Kind ErrorKind, SymbolRef Sym, CheckerContext &C) const; + + void processObjCLiterals(CheckerContext &C, const Expr *Ex) const; const ProgramPointTag *getDeadSymbolTag(SymbolRef sym) const; - const ProgramState *handleSymbolDeath(const ProgramState *state, + ProgramStateRef handleSymbolDeath(ProgramStateRef state, SymbolRef sid, RefVal V, SmallVectorImpl<SymbolRef> &Leaked) const; - std::pair<ExplodedNode *, const ProgramState *> - handleAutoreleaseCounts(const ProgramState *state, + std::pair<ExplodedNode *, ProgramStateRef > + handleAutoreleaseCounts(ProgramStateRef state, GenericNodeBuilderRefCount Bd, ExplodedNode *Pred, - ExprEngine &Eng, SymbolRef Sym, RefVal V) const; + CheckerContext &Ctx, SymbolRef Sym, RefVal V) const; - ExplodedNode *processLeaks(const ProgramState *state, + ExplodedNode *processLeaks(ProgramStateRef state, SmallVectorImpl<SymbolRef> &Leaked, GenericNodeBuilderRefCount &Builder, - ExprEngine &Eng, + CheckerContext &Ctx, ExplodedNode *Pred = 0) const; }; } // end anonymous namespace namespace { class StopTrackingCallback : public SymbolVisitor { - const ProgramState *state; + ProgramStateRef state; public: - StopTrackingCallback(const ProgramState *st) : state(st) {} - const ProgramState *getState() const { return state; } + StopTrackingCallback(ProgramStateRef st) : state(st) {} + ProgramStateRef getState() const { return state; } bool VisitSymbol(SymbolRef sym) { state = state->remove<RefBindings>(sym); @@ -2495,9 +2541,10 @@ void RetainCountChecker::checkPostStmt(const BlockExpr *BE, if (!BE->getBlockDecl()->hasCaptures()) return; - const ProgramState *state = C.getState(); + ProgramStateRef state = C.getState(); const BlockDataRegion *R = - cast<BlockDataRegion>(state->getSVal(BE).getAsRegion()); + cast<BlockDataRegion>(state->getSVal(BE, + C.getLocationContext()).getAsRegion()); BlockDataRegion::referenced_vars_iterator I = R->referenced_vars_begin(), E = R->referenced_vars_end(); @@ -2509,7 +2556,7 @@ void RetainCountChecker::checkPostStmt(const BlockExpr *BE, // 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(); + const LocationContext *LC = C.getLocationContext(); MemRegionManager &MemMgr = C.getSValBuilder().getRegionManager(); for ( ; I != E; ++I) { @@ -2546,8 +2593,8 @@ void RetainCountChecker::checkPostStmt(const CastExpr *CE, break; } - const ProgramState *state = C.getState(); - SymbolRef Sym = state->getSVal(CE).getAsLocSymbol(); + ProgramStateRef state = C.getState(); + SymbolRef Sym = state->getSVal(CE, C.getLocationContext()).getAsLocSymbol(); if (!Sym) return; const RefVal* T = state->get<RefBindings>(Sym); @@ -2563,15 +2610,18 @@ void RetainCountChecker::checkPostStmt(const CastExpr *CE, return; } - C.generateNode(state); + C.addTransition(state); } void RetainCountChecker::checkPostStmt(const CallExpr *CE, CheckerContext &C) const { + if (C.wasInlined) + return; + // Get the callee. - const ProgramState *state = C.getState(); + ProgramStateRef state = C.getState(); const Expr *Callee = CE->getCallee(); - SVal L = state->getSVal(Callee); + SVal L = state->getSVal(Callee, C.getLocationContext()); RetainSummaryManager &Summaries = getSummaryManager(C); const RetainSummary *Summ = 0; @@ -2591,7 +2641,7 @@ void RetainCountChecker::checkPostStmt(const CallExpr *CE, if (!Summ) Summ = Summaries.getDefaultSummary(); - checkSummary(*Summ, CallOrObjCMessage(CE, state), C); + checkSummary(*Summ, CallOrObjCMessage(CE, state, C.getLocationContext()), C); } void RetainCountChecker::checkPostStmt(const CXXConstructExpr *CE, @@ -2607,20 +2657,62 @@ void RetainCountChecker::checkPostStmt(const CXXConstructExpr *CE, if (!Summ) return; - const ProgramState *state = C.getState(); - checkSummary(*Summ, CallOrObjCMessage(CE, state), C); + ProgramStateRef state = C.getState(); + checkSummary(*Summ, CallOrObjCMessage(CE, state, C.getLocationContext()), C); +} + +void RetainCountChecker::processObjCLiterals(CheckerContext &C, + const Expr *Ex) const { + ProgramStateRef state = C.getState(); + const ExplodedNode *pred = C.getPredecessor(); + for (Stmt::const_child_iterator it = Ex->child_begin(), et = Ex->child_end() ; + it != et ; ++it) { + const Stmt *child = *it; + SVal V = state->getSVal(child, pred->getLocationContext()); + if (SymbolRef sym = V.getAsSymbol()) + if (const RefVal* T = state->get<RefBindings>(sym)) { + RefVal::Kind hasErr = (RefVal::Kind) 0; + state = updateSymbol(state, sym, *T, MayEscape, hasErr, C); + if (hasErr) { + processNonLeakError(state, child->getSourceRange(), hasErr, sym, C); + return; + } + } + } + + // Return the object as autoreleased. + // RetEffect RE = RetEffect::MakeNotOwned(RetEffect::ObjC); + if (SymbolRef sym = + state->getSVal(Ex, pred->getLocationContext()).getAsSymbol()) { + QualType ResultTy = Ex->getType(); + state = state->set<RefBindings>(sym, RefVal::makeNotOwned(RetEffect::ObjC, + ResultTy)); + } + + C.addTransition(state); +} + +void RetainCountChecker::checkPostStmt(const ObjCArrayLiteral *AL, + CheckerContext &C) const { + // Apply the 'MayEscape' to all values. + processObjCLiterals(C, AL); +} + +void RetainCountChecker::checkPostStmt(const ObjCDictionaryLiteral *DL, + CheckerContext &C) const { + // Apply the 'MayEscape' to all keys and values. + processObjCLiterals(C, DL); } void RetainCountChecker::checkPostObjCMessage(const ObjCMessage &Msg, CheckerContext &C) const { - const ProgramState *state = C.getState(); - ExplodedNode *Pred = C.getPredecessor(); + ProgramStateRef state = C.getState(); RetainSummaryManager &Summaries = getSummaryManager(C); const RetainSummary *Summ; if (Msg.isInstanceMessage()) { - const LocationContext *LC = Pred->getLocationContext(); + const LocationContext *LC = C.getLocationContext(); Summ = Summaries.getInstanceMethodSummary(Msg, state, LC); } else { Summ = Summaries.getClassMethodSummary(Msg); @@ -2630,7 +2722,7 @@ void RetainCountChecker::checkPostObjCMessage(const ObjCMessage &Msg, if (!Summ) return; - checkSummary(*Summ, CallOrObjCMessage(Msg, state), C); + checkSummary(*Summ, CallOrObjCMessage(Msg, state, C.getLocationContext()), C); } /// GetReturnType - Used to get the return type of a message expression or @@ -2664,7 +2756,7 @@ static QualType GetReturnType(const Expr *RetE, ASTContext &Ctx) { void RetainCountChecker::checkSummary(const RetainSummary &Summ, const CallOrObjCMessage &CallOrMsg, CheckerContext &C) const { - const ProgramState *state = C.getState(); + ProgramStateRef state = C.getState(); // Evaluate the effect of the arguments. RefVal::Kind hasErr = (RefVal::Kind) 0; @@ -2689,7 +2781,7 @@ void RetainCountChecker::checkSummary(const RetainSummary &Summ, // Evaluate the effect on the message receiver. bool ReceiverIsTracked = false; if (!hasErr && CallOrMsg.isObjCMessage()) { - const LocationContext *LC = C.getPredecessor()->getLocationContext(); + const LocationContext *LC = C.getLocationContext(); SVal Receiver = CallOrMsg.getInstanceMessageReceiver(LC); if (SymbolRef Sym = Receiver.getAsLocSymbol()) { if (const RefVal *T = state->get<RefBindings>(Sym)) { @@ -2722,7 +2814,7 @@ void RetainCountChecker::checkSummary(const RetainSummary &Summ, switch (RE.getKind()) { default: - llvm_unreachable("Unhandled RetEffect."); break; + llvm_unreachable("Unhandled RetEffect."); case RetEffect::NoRet: // No work necessary. @@ -2730,7 +2822,8 @@ void RetainCountChecker::checkSummary(const RetainSummary &Summ, case RetEffect::OwnedAllocatedSymbol: case RetEffect::OwnedSymbol: { - SymbolRef Sym = state->getSVal(CallOrMsg.getOriginExpr()).getAsSymbol(); + SymbolRef Sym = state->getSVal(CallOrMsg.getOriginExpr(), + C.getLocationContext()).getAsSymbol(); if (!Sym) break; @@ -2757,7 +2850,7 @@ void RetainCountChecker::checkSummary(const RetainSummary &Summ, case RetEffect::ARCNotOwnedSymbol: case RetEffect::NotOwnedSymbol: { const Expr *Ex = CallOrMsg.getOriginExpr(); - SymbolRef Sym = state->getSVal(Ex).getAsSymbol(); + SymbolRef Sym = state->getSVal(Ex, C.getLocationContext()).getAsSymbol(); if (!Sym) break; @@ -2776,7 +2869,7 @@ void RetainCountChecker::checkSummary(const RetainSummary &Summ, if (state == C.getState()) { NewNode = C.getPredecessor(); } else { - NewNode = C.generateNode(state); + NewNode = C.addTransition(state); } // Annotate the node with summary we used. @@ -2791,15 +2884,15 @@ void RetainCountChecker::checkSummary(const RetainSummary &Summ, } -const ProgramState * -RetainCountChecker::updateSymbol(const ProgramState *state, SymbolRef sym, +ProgramStateRef +RetainCountChecker::updateSymbol(ProgramStateRef 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; + IgnoreRetainMsg = (bool)C.getASTContext().getLangOpts().ObjCAutoRefCount; switch (E) { default: break; @@ -2822,7 +2915,6 @@ RetainCountChecker::updateSymbol(const ProgramState *state, SymbolRef sym, case IncRefMsg: case MakeCollectable: llvm_unreachable("DecRefMsg/IncRefMsg/MakeCollectable already converted"); - return state; case Dealloc: // Any use of -dealloc in GC is *bad*. @@ -2835,7 +2927,6 @@ RetainCountChecker::updateSymbol(const ProgramState *state, SymbolRef sym, 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; @@ -2879,7 +2970,6 @@ RetainCountChecker::updateSymbol(const ProgramState *state, SymbolRef sym, switch (V.getKind()) { default: llvm_unreachable("Invalid RefVal state for a retain."); - break; case RefVal::Owned: case RefVal::NotOwned: V = V + 1; @@ -2901,7 +2991,6 @@ RetainCountChecker::updateSymbol(const ProgramState *state, SymbolRef sym, default: // case 'RefVal::Released' handled above. llvm_unreachable("Invalid RefVal state for a release."); - break; case RefVal::Owned: assert(V.getCount() > 0); @@ -2932,7 +3021,7 @@ RetainCountChecker::updateSymbol(const ProgramState *state, SymbolRef sym, return state->set<RefBindings>(sym, V); } -void RetainCountChecker::processNonLeakError(const ProgramState *St, +void RetainCountChecker::processNonLeakError(ProgramStateRef St, SourceRange ErrorRange, RefVal::Kind ErrorKind, SymbolRef Sym, @@ -2945,7 +3034,6 @@ void RetainCountChecker::processNonLeakError(const ProgramState *St, switch (ErrorKind) { default: llvm_unreachable("Unhandled error."); - return; case RefVal::ErrorUseAfterRelease: if (!useAfterRelease) useAfterRelease.reset(new UseAfterRelease()); @@ -2969,7 +3057,7 @@ void RetainCountChecker::processNonLeakError(const ProgramState *St, } assert(BT); - CFRefReport *report = new CFRefReport(*BT, C.getASTContext().getLangOptions(), + CFRefReport *report = new CFRefReport(*BT, C.getASTContext().getLangOpts(), C.isObjCGCEnabled(), SummaryLog, N, Sym); report->addRange(ErrorRange); @@ -2982,11 +3070,8 @@ void RetainCountChecker::processNonLeakError(const ProgramState *St, 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); - - const FunctionDecl *FD = L.getAsFunctionDecl(); + ProgramStateRef state = C.getState(); + const FunctionDecl *FD = C.getCalleeDecl(CE); if (!FD) return false; @@ -3008,7 +3093,7 @@ bool RetainCountChecker::evalCall(const CallExpr *CE, CheckerContext &C) const { // See if it's one of the specific functions we know how to eval. bool canEval = false; - QualType ResultTy = FD->getResultType(); + QualType ResultTy = CE->getCallReturnType(); if (ResultTy->isObjCIdType()) { // Handle: id NSMakeCollectable(CFTypeRef) canEval = II->isStr("NSMakeCollectable"); @@ -3026,14 +3111,15 @@ bool RetainCountChecker::evalCall(const CallExpr *CE, CheckerContext &C) const { return false; // Bind the return value. - SVal RetVal = state->getSVal(CE->getArg(0)); + const LocationContext *LCtx = C.getLocationContext(); + SVal RetVal = state->getSVal(CE->getArg(0), LCtx); 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); + SVal RetVal = SVB.getConjuredSymbolVal(0, CE, LCtx, ResultTy, Count); } - state = state->BindExpr(CE, RetVal, false); + state = state->BindExpr(CE, LCtx, RetVal, false); // FIXME: This should not be necessary, but otherwise the argument seems to be // considered alive during the next statement. @@ -3046,7 +3132,7 @@ bool RetainCountChecker::evalCall(const CallExpr *CE, CheckerContext &C) const { // Invalidate the argument region. unsigned Count = C.getCurrentBlockCount(); - state = state->invalidateRegions(ArgRegion, CE, Count); + state = state->invalidateRegions(ArgRegion, CE, Count, LCtx); // Restore the refcount status of the argument. if (Binding) @@ -3061,14 +3147,30 @@ bool RetainCountChecker::evalCall(const CallExpr *CE, CheckerContext &C) const { // Handle return statements. //===----------------------------------------------------------------------===// +// Return true if the current LocationContext has no caller context. +static bool inTopFrame(CheckerContext &C) { + const LocationContext *LC = C.getLocationContext(); + return LC->getParent() == 0; +} + void RetainCountChecker::checkPreStmt(const ReturnStmt *S, CheckerContext &C) const { + + // Only adjust the reference count if this is the top-level call frame, + // and not the result of inlining. In the future, we should do + // better checking even for inlined calls, and see if they match + // with their expected semantics (e.g., the method should return a retained + // object, etc.). + if (!inTopFrame(C)) + return; + const Expr *RetE = S->getRetValue(); if (!RetE) return; - const ProgramState *state = C.getState(); - SymbolRef Sym = state->getSValAsScalarOrLoc(RetE).getAsLocSymbol(); + ProgramStateRef state = C.getState(); + SymbolRef Sym = + state->getSValAsScalarOrLoc(RetE, C.getLocationContext()).getAsLocSymbol(); if (!Sym) return; @@ -3107,7 +3209,7 @@ void RetainCountChecker::checkPreStmt(const ReturnStmt *S, // Update the binding. state = state->set<RefBindings>(Sym, X); - ExplodedNode *Pred = C.generateNode(state); + ExplodedNode *Pred = C.addTransition(state); // At this point we have updated the state properly. // Everything after this is merely checking to see if the return value has @@ -3121,8 +3223,7 @@ void RetainCountChecker::checkPreStmt(const ReturnStmt *S, static SimpleProgramPointTag AutoreleaseTag("RetainCountChecker : Autorelease"); GenericNodeBuilderRefCount Bd(C, &AutoreleaseTag); - llvm::tie(Pred, state) = handleAutoreleaseCounts(state, Bd, Pred, - C.getEngine(), Sym, X); + llvm::tie(Pred, state) = handleAutoreleaseCounts(state, Bd, Pred, C, Sym, X); // Did we cache out? if (!Pred) @@ -3159,7 +3260,7 @@ void RetainCountChecker::checkReturnWithRetEffect(const ReturnStmt *S, ExplodedNode *Pred, RetEffect RE, RefVal X, SymbolRef Sym, - const ProgramState *state) const { + ProgramStateRef state) const { // Any leaks or other errors? if (X.isReturnedOwned() && X.getCount() == 0) { if (RE.getKind() != RetEffect::NoRet) { @@ -3186,14 +3287,14 @@ void RetainCountChecker::checkReturnWithRetEffect(const ReturnStmt *S, static SimpleProgramPointTag ReturnOwnLeakTag("RetainCountChecker : ReturnsOwnLeak"); - ExplodedNode *N = C.generateNode(state, Pred, &ReturnOwnLeakTag); + ExplodedNode *N = C.addTransition(state, Pred, &ReturnOwnLeakTag); if (N) { - const LangOptions &LOpts = C.getASTContext().getLangOptions(); + const LangOptions &LOpts = C.getASTContext().getLangOpts(); bool GCEnabled = C.isObjCGCEnabled(); CFRefReport *report = new CFRefLeakReport(*getLeakAtReturnBug(LOpts, GCEnabled), LOpts, GCEnabled, SummaryLog, - N, Sym, C.getEngine()); + N, Sym, C); C.EmitReport(report); } } @@ -3206,14 +3307,14 @@ void RetainCountChecker::checkReturnWithRetEffect(const ReturnStmt *S, static SimpleProgramPointTag ReturnNotOwnedTag("RetainCountChecker : ReturnNotOwnedForOwned"); - ExplodedNode *N = C.generateNode(state, Pred, &ReturnNotOwnedTag); + ExplodedNode *N = C.addTransition(state, Pred, &ReturnNotOwnedTag); if (N) { if (!returnNotOwnedForOwned) returnNotOwnedForOwned.reset(new ReturnedNotOwnedForOwned()); CFRefReport *report = new CFRefReport(*returnNotOwnedForOwned, - C.getASTContext().getLangOptions(), + C.getASTContext().getLangOpts(), C.isObjCGCEnabled(), SummaryLog, N, Sym); C.EmitReport(report); } @@ -3236,7 +3337,7 @@ void RetainCountChecker::checkBind(SVal loc, SVal val, const Stmt *S, // (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(); + ProgramStateRef state = C.getState(); if (loc::MemRegionVal *regionLoc = dyn_cast<loc::MemRegionVal>(&loc)) { escapes = !regionLoc->getRegion()->hasStackStorage(); @@ -3247,6 +3348,12 @@ void RetainCountChecker::checkBind(SVal loc, SVal val, const Stmt *S, // the binding). escapes = (state == (state->bindLoc(*regionLoc, val))); } + if (!escapes) { + // Case 4: We do not currently model what happens when a symbol is + // assigned to a struct field, so be conservative here and let the symbol + // go. TODO: This could definitely be improved upon. + escapes = !isa<VarRegion>(regionLoc->getRegion()); + } } // If our store can represent the binding and we aren't storing to something @@ -3261,7 +3368,7 @@ void RetainCountChecker::checkBind(SVal loc, SVal val, const Stmt *S, C.addTransition(state); } -const ProgramState *RetainCountChecker::evalAssume(const ProgramState *state, +ProgramStateRef RetainCountChecker::evalAssume(ProgramStateRef state, SVal Cond, bool Assumption) const { @@ -3294,11 +3401,12 @@ const ProgramState *RetainCountChecker::evalAssume(const ProgramState *state, return state; } -const ProgramState * -RetainCountChecker::checkRegionChanges(const ProgramState *state, +ProgramStateRef +RetainCountChecker::checkRegionChanges(ProgramStateRef state, const StoreManager::InvalidatedSymbols *invalidated, ArrayRef<const MemRegion *> ExplicitRegions, - ArrayRef<const MemRegion *> Regions) const { + ArrayRef<const MemRegion *> Regions, + const CallOrObjCMessage *Call) const { if (!invalidated) return state; @@ -3324,10 +3432,11 @@ RetainCountChecker::checkRegionChanges(const ProgramState *state, // Handle dead symbols and end-of-path. //===----------------------------------------------------------------------===// -std::pair<ExplodedNode *, const ProgramState *> -RetainCountChecker::handleAutoreleaseCounts(const ProgramState *state, +std::pair<ExplodedNode *, ProgramStateRef > +RetainCountChecker::handleAutoreleaseCounts(ProgramStateRef state, GenericNodeBuilderRefCount Bd, - ExplodedNode *Pred, ExprEngine &Eng, + ExplodedNode *Pred, + CheckerContext &Ctx, SymbolRef Sym, RefVal V) const { unsigned ACnt = V.getAutoreleaseCount(); @@ -3335,7 +3444,7 @@ RetainCountChecker::handleAutoreleaseCounts(const ProgramState *state, if (!ACnt) return std::make_pair(Pred, state); - assert(!Eng.isObjCGCEnabled() && "Autorelease counts in GC mode?"); + assert(!Ctx.isObjCGCEnabled() && "Autorelease counts in GC mode?"); unsigned Cnt = V.getCount(); // FIXME: Handle sending 'autorelease' to already released object. @@ -3366,10 +3475,8 @@ RetainCountChecker::handleAutoreleaseCounts(const ProgramState *state, V = V ^ RefVal::ErrorOverAutorelease; state = state->set<RefBindings>(Sym, V); - if (ExplodedNode *N = Bd.MakeNode(state, Pred)) { - N->markAsSink(); - - llvm::SmallString<128> sbuf; + if (ExplodedNode *N = Bd.MakeNode(state, Pred, true)) { + SmallString<128> sbuf; llvm::raw_svector_ostream os(sbuf); os << "Object over-autoreleased: object was sent -autorelease "; if (V.getAutoreleaseCount() > 1) @@ -3379,18 +3486,18 @@ RetainCountChecker::handleAutoreleaseCounts(const ProgramState *state, if (!overAutorelease) overAutorelease.reset(new OverAutorelease()); - const LangOptions &LOpts = Eng.getContext().getLangOptions(); + const LangOptions &LOpts = Ctx.getASTContext().getLangOpts(); CFRefReport *report = new CFRefReport(*overAutorelease, LOpts, /* GCEnabled = */ false, SummaryLog, N, Sym, os.str()); - Eng.getBugReporter().EmitReport(report); + Ctx.EmitReport(report); } - return std::make_pair((ExplodedNode *)0, (const ProgramState *)0); + return std::make_pair((ExplodedNode *)0, (ProgramStateRef )0); } -const ProgramState * -RetainCountChecker::handleSymbolDeath(const ProgramState *state, +ProgramStateRef +RetainCountChecker::handleSymbolDeath(ProgramStateRef state, SymbolRef sid, RefVal V, SmallVectorImpl<SymbolRef> &Leaked) const { bool hasLeak = false; @@ -3407,10 +3514,11 @@ RetainCountChecker::handleSymbolDeath(const ProgramState *state, } ExplodedNode * -RetainCountChecker::processLeaks(const ProgramState *state, +RetainCountChecker::processLeaks(ProgramStateRef state, SmallVectorImpl<SymbolRef> &Leaked, GenericNodeBuilderRefCount &Builder, - ExprEngine &Eng, ExplodedNode *Pred) const { + CheckerContext &Ctx, + ExplodedNode *Pred) const { if (Leaked.empty()) return Pred; @@ -3421,51 +3529,58 @@ RetainCountChecker::processLeaks(const ProgramState *state, for (SmallVectorImpl<SymbolRef>::iterator I = Leaked.begin(), E = Leaked.end(); I != E; ++I) { - const LangOptions &LOpts = Eng.getContext().getLangOptions(); - bool GCEnabled = Eng.isObjCGCEnabled(); + const LangOptions &LOpts = Ctx.getASTContext().getLangOpts(); + bool GCEnabled = Ctx.isObjCGCEnabled(); CFRefBug *BT = Pred ? getLeakWithinFunctionBug(LOpts, GCEnabled) : getLeakAtReturnBug(LOpts, GCEnabled); assert(BT && "BugType not initialized."); CFRefLeakReport *report = new CFRefLeakReport(*BT, LOpts, GCEnabled, - SummaryLog, N, *I, Eng); - Eng.getBugReporter().EmitReport(report); + SummaryLog, N, *I, Ctx); + Ctx.EmitReport(report); } } return N; } -void RetainCountChecker::checkEndPath(EndOfFunctionNodeBuilder &Builder, - ExprEngine &Eng) const { - const ProgramState *state = Builder.getState(); - GenericNodeBuilderRefCount Bd(Builder); +void RetainCountChecker::checkEndPath(CheckerContext &Ctx) const { + ProgramStateRef state = Ctx.getState(); + GenericNodeBuilderRefCount Bd(Ctx); RefBindings B = state->get<RefBindings>(); - ExplodedNode *Pred = Builder.getPredecessor(); + ExplodedNode *Pred = Ctx.getPredecessor(); for (RefBindings::iterator I = B.begin(), E = B.end(); I != E; ++I) { - llvm::tie(Pred, state) = handleAutoreleaseCounts(state, Bd, Pred, Eng, + llvm::tie(Pred, state) = handleAutoreleaseCounts(state, Bd, Pred, Ctx, I->first, I->second); if (!state) return; } + // If the current LocationContext has a parent, don't check for leaks. + // We will do that later. + // FIXME: we should instead check for imblances of the retain/releases, + // and suggest annotations. + if (Ctx.getLocationContext()->getParent()) + return; + B = state->get<RefBindings>(); SmallVector<SymbolRef, 10> Leaked; for (RefBindings::iterator I = B.begin(), E = B.end(); I != E; ++I) state = handleSymbolDeath(state, I->first, I->second, Leaked); - processLeaks(state, Leaked, Bd, Eng, Pred); + processLeaks(state, Leaked, Bd, Ctx, Pred); } const ProgramPointTag * RetainCountChecker::getDeadSymbolTag(SymbolRef sym) const { const SimpleProgramPointTag *&tag = DeadSymbolTags[sym]; if (!tag) { - llvm::SmallString<64> buf; + SmallString<64> buf; llvm::raw_svector_ostream out(buf); - out << "RetainCountChecker : Dead Symbol : " << sym->getSymbolID(); + out << "RetainCountChecker : Dead Symbol : "; + sym->dumpToStream(out); tag = new SimpleProgramPointTag(out.str()); } return tag; @@ -3473,10 +3588,9 @@ RetainCountChecker::getDeadSymbolTag(SymbolRef sym) const { void RetainCountChecker::checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const { - ExprEngine &Eng = C.getEngine(); ExplodedNode *Pred = C.getPredecessor(); - const ProgramState *state = C.getState(); + ProgramStateRef state = C.getState(); RefBindings B = state->get<RefBindings>(); // Update counts from autorelease pools @@ -3487,7 +3601,7 @@ void RetainCountChecker::checkDeadSymbols(SymbolReaper &SymReaper, // Use the symbol as the tag. // FIXME: This might not be as unique as we would like. GenericNodeBuilderRefCount Bd(C, getDeadSymbolTag(Sym)); - llvm::tie(Pred, state) = handleAutoreleaseCounts(state, Bd, Pred, Eng, + llvm::tie(Pred, state) = handleAutoreleaseCounts(state, Bd, Pred, C, Sym, *T); if (!state) return; @@ -3505,7 +3619,7 @@ void RetainCountChecker::checkDeadSymbols(SymbolReaper &SymReaper, { GenericNodeBuilderRefCount Bd(C, this); - Pred = processLeaks(state, Leaked, Bd, Eng, Pred); + Pred = processLeaks(state, Leaked, Bd, C, Pred); } // Did we cache out? @@ -3520,7 +3634,7 @@ void RetainCountChecker::checkDeadSymbols(SymbolReaper &SymReaper, B = F.remove(B, *I); state = state->set<RefBindings>(B); - C.generateNode(state, Pred); + C.addTransition(state, Pred); } //===----------------------------------------------------------------------===// @@ -3528,10 +3642,10 @@ void RetainCountChecker::checkDeadSymbols(SymbolReaper &SymReaper, //===----------------------------------------------------------------------===// static void PrintPool(raw_ostream &Out, SymbolRef Sym, - const ProgramState *State) { + ProgramStateRef State) { Out << ' '; if (Sym) - Out << Sym->getSymbolID(); + Sym->dumpToStream(Out); else Out << "<pool>"; Out << ":{"; @@ -3544,14 +3658,14 @@ static void PrintPool(raw_ostream &Out, SymbolRef Sym, Out << '}'; } -static bool UsesAutorelease(const ProgramState *state) { +static bool UsesAutorelease(ProgramStateRef 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()); } -void RetainCountChecker::printState(raw_ostream &Out, const ProgramState *State, +void RetainCountChecker::printState(raw_ostream &Out, ProgramStateRef State, const char *NL, const char *Sep) const { RefBindings B = State->get<RefBindings>(); diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ReturnPointerRangeChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ReturnPointerRangeChecker.cpp index e761bff..6e56593 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ReturnPointerRangeChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ReturnPointerRangeChecker.cpp @@ -25,7 +25,7 @@ using namespace ento; namespace { class ReturnPointerRangeChecker : public Checker< check::PreStmt<ReturnStmt> > { - mutable llvm::OwningPtr<BuiltinBug> BT; + mutable OwningPtr<BuiltinBug> BT; public: void checkPreStmt(const ReturnStmt *RS, CheckerContext &C) const; }; @@ -33,13 +33,13 @@ public: void ReturnPointerRangeChecker::checkPreStmt(const ReturnStmt *RS, CheckerContext &C) const { - const ProgramState *state = C.getState(); + ProgramStateRef state = C.getState(); const Expr *RetE = RS->getRetValue(); if (!RetE) return; - SVal V = state->getSVal(RetE); + SVal V = state->getSVal(RetE, C.getLocationContext()); const MemRegion *R = V.getAsRegion(); const ElementRegion *ER = dyn_cast_or_null<ElementRegion>(R); @@ -58,8 +58,8 @@ void ReturnPointerRangeChecker::checkPreStmt(const ReturnStmt *RS, = C.getStoreManager().getSizeInElements(state, ER->getSuperRegion(), ER->getValueType()); - const ProgramState *StInBound = state->assumeInBound(Idx, NumElements, true); - const ProgramState *StOutBound = state->assumeInBound(Idx, NumElements, false); + ProgramStateRef StInBound = state->assumeInBound(Idx, NumElements, true); + ProgramStateRef StOutBound = state->assumeInBound(Idx, NumElements, false); if (StOutBound && !StInBound) { ExplodedNode *N = C.generateSink(StOutBound); diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ReturnUndefChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ReturnUndefChecker.cpp index e8c8d90..7b1f0b1 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ReturnUndefChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ReturnUndefChecker.cpp @@ -25,7 +25,7 @@ using namespace ento; namespace { class ReturnUndefChecker : public Checker< check::PreStmt<ReturnStmt> > { - mutable llvm::OwningPtr<BuiltinBug> BT; + mutable OwningPtr<BuiltinBug> BT; public: void checkPreStmt(const ReturnStmt *RS, CheckerContext &C) const; }; @@ -38,7 +38,7 @@ void ReturnUndefChecker::checkPreStmt(const ReturnStmt *RS, if (!RetE) return; - if (!C.getState()->getSVal(RetE).isUndef()) + if (!C.getState()->getSVal(RetE, C.getLocationContext()).isUndef()) return; ExplodedNode *N = C.generateSink(); @@ -54,7 +54,8 @@ void ReturnUndefChecker::checkPreStmt(const ReturnStmt *RS, new BugReport(*BT, BT->getDescription(), N); report->addRange(RetE->getSourceRange()); - report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, RetE)); + report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, RetE, + report)); C.EmitReport(report); } diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp index 91c4b96..54cf569 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp @@ -26,12 +26,12 @@ using namespace ento; namespace { class StackAddrEscapeChecker : public Checker< check::PreStmt<ReturnStmt>, check::EndPath > { - mutable llvm::OwningPtr<BuiltinBug> BT_stackleak; - mutable llvm::OwningPtr<BuiltinBug> BT_returnstack; + mutable OwningPtr<BuiltinBug> BT_stackleak; + mutable OwningPtr<BuiltinBug> BT_returnstack; public: void checkPreStmt(const ReturnStmt *RS, CheckerContext &C) const; - void checkEndPath(EndOfFunctionNodeBuilder &B, ExprEngine &Eng) const; + void checkEndPath(CheckerContext &Ctx) const; private: void EmitStackError(CheckerContext &C, const MemRegion *R, const Expr *RetE) const; @@ -100,7 +100,7 @@ void StackAddrEscapeChecker::EmitStackError(CheckerContext &C, const MemRegion * new BuiltinBug("Return of address to stack-allocated memory")); // Generate a report for this bug. - llvm::SmallString<512> buf; + SmallString<512> buf; llvm::raw_svector_ostream os(buf); SourceRange range = GenName(os, R, C.getSourceManager()); os << " returned to caller"; @@ -113,45 +113,53 @@ void StackAddrEscapeChecker::EmitStackError(CheckerContext &C, const MemRegion * } void StackAddrEscapeChecker::checkPreStmt(const ReturnStmt *RS, - CheckerContext &C) const { + CheckerContext &C) const { const Expr *RetE = RS->getRetValue(); if (!RetE) return; - SVal V = C.getState()->getSVal(RetE); + SVal V = C.getState()->getSVal(RetE, C.getLocationContext()); const MemRegion *R = V.getAsRegion(); - if (!R || !R->hasStackStorage()) - return; + if (!R) + return; - if (R->hasStackStorage()) { - // Automatic reference counting automatically copies blocks. - if (C.getASTContext().getLangOptions().ObjCAutoRefCount && - isa<BlockDataRegion>(R)) - return; + const StackSpaceRegion *SS = + dyn_cast_or_null<StackSpaceRegion>(R->getMemorySpace()); + + if (!SS) + return; - EmitStackError(C, R, RetE); + // Return stack memory in an ancestor stack frame is fine. + const StackFrameContext *SFC = SS->getStackFrame(); + if (SFC != C.getLocationContext()->getCurrentStackFrame()) + return; + + // Automatic reference counting automatically copies blocks. + if (C.getASTContext().getLangOpts().ObjCAutoRefCount && + isa<BlockDataRegion>(R)) return; - } -} -void StackAddrEscapeChecker::checkEndPath(EndOfFunctionNodeBuilder &B, - ExprEngine &Eng) const { + EmitStackError(C, R, RetE); +} - const ProgramState *state = B.getState(); +void StackAddrEscapeChecker::checkEndPath(CheckerContext &Ctx) const { + ProgramStateRef state = Ctx.getState(); // Iterate over all bindings to global variables and see if it contains // a memory region in the stack space. class CallBack : public StoreManager::BindingsHandler { private: - ExprEngine &Eng; + CheckerContext &Ctx; const StackFrameContext *CurSFC; public: SmallVector<std::pair<const MemRegion*, const MemRegion*>, 10> V; - CallBack(ExprEngine &Eng, const LocationContext *LCtx) - : Eng(Eng), CurSFC(LCtx->getCurrentStackFrame()) {} + CallBack(CheckerContext &CC) : + Ctx(CC), + CurSFC(CC.getLocationContext()->getCurrentStackFrame()) + {} bool HandleBinding(StoreManager &SMgr, Store store, const MemRegion *region, SVal val) { @@ -165,7 +173,7 @@ void StackAddrEscapeChecker::checkEndPath(EndOfFunctionNodeBuilder &B, // Under automated retain release, it is okay to assign a block // directly to a global variable. - if (Eng.getContext().getLangOptions().ObjCAutoRefCount && + if (Ctx.getASTContext().getLangOpts().ObjCAutoRefCount && isa<BlockDataRegion>(vR)) return true; @@ -181,14 +189,14 @@ void StackAddrEscapeChecker::checkEndPath(EndOfFunctionNodeBuilder &B, } }; - CallBack cb(Eng, B.getPredecessor()->getLocationContext()); + CallBack cb(Ctx); state->getStateManager().getStoreManager().iterBindings(state->getStore(),cb); if (cb.V.empty()) return; // Generate an error node. - ExplodedNode *N = B.generateNode(state); + ExplodedNode *N = Ctx.addTransition(state); if (!N) return; @@ -201,10 +209,10 @@ void StackAddrEscapeChecker::checkEndPath(EndOfFunctionNodeBuilder &B, for (unsigned i = 0, e = cb.V.size(); i != e; ++i) { // Generate a report for this bug. - llvm::SmallString<512> buf; + SmallString<512> buf; llvm::raw_svector_ostream os(buf); SourceRange range = GenName(os, cb.V[i].second, - Eng.getContext().getSourceManager()); + Ctx.getSourceManager()); os << " is still referred to by the global variable '"; const VarRegion *VR = cast<VarRegion>(cb.V[i].first->getBaseRegion()); os << *VR->getDecl() @@ -213,7 +221,7 @@ void StackAddrEscapeChecker::checkEndPath(EndOfFunctionNodeBuilder &B, if (range.isValid()) report->addRange(range); - Eng.getBugReporter().EmitReport(report); + Ctx.EmitReport(report); } } diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp index 1d14e9e..3745d4a 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp @@ -64,7 +64,7 @@ class StreamChecker : public Checker<eval::Call, *II_fwrite, *II_fseek, *II_ftell, *II_rewind, *II_fgetpos, *II_fsetpos, *II_clearerr, *II_feof, *II_ferror, *II_fileno; - mutable llvm::OwningPtr<BuiltinBug> BT_nullfp, BT_illegalwhence, + mutable OwningPtr<BuiltinBug> BT_nullfp, BT_illegalwhence, BT_doubleclose, BT_ResourceLeak; public: @@ -75,7 +75,7 @@ public: bool evalCall(const CallExpr *CE, CheckerContext &C) const; void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const; - void checkEndPath(EndOfFunctionNodeBuilder &B, ExprEngine &Eng) const; + void checkEndPath(CheckerContext &Ctx) const; void checkPreStmt(const ReturnStmt *S, CheckerContext &C) const; private: @@ -96,9 +96,9 @@ private: void OpenFileAux(CheckerContext &C, const CallExpr *CE) const; - const ProgramState *CheckNullStream(SVal SV, const ProgramState *state, + ProgramStateRef CheckNullStream(SVal SV, ProgramStateRef state, CheckerContext &C) const; - const ProgramState *CheckDoubleClose(const CallExpr *CE, const ProgramState *state, + ProgramStateRef CheckDoubleClose(const CallExpr *CE, ProgramStateRef state, CheckerContext &C) const; }; @@ -115,10 +115,7 @@ namespace ento { } bool StreamChecker::evalCall(const CallExpr *CE, CheckerContext &C) const { - const ProgramState *state = C.getState(); - const Expr *Callee = CE->getCallee(); - SVal L = state->getSVal(Callee); - const FunctionDecl *FD = L.getAsFunctionDecl(); + const FunctionDecl *FD = C.getCalleeDecl(CE); if (!FD) return false; @@ -221,17 +218,18 @@ void StreamChecker::Tmpfile(CheckerContext &C, const CallExpr *CE) const { } void StreamChecker::OpenFileAux(CheckerContext &C, const CallExpr *CE) const { - const ProgramState *state = C.getState(); + ProgramStateRef state = C.getState(); unsigned Count = C.getCurrentBlockCount(); SValBuilder &svalBuilder = C.getSValBuilder(); + const LocationContext *LCtx = C.getPredecessor()->getLocationContext(); DefinedSVal RetVal = - cast<DefinedSVal>(svalBuilder.getConjuredSymbolVal(0, CE, Count)); - state = state->BindExpr(CE, RetVal); + cast<DefinedSVal>(svalBuilder.getConjuredSymbolVal(0, CE, LCtx, Count)); + state = state->BindExpr(CE, C.getLocationContext(), RetVal); ConstraintManager &CM = C.getConstraintManager(); // Bifurcate the state into two: one with a valid FILE* pointer, the other // with a NULL. - const ProgramState *stateNotNull, *stateNull; + ProgramStateRef stateNotNull, stateNull; llvm::tie(stateNotNull, stateNull) = CM.assumeDual(state, RetVal); if (SymbolRef Sym = RetVal.getAsSymbol()) { @@ -247,29 +245,32 @@ void StreamChecker::OpenFileAux(CheckerContext &C, const CallExpr *CE) const { } void StreamChecker::Fclose(CheckerContext &C, const CallExpr *CE) const { - const ProgramState *state = CheckDoubleClose(CE, C.getState(), C); + ProgramStateRef state = CheckDoubleClose(CE, C.getState(), C); if (state) C.addTransition(state); } void StreamChecker::Fread(CheckerContext &C, const CallExpr *CE) const { - const ProgramState *state = C.getState(); - if (!CheckNullStream(state->getSVal(CE->getArg(3)), state, C)) + ProgramStateRef state = C.getState(); + if (!CheckNullStream(state->getSVal(CE->getArg(3), C.getLocationContext()), + state, C)) return; } void StreamChecker::Fwrite(CheckerContext &C, const CallExpr *CE) const { - const ProgramState *state = C.getState(); - if (!CheckNullStream(state->getSVal(CE->getArg(3)), state, C)) + ProgramStateRef state = C.getState(); + if (!CheckNullStream(state->getSVal(CE->getArg(3), C.getLocationContext()), + state, C)) return; } void StreamChecker::Fseek(CheckerContext &C, const CallExpr *CE) const { - const ProgramState *state = C.getState(); - if (!(state = CheckNullStream(state->getSVal(CE->getArg(0)), state, C))) + ProgramStateRef state = C.getState(); + if (!(state = CheckNullStream(state->getSVal(CE->getArg(0), + C.getLocationContext()), state, C))) return; // Check the legality of the 'whence' argument of 'fseek'. - SVal Whence = state->getSVal(CE->getArg(2)); + SVal Whence = state->getSVal(CE->getArg(2), C.getLocationContext()); const nonloc::ConcreteInt *CI = dyn_cast<nonloc::ConcreteInt>(&Whence); if (!CI) @@ -279,7 +280,7 @@ void StreamChecker::Fseek(CheckerContext &C, const CallExpr *CE) const { if (x >= 0 && x <= 2) return; - if (ExplodedNode *N = C.generateNode(state)) { + if (ExplodedNode *N = C.addTransition(state)) { if (!BT_illegalwhence) BT_illegalwhence.reset(new BuiltinBug("Illegal whence argument", "The whence argument to fseek() should be " @@ -291,61 +292,69 @@ void StreamChecker::Fseek(CheckerContext &C, const CallExpr *CE) const { } void StreamChecker::Ftell(CheckerContext &C, const CallExpr *CE) const { - const ProgramState *state = C.getState(); - if (!CheckNullStream(state->getSVal(CE->getArg(0)), state, C)) + ProgramStateRef state = C.getState(); + if (!CheckNullStream(state->getSVal(CE->getArg(0), C.getLocationContext()), + state, C)) return; } void StreamChecker::Rewind(CheckerContext &C, const CallExpr *CE) const { - const ProgramState *state = C.getState(); - if (!CheckNullStream(state->getSVal(CE->getArg(0)), state, C)) + ProgramStateRef state = C.getState(); + if (!CheckNullStream(state->getSVal(CE->getArg(0), C.getLocationContext()), + state, C)) return; } void StreamChecker::Fgetpos(CheckerContext &C, const CallExpr *CE) const { - const ProgramState *state = C.getState(); - if (!CheckNullStream(state->getSVal(CE->getArg(0)), state, C)) + ProgramStateRef state = C.getState(); + if (!CheckNullStream(state->getSVal(CE->getArg(0), C.getLocationContext()), + state, C)) return; } void StreamChecker::Fsetpos(CheckerContext &C, const CallExpr *CE) const { - const ProgramState *state = C.getState(); - if (!CheckNullStream(state->getSVal(CE->getArg(0)), state, C)) + ProgramStateRef state = C.getState(); + if (!CheckNullStream(state->getSVal(CE->getArg(0), C.getLocationContext()), + state, C)) return; } void StreamChecker::Clearerr(CheckerContext &C, const CallExpr *CE) const { - const ProgramState *state = C.getState(); - if (!CheckNullStream(state->getSVal(CE->getArg(0)), state, C)) + ProgramStateRef state = C.getState(); + if (!CheckNullStream(state->getSVal(CE->getArg(0), C.getLocationContext()), + state, C)) return; } void StreamChecker::Feof(CheckerContext &C, const CallExpr *CE) const { - const ProgramState *state = C.getState(); - if (!CheckNullStream(state->getSVal(CE->getArg(0)), state, C)) + ProgramStateRef state = C.getState(); + if (!CheckNullStream(state->getSVal(CE->getArg(0), C.getLocationContext()), + state, C)) return; } void StreamChecker::Ferror(CheckerContext &C, const CallExpr *CE) const { - const ProgramState *state = C.getState(); - if (!CheckNullStream(state->getSVal(CE->getArg(0)), state, C)) + ProgramStateRef state = C.getState(); + if (!CheckNullStream(state->getSVal(CE->getArg(0), C.getLocationContext()), + state, C)) return; } void StreamChecker::Fileno(CheckerContext &C, const CallExpr *CE) const { - const ProgramState *state = C.getState(); - if (!CheckNullStream(state->getSVal(CE->getArg(0)), state, C)) + ProgramStateRef state = C.getState(); + if (!CheckNullStream(state->getSVal(CE->getArg(0), C.getLocationContext()), + state, C)) return; } -const ProgramState *StreamChecker::CheckNullStream(SVal SV, const ProgramState *state, +ProgramStateRef StreamChecker::CheckNullStream(SVal SV, ProgramStateRef state, CheckerContext &C) const { const DefinedSVal *DV = dyn_cast<DefinedSVal>(&SV); if (!DV) return 0; ConstraintManager &CM = C.getConstraintManager(); - const ProgramState *stateNotNull, *stateNull; + ProgramStateRef stateNotNull, stateNull; llvm::tie(stateNotNull, stateNull) = CM.assumeDual(state, *DV); if (!stateNotNull && stateNull) { @@ -361,10 +370,11 @@ const ProgramState *StreamChecker::CheckNullStream(SVal SV, const ProgramState * return stateNotNull; } -const ProgramState *StreamChecker::CheckDoubleClose(const CallExpr *CE, - const ProgramState *state, +ProgramStateRef StreamChecker::CheckDoubleClose(const CallExpr *CE, + ProgramStateRef state, CheckerContext &C) const { - SymbolRef Sym = state->getSVal(CE->getArg(0)).getAsSymbol(); + SymbolRef Sym = + state->getSVal(CE->getArg(0), C.getLocationContext()).getAsSymbol(); if (!Sym) return state; @@ -399,7 +409,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 ProgramState *state = C.getState(); + ProgramStateRef state = C.getState(); const StreamState *SS = state->get<StreamState>(Sym); if (!SS) return; @@ -418,23 +428,22 @@ void StreamChecker::checkDeadSymbols(SymbolReaper &SymReaper, } } -void StreamChecker::checkEndPath(EndOfFunctionNodeBuilder &B, - ExprEngine &Eng) const { - const ProgramState *state = B.getState(); +void StreamChecker::checkEndPath(CheckerContext &Ctx) const { + ProgramStateRef state = Ctx.getState(); typedef llvm::ImmutableMap<SymbolRef, StreamState> SymMap; SymMap M = state->get<StreamState>(); for (SymMap::iterator I = M.begin(), E = M.end(); I != E; ++I) { StreamState SS = I->second; if (SS.isOpened()) { - ExplodedNode *N = B.generateNode(state); + ExplodedNode *N = Ctx.addTransition(state); if (N) { if (!BT_ResourceLeak) BT_ResourceLeak.reset(new BuiltinBug("Resource Leak", "Opened File never closed. Potential Resource leak.")); BugReport *R = new BugReport(*BT_ResourceLeak, BT_ResourceLeak->getDescription(), N); - Eng.getBugReporter().EmitReport(R); + Ctx.EmitReport(R); } } } @@ -445,8 +454,8 @@ void StreamChecker::checkPreStmt(const ReturnStmt *S, CheckerContext &C) const { if (!RetE) return; - const ProgramState *state = C.getState(); - SymbolRef Sym = state->getSVal(RetE).getAsSymbol(); + ProgramStateRef state = C.getState(); + SymbolRef Sym = state->getSVal(RetE, C.getLocationContext()).getAsSymbol(); if (!Sym) return; diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/TaintTesterChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/TaintTesterChecker.cpp new file mode 100644 index 0000000..1133682 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/TaintTesterChecker.cpp @@ -0,0 +1,62 @@ +//== TaintTesterChecker.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 can be used for testing how taint data is propagated. +// +//===----------------------------------------------------------------------===// +#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/BugType.h" + +using namespace clang; +using namespace ento; + +namespace { +class TaintTesterChecker : public Checker< check::PostStmt<Expr> > { + + mutable OwningPtr<BugType> BT; + void initBugType() const; + + /// Given a pointer argument, get the symbol of the value it contains + /// (points to). + SymbolRef getPointedToSymbol(CheckerContext &C, + const Expr* Arg, + bool IssueWarning = true) const; + +public: + void checkPostStmt(const Expr *E, CheckerContext &C) const; +}; +} + +inline void TaintTesterChecker::initBugType() const { + if (!BT) + BT.reset(new BugType("Tainted data", "General")); +} + +void TaintTesterChecker::checkPostStmt(const Expr *E, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + if (!State) + return; + + if (State->isTainted(E, C.getLocationContext())) { + if (ExplodedNode *N = C.addTransition()) { + initBugType(); + BugReport *report = new BugReport(*BT, "tainted",N); + report->addRange(E->getSourceRange()); + C.EmitReport(report); + } + } +} + +void ento::registerTaintTesterChecker(CheckerManager &mgr) { + mgr.registerChecker<TaintTesterChecker>(); +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp index b860b34..a30f6d5 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp @@ -24,12 +24,14 @@ using namespace ento; namespace { class UndefBranchChecker : public Checker<check::BranchCondition> { - mutable llvm::OwningPtr<BuiltinBug> BT; + mutable OwningPtr<BuiltinBug> BT; struct FindUndefExpr { - const ProgramState *St; + ProgramStateRef St; + const LocationContext *LCtx; - FindUndefExpr(const ProgramState *S) : St(S) {} + FindUndefExpr(ProgramStateRef S, const LocationContext *L) + : St(S), LCtx(L) {} const Expr *FindExpr(const Expr *Ex) { if (!MatchesCriteria(Ex)) @@ -45,25 +47,25 @@ class UndefBranchChecker : public Checker<check::BranchCondition> { return Ex; } - bool MatchesCriteria(const Expr *Ex) { return St->getSVal(Ex).isUndef(); } + bool MatchesCriteria(const Expr *Ex) { + return St->getSVal(Ex, LCtx).isUndef(); + } }; public: - void checkBranchCondition(const Stmt *Condition, BranchNodeBuilder &Builder, - ExprEngine &Eng) const; + void checkBranchCondition(const Stmt *Condition, CheckerContext &Ctx) const; }; } void UndefBranchChecker::checkBranchCondition(const Stmt *Condition, - BranchNodeBuilder &Builder, - ExprEngine &Eng) const { - const ProgramState *state = Builder.getState(); - SVal X = state->getSVal(Condition); + CheckerContext &Ctx) const { + SVal X = Ctx.getState()->getSVal(Condition, Ctx.getLocationContext()); if (X.isUndef()) { - ExplodedNode *N = Builder.generateNode(Condition, state); + // Generate a sink node, which implicitly marks both outgoing branches as + // infeasible. + ExplodedNode *N = Ctx.generateSink(); if (N) { - N->markAsSink(); if (!BT) BT.reset( new BuiltinBug("Branch condition evaluates to a garbage value")); @@ -86,25 +88,22 @@ void UndefBranchChecker::checkBranchCondition(const Stmt *Condition, const Expr *Ex = cast<Expr>(Condition); ExplodedNode *PrevN = *N->pred_begin(); ProgramPoint P = PrevN->getLocation(); - const ProgramState *St = N->getState(); + ProgramStateRef St = N->getState(); if (PostStmt *PS = dyn_cast<PostStmt>(&P)) if (PS->getStmt() == Ex) St = PrevN->getState(); - FindUndefExpr FindIt(St); + FindUndefExpr FindIt(St, Ctx.getLocationContext()); Ex = FindIt.FindExpr(Ex); // Emit the bug report. BugReport *R = new BugReport(*BT, BT->getDescription(), N); - R->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, Ex)); + R->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, Ex, R)); R->addRange(Ex->getSourceRange()); - Eng.getBugReporter().EmitReport(R); + Ctx.EmitReport(R); } - - Builder.markInfeasible(true); - Builder.markInfeasible(false); } } diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp index 2aebed9..d57767e 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp @@ -17,6 +17,7 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "llvm/ADT/SmallString.h" #include "llvm/Support/raw_ostream.h" using namespace clang; @@ -25,23 +26,23 @@ using namespace ento; namespace { class UndefCapturedBlockVarChecker : public Checker< check::PostStmt<BlockExpr> > { - mutable llvm::OwningPtr<BugType> BT; + mutable OwningPtr<BugType> BT; public: void checkPostStmt(const BlockExpr *BE, CheckerContext &C) const; }; } // end anonymous namespace -static const BlockDeclRefExpr *FindBlockDeclRefExpr(const Stmt *S, - const VarDecl *VD){ - if (const BlockDeclRefExpr *BR = dyn_cast<BlockDeclRefExpr>(S)) +static const DeclRefExpr *FindBlockDeclRefExpr(const Stmt *S, + const VarDecl *VD) { + if (const DeclRefExpr *BR = dyn_cast<DeclRefExpr>(S)) if (BR->getDecl() == VD) return BR; for (Stmt::const_child_iterator I = S->child_begin(), E = S->child_end(); I!=E; ++I) if (const Stmt *child = *I) { - const BlockDeclRefExpr *BR = FindBlockDeclRefExpr(child, VD); + const DeclRefExpr *BR = FindBlockDeclRefExpr(child, VD); if (BR) return BR; } @@ -55,9 +56,10 @@ UndefCapturedBlockVarChecker::checkPostStmt(const BlockExpr *BE, if (!BE->getBlockDecl()->hasCaptures()) return; - const ProgramState *state = C.getState(); + ProgramStateRef state = C.getState(); const BlockDataRegion *R = - cast<BlockDataRegion>(state->getSVal(BE).getAsRegion()); + cast<BlockDataRegion>(state->getSVal(BE, + C.getLocationContext()).getAsRegion()); BlockDataRegion::referenced_vars_iterator I = R->referenced_vars_begin(), E = R->referenced_vars_end(); @@ -72,7 +74,7 @@ UndefCapturedBlockVarChecker::checkPostStmt(const BlockExpr *BE, continue; // Get the VarRegion associated with VD in the local stack frame. - const LocationContext *LC = C.getPredecessor()->getLocationContext(); + const LocationContext *LC = C.getLocationContext(); VR = C.getSValBuilder().getRegionManager().getVarRegion(VD, LC); SVal VRVal = state->getSVal(VR); @@ -82,7 +84,7 @@ UndefCapturedBlockVarChecker::checkPostStmt(const BlockExpr *BE, BT.reset(new BuiltinBug("uninitialized variable captured by block")); // Generate a bug report. - llvm::SmallString<128> buf; + SmallString<128> buf; llvm::raw_svector_ostream os(buf); os << "Variable '" << VD->getName() diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp index 7ae9668..c3c9ed7 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp @@ -18,6 +18,7 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" +#include "llvm/ADT/SmallString.h" using namespace clang; using namespace ento; @@ -26,7 +27,7 @@ namespace { class UndefResultChecker : public Checker< check::PostStmt<BinaryOperator> > { - mutable llvm::OwningPtr<BugType> BT; + mutable OwningPtr<BugType> BT; public: void checkPostStmt(const BinaryOperator *B, CheckerContext &C) const; @@ -35,8 +36,9 @@ public: void UndefResultChecker::checkPostStmt(const BinaryOperator *B, CheckerContext &C) const { - const ProgramState *state = C.getState(); - if (state->getSVal(B).isUndef()) { + ProgramStateRef state = C.getState(); + const LocationContext *LCtx = C.getLocationContext(); + if (state->getSVal(B, LCtx).isUndef()) { // Generate an error node. ExplodedNode *N = C.generateSink(); if (!N) @@ -45,16 +47,16 @@ void UndefResultChecker::checkPostStmt(const BinaryOperator *B, if (!BT) BT.reset(new BuiltinBug("Result of operation is garbage or undefined")); - llvm::SmallString<256> sbuf; + SmallString<256> sbuf; llvm::raw_svector_ostream OS(sbuf); const Expr *Ex = NULL; bool isLeft = true; - if (state->getSVal(B->getLHS()).isUndef()) { + if (state->getSVal(B->getLHS(), LCtx).isUndef()) { Ex = B->getLHS()->IgnoreParenCasts(); isLeft = true; } - else if (state->getSVal(B->getRHS()).isUndef()) { + else if (state->getSVal(B->getRHS(), LCtx).isUndef()) { Ex = B->getRHS()->IgnoreParenCasts(); isLeft = false; } @@ -74,10 +76,12 @@ void UndefResultChecker::checkPostStmt(const BinaryOperator *B, BugReport *report = new BugReport(*BT, OS.str(), N); if (Ex) { report->addRange(Ex->getSourceRange()); - report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, Ex)); + report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, Ex, + report)); } else - report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, B)); + report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, B, + report)); C.EmitReport(report); } } diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UndefinedArraySubscriptChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UndefinedArraySubscriptChecker.cpp index bb6831b..0297c4e 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UndefinedArraySubscriptChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UndefinedArraySubscriptChecker.cpp @@ -24,7 +24,7 @@ using namespace ento; namespace { class UndefinedArraySubscriptChecker : public Checker< check::PreStmt<ArraySubscriptExpr> > { - mutable llvm::OwningPtr<BugType> BT; + mutable OwningPtr<BugType> BT; public: void checkPreStmt(const ArraySubscriptExpr *A, CheckerContext &C) const; @@ -34,7 +34,7 @@ public: void UndefinedArraySubscriptChecker::checkPreStmt(const ArraySubscriptExpr *A, CheckerContext &C) const { - if (C.getState()->getSVal(A->getIdx()).isUndef()) { + if (C.getState()->getSVal(A->getIdx(), C.getLocationContext()).isUndef()) { if (ExplodedNode *N = C.generateSink()) { if (!BT) BT.reset(new BuiltinBug("Array subscript is undefined")); @@ -43,7 +43,8 @@ UndefinedArraySubscriptChecker::checkPreStmt(const ArraySubscriptExpr *A, BugReport *R = new BugReport(*BT, BT->getName(), N); R->addRange(A->getIdx()->getSourceRange()); R->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, - A->getIdx())); + A->getIdx(), + R)); C.EmitReport(R); } } diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp index 5ca4a9f..78f7fa6 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp @@ -24,7 +24,7 @@ using namespace ento; namespace { class UndefinedAssignmentChecker : public Checker<check::Bind> { - mutable llvm::OwningPtr<BugType> BT; + mutable OwningPtr<BugType> BT; public: void checkBind(SVal location, SVal val, const Stmt *S, @@ -54,8 +54,8 @@ void UndefinedAssignmentChecker::checkBind(SVal location, SVal val, while (StoreE) { if (const BinaryOperator *B = dyn_cast<BinaryOperator>(StoreE)) { if (B->isCompoundAssignmentOp()) { - const ProgramState *state = C.getState(); - if (state->getSVal(B->getLHS()).isUndef()) { + ProgramStateRef state = C.getState(); + if (state->getSVal(B->getLHS(), C.getLocationContext()).isUndef()) { str = "The left expression of the compound assignment is an " "uninitialized value. The computed value will also be garbage"; ex = B->getLHS(); @@ -78,7 +78,7 @@ void UndefinedAssignmentChecker::checkBind(SVal location, SVal val, BugReport *R = new BugReport(*BT, str, N); if (ex) { R->addRange(ex->getSourceRange()); - R->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, ex)); + R->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, ex, R)); } C.EmitReport(R); } diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp index cec286d..60e665fe 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp @@ -19,6 +19,8 @@ #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/Basic/TargetInfo.h" #include "llvm/ADT/Optional.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringSwitch.h" #include <fcntl.h> @@ -28,7 +30,7 @@ using llvm::Optional; namespace { class UnixAPIChecker : public Checker< check::PreStmt<CallExpr> > { - mutable llvm::OwningPtr<BugType> BT_open, BT_pthreadOnce, BT_mallocZero; + mutable OwningPtr<BugType> BT_open, BT_pthreadOnce, BT_mallocZero; mutable Optional<uint64_t> Val_O_CREAT; public: @@ -36,10 +38,24 @@ public: void CheckOpen(CheckerContext &C, const CallExpr *CE) const; void CheckPthreadOnce(CheckerContext &C, const CallExpr *CE) const; + void CheckCallocZero(CheckerContext &C, const CallExpr *CE) const; void CheckMallocZero(CheckerContext &C, const CallExpr *CE) const; + void CheckReallocZero(CheckerContext &C, const CallExpr *CE) const; + void CheckAllocaZero(CheckerContext &C, const CallExpr *CE) const; + void CheckVallocZero(CheckerContext &C, const CallExpr *CE) const; typedef void (UnixAPIChecker::*SubChecker)(CheckerContext &, const CallExpr *) const; +private: + bool ReportZeroByteAllocation(CheckerContext &C, + ProgramStateRef falseState, + const Expr *arg, + const char *fn_name) const; + void BasicAllocationCheck(CheckerContext &C, + const CallExpr *CE, + const unsigned numArgs, + const unsigned sizeArg, + const char *fn) const; }; } //end anonymous namespace @@ -47,11 +63,11 @@ public: // Utility functions. //===----------------------------------------------------------------------===// -static inline void LazyInitialize(llvm::OwningPtr<BugType> &BT, +static inline void LazyInitialize(OwningPtr<BugType> &BT, const char *name) { if (BT) return; - BT.reset(new BugType(name, "Unix API")); + BT.reset(new BugType(name, categories::UnixAPI)); } //===----------------------------------------------------------------------===// @@ -74,7 +90,7 @@ void UnixAPIChecker::CheckOpen(CheckerContext &C, const CallExpr *CE) const { } // Look at the 'oflags' argument for the O_CREAT flag. - const ProgramState *state = C.getState(); + ProgramStateRef state = C.getState(); if (CE->getNumArgs() < 2) { // The frontend should issue a warning for this case, so this is a sanity @@ -84,7 +100,7 @@ void UnixAPIChecker::CheckOpen(CheckerContext &C, const CallExpr *CE) const { // Now check if oflags has O_CREAT set. const Expr *oflagsEx = CE->getArg(1); - const SVal V = state->getSVal(oflagsEx); + const SVal V = state->getSVal(oflagsEx, C.getLocationContext()); if (!isa<NonLoc>(V)) { // The case where 'V' can be a location can only be due to a bad header, // so in this case bail out. @@ -102,7 +118,7 @@ void UnixAPIChecker::CheckOpen(CheckerContext &C, const CallExpr *CE) const { DefinedSVal maskedFlags = cast<DefinedSVal>(maskedFlagsUC); // Check if maskedFlags is non-zero. - const ProgramState *trueState, *falseState; + ProgramStateRef trueState, falseState; llvm::tie(trueState, falseState) = state->assume(maskedFlags); // Only emit an error if the value of 'maskedFlags' is properly @@ -141,8 +157,9 @@ 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 ProgramState *state = C.getState(); - const MemRegion *R = state->getSVal(CE->getArg(0)).getAsRegion(); + ProgramStateRef state = C.getState(); + const MemRegion *R = + state->getSVal(CE->getArg(0), C.getLocationContext()).getAsRegion(); if (!R || !isa<StackSpaceRegion>(R->getMemorySpace())) return; @@ -150,7 +167,7 @@ void UnixAPIChecker::CheckPthreadOnce(CheckerContext &C, if (!N) return; - llvm::SmallString<256> S; + SmallString<256> S; llvm::raw_svector_ostream os(S); os << "Call to 'pthread_once' uses"; if (const VarRegion *VR = dyn_cast<VarRegion>(R)) @@ -170,78 +187,157 @@ void UnixAPIChecker::CheckPthreadOnce(CheckerContext &C, } //===----------------------------------------------------------------------===// -// "malloc" with allocation size 0 +// "calloc", "malloc", "realloc", "alloca" and "valloc" with allocation size 0 //===----------------------------------------------------------------------===// +// FIXME: Eventually these should be rolled into the MallocChecker, but right now +// they're more basic and valuable for widespread use. + +// Returns true if we try to do a zero byte allocation, false otherwise. +// Fills in trueState and falseState. +static bool IsZeroByteAllocation(ProgramStateRef state, + const SVal argVal, + ProgramStateRef *trueState, + ProgramStateRef *falseState) { + llvm::tie(*trueState, *falseState) = + state->assume(cast<DefinedSVal>(argVal)); + + return (*falseState && !*trueState); +} -// FIXME: Eventually this should be rolled into the MallocChecker, but this -// check is more basic and is valuable for widespread use. -void UnixAPIChecker::CheckMallocZero(CheckerContext &C, - const CallExpr *CE) const { +// Generates an error report, indicating that the function whose name is given +// will perform a zero byte allocation. +// Returns false if an error occured, true otherwise. +bool UnixAPIChecker::ReportZeroByteAllocation(CheckerContext &C, + ProgramStateRef falseState, + const Expr *arg, + const char *fn_name) const { + ExplodedNode *N = C.generateSink(falseState); + if (!N) + return false; + + LazyInitialize(BT_mallocZero, + "Undefined allocation of 0 bytes (CERT MEM04-C; CWE-131)"); + + SmallString<256> S; + llvm::raw_svector_ostream os(S); + os << "Call to '" << fn_name << "' has an allocation size of 0 bytes"; + BugReport *report = new BugReport(*BT_mallocZero, os.str(), N); - // Sanity check that malloc takes one argument. - if (CE->getNumArgs() != 1) + report->addRange(arg->getSourceRange()); + report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, arg, + report)); + C.EmitReport(report); + + return true; +} + +// Does a basic check for 0-sized allocations suitable for most of the below +// functions (modulo "calloc") +void UnixAPIChecker::BasicAllocationCheck(CheckerContext &C, + const CallExpr *CE, + const unsigned numArgs, + const unsigned sizeArg, + const char *fn) const { + // Sanity check for the correct number of arguments + if (CE->getNumArgs() != numArgs) return; // Check if the allocation size is 0. - const ProgramState *state = C.getState(); - SVal argVal = state->getSVal(CE->getArg(0)); + ProgramStateRef state = C.getState(); + ProgramStateRef trueState = NULL, falseState = NULL; + const Expr *arg = CE->getArg(sizeArg); + SVal argVal = state->getSVal(arg, C.getLocationContext()); if (argVal.isUnknownOrUndef()) return; - - const ProgramState *trueState, *falseState; - llvm::tie(trueState, falseState) = state->assume(cast<DefinedSVal>(argVal)); - + // Is the value perfectly constrained to zero? - if (falseState && !trueState) { - ExplodedNode *N = C.generateSink(falseState); - if (!N) - return; - - // FIXME: Add reference to CERT advisory, and/or C99 standard in bug - // output. + if (IsZeroByteAllocation(state, argVal, &trueState, &falseState)) { + (void) ReportZeroByteAllocation(C, falseState, arg, fn); + return; + } + // Assume the the value is non-zero going forward. + assert(trueState); + if (trueState != state) + C.addTransition(trueState); +} - LazyInitialize(BT_mallocZero, "Undefined allocation of 0 bytes"); - - BugReport *report = - new BugReport(*BT_mallocZero, "Call to 'malloc' has an allocation" - " size of 0 bytes", N); - report->addRange(CE->getArg(0)->getSourceRange()); - report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, - CE->getArg(0))); - C.EmitReport(report); +void UnixAPIChecker::CheckCallocZero(CheckerContext &C, + const CallExpr *CE) const { + unsigned int nArgs = CE->getNumArgs(); + if (nArgs != 2) return; + + ProgramStateRef state = C.getState(); + ProgramStateRef trueState = NULL, falseState = NULL; + + unsigned int i; + for (i = 0; i < nArgs; i++) { + const Expr *arg = CE->getArg(i); + SVal argVal = state->getSVal(arg, C.getLocationContext()); + if (argVal.isUnknownOrUndef()) { + if (i == 0) + continue; + else + return; + } + + if (IsZeroByteAllocation(state, argVal, &trueState, &falseState)) { + if (ReportZeroByteAllocation(C, falseState, arg, "calloc")) + return; + else if (i == 0) + continue; + else + return; + } } + // Assume the the value is non-zero going forward. assert(trueState); - if (trueState != state) { + if (trueState != state) C.addTransition(trueState); - } } - + +void UnixAPIChecker::CheckMallocZero(CheckerContext &C, + const CallExpr *CE) const { + BasicAllocationCheck(C, CE, 1, 0, "malloc"); +} + +void UnixAPIChecker::CheckReallocZero(CheckerContext &C, + const CallExpr *CE) const { + BasicAllocationCheck(C, CE, 2, 1, "realloc"); +} + +void UnixAPIChecker::CheckAllocaZero(CheckerContext &C, + const CallExpr *CE) const { + BasicAllocationCheck(C, CE, 1, 0, "alloca"); +} + +void UnixAPIChecker::CheckVallocZero(CheckerContext &C, + const CallExpr *CE) const { + BasicAllocationCheck(C, CE, 1, 0, "valloc"); +} + + //===----------------------------------------------------------------------===// // Central dispatch function. //===----------------------------------------------------------------------===// -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 ProgramState *state = C.getState(); - const Expr *Callee = CE->getCallee(); - const FunctionDecl *Fn = state->getSVal(Callee).getAsFunctionDecl(); - - if (!Fn) - return; - - const IdentifierInfo *FI = Fn->getIdentifier(); - if (!FI) +void UnixAPIChecker::checkPreStmt(const CallExpr *CE, + CheckerContext &C) const { + StringRef FName = C.getCalleeName(CE); + if (FName.empty()) return; SubChecker SC = - llvm::StringSwitch<SubChecker>(FI->getName()) + llvm::StringSwitch<SubChecker>(FName) .Case("open", &UnixAPIChecker::CheckOpen) .Case("pthread_once", &UnixAPIChecker::CheckPthreadOnce) + .Case("calloc", &UnixAPIChecker::CheckCallocZero) .Case("malloc", &UnixAPIChecker::CheckMallocZero) + .Case("realloc", &UnixAPIChecker::CheckReallocZero) + .Cases("alloca", "__builtin_alloca", &UnixAPIChecker::CheckAllocaZero) + .Case("valloc", &UnixAPIChecker::CheckVallocZero) .Default(NULL); if (SC) diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp index 459ee65..5a13ed0 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp @@ -24,7 +24,7 @@ #include "clang/AST/ParentMap.h" #include "clang/Basic/Builtins.h" #include "clang/Basic/SourceManager.h" -#include "llvm/ADT/SmallPtrSet.h" +#include "llvm/ADT/SmallSet.h" // The number of CFGBlock pointers we want to reserve memory for. This is used // once for each function we analyze. @@ -54,10 +54,11 @@ void UnreachableCodeChecker::checkEndAnalysis(ExplodedGraph &G, BugReporter &B, ExprEngine &Eng) const { CFGBlocksSet reachable, visited; - + if (Eng.hasWorkRemaining()) return; + const Decl *D = 0; CFG *C = 0; ParentMap *PM = 0; const LocationContext *LC = 0; @@ -67,9 +68,11 @@ void UnreachableCodeChecker::checkEndAnalysis(ExplodedGraph &G, const ProgramPoint &P = I->getLocation(); LC = P.getLocationContext(); + if (!D) + D = LC->getAnalysisDeclContext()->getDecl(); // Save the CFG if we don't have it already if (!C) - C = LC->getAnalysisContext()->getUnoptimizedCFG(); + C = LC->getAnalysisDeclContext()->getUnoptimizedCFG(); if (!PM) PM = &LC->getParentMap(); @@ -80,10 +83,15 @@ void UnreachableCodeChecker::checkEndAnalysis(ExplodedGraph &G, } // Bail out if we didn't get the CFG or the ParentMap. - if (!C || !PM) + if (!D || !C || !PM) return; - - ASTContext &Ctx = B.getContext(); + + // Don't do anything for template instantiations. Proving that code + // in a template instantiation is unreachable means proving that it is + // unreachable in all instantiations. + if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) + if (FD->isTemplateInstantiation()) + return; // Find CFGBlocks that were not covered by any node for (CFG::const_iterator I = C->begin(), E = C->end(); I != E; ++I) { @@ -108,6 +116,14 @@ void UnreachableCodeChecker::checkEndAnalysis(ExplodedGraph &G, if (CB->size() > 0 && isInvalidPath(CB, *PM)) continue; + // It is good practice to always have a "default" label in a "switch", even + // if we should never get there. It can be used to detect errors, for + // instance. Unreachable code directly under a "default" label is therefore + // likely to be a false positive. + if (const Stmt *label = CB->getLabel()) + if (label->getStmtClass() == Stmt::DefaultStmtClass) + continue; + // Special case for __builtin_unreachable. // FIXME: This should be extended to include other unreachable markers, // such as llvm_unreachable. @@ -117,7 +133,7 @@ void UnreachableCodeChecker::checkEndAnalysis(ExplodedGraph &G, 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) { + if (CE->isBuiltinCall() == Builtin::BI__builtin_unreachable) { foundUnreachable = true; break; } @@ -146,8 +162,8 @@ void UnreachableCodeChecker::checkEndAnalysis(ExplodedGraph &G, if (SM.isInSystemHeader(SL) || SM.isInExternCSystemHeader(SL)) continue; - B.EmitBasicReport("Unreachable code", "Dead code", "This statement is never" - " executed", DL, SR); + B.EmitBasicReport(D, "Unreachable code", "Dead code", + "This statement is never executed", DL, SR); } } diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp index b34b97c..38c9cc1 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp @@ -20,20 +20,61 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/AST/CharUnits.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/STLExtras.h" using namespace clang; using namespace ento; namespace { class VLASizeChecker : public Checker< check::PreStmt<DeclStmt> > { - mutable llvm::OwningPtr<BugType> BT_zero; - mutable llvm::OwningPtr<BugType> BT_undef; - + mutable OwningPtr<BugType> BT; + enum VLASize_Kind { VLA_Garbage, VLA_Zero, VLA_Tainted }; + + void reportBug(VLASize_Kind Kind, + const Expr *SizeE, + ProgramStateRef State, + CheckerContext &C) const; public: void checkPreStmt(const DeclStmt *DS, CheckerContext &C) const; }; } // end anonymous namespace +void VLASizeChecker::reportBug(VLASize_Kind Kind, + const Expr *SizeE, + ProgramStateRef State, + CheckerContext &C) const { + // Generate an error node. + ExplodedNode *N = C.generateSink(State); + if (!N) + return; + + if (!BT) + BT.reset(new BuiltinBug("Dangerous variable-length array (VLA) declaration")); + + SmallString<256> buf; + llvm::raw_svector_ostream os(buf); + os << "Declared variable-length array (VLA) "; + switch (Kind) { + case VLA_Garbage: + os << "uses a garbage value as its size"; + break; + case VLA_Zero: + os << "has zero size"; + break; + case VLA_Tainted: + os << "has tainted size"; + break; + } + + BugReport *report = new BugReport(*BT, os.str(), N); + report->addRange(SizeE->getSourceRange()); + report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, SizeE, + report)); + C.EmitReport(report); + return; +} + void VLASizeChecker::checkPreStmt(const DeclStmt *DS, CheckerContext &C) const { if (!DS->isSingleDecl()) return; @@ -49,24 +90,11 @@ void VLASizeChecker::checkPreStmt(const DeclStmt *DS, CheckerContext &C) const { // FIXME: Handle multi-dimensional VLAs. const Expr *SE = VLA->getSizeExpr(); - const ProgramState *state = C.getState(); - SVal sizeV = state->getSVal(SE); + ProgramStateRef state = C.getState(); + SVal sizeV = state->getSVal(SE, C.getLocationContext()); if (sizeV.isUndef()) { - // Generate an error node. - ExplodedNode *N = C.generateSink(); - if (!N) - return; - - if (!BT_undef) - BT_undef.reset(new BuiltinBug("Declared variable-length array (VLA) " - "uses a garbage value as its size")); - - BugReport *report = - new BugReport(*BT_undef, BT_undef->getName(), N); - report->addRange(SE->getSourceRange()); - report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, SE)); - C.EmitReport(report); + reportBug(VLA_Garbage, SE, state, C); return; } @@ -75,23 +103,20 @@ void VLASizeChecker::checkPreStmt(const DeclStmt *DS, CheckerContext &C) const { if (sizeV.isUnknown()) return; + // Check if the size is tainted. + if (state->isTainted(sizeV)) { + reportBug(VLA_Tainted, SE, 0, C); + return; + } + // Check if the size is zero. DefinedSVal sizeD = cast<DefinedSVal>(sizeV); - const ProgramState *stateNotZero, *stateZero; + ProgramStateRef stateNotZero, stateZero; llvm::tie(stateNotZero, stateZero) = state->assume(sizeD); if (stateZero && !stateNotZero) { - ExplodedNode *N = C.generateSink(stateZero); - if (!BT_zero) - BT_zero.reset(new BuiltinBug("Declared variable-length array (VLA) has " - "zero size")); - - BugReport *report = - new BugReport(*BT_zero, BT_zero->getName(), N); - report->addRange(SE->getSourceRange()); - report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, SE)); - C.EmitReport(report); + reportBug(VLA_Zero, SE, stateZero, C); return; } @@ -117,7 +142,7 @@ void VLASizeChecker::checkPreStmt(const DeclStmt *DS, CheckerContext &C) const { cast<NonLoc>(EleSizeVal), SizeTy); // Finally, assume that the array's extent matches the given size. - const LocationContext *LC = C.getPredecessor()->getLocationContext(); + const LocationContext *LC = C.getLocationContext(); DefinedOrUnknownSVal Extent = state->getRegion(VD, LC)->getExtent(svalBuilder); DefinedOrUnknownSVal ArraySize = cast<DefinedOrUnknownSVal>(ArraySizeVal); diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp new file mode 100644 index 0000000..f7c7c0c --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp @@ -0,0 +1,241 @@ +//=======- VirtualCallChecker.cpp --------------------------------*- 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 a checker that checks virtual function calls during +// construction or destruction of C++ objects. +// +//===----------------------------------------------------------------------===// + +#include "ClangSACheckers.h" +#include "clang/AST/DeclCXX.h" +#include "clang/AST/StmtVisitor.h" +#include "llvm/Support/SaveAndRestore.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" +#include "llvm/ADT/SmallString.h" + +using namespace clang; +using namespace ento; + +namespace { + +class WalkAST : public StmtVisitor<WalkAST> { + BugReporter &BR; + AnalysisDeclContext *AC; + + typedef const CallExpr * WorkListUnit; + typedef SmallVector<WorkListUnit, 20> DFSWorkList; + + /// A vector representing the worklist which has a chain of CallExprs. + DFSWorkList WList; + + // PreVisited : A CallExpr to this FunctionDecl is in the worklist, but the + // body has not been visited yet. + // PostVisited : A CallExpr to this FunctionDecl is in the worklist, and the + // body has been visited. + enum Kind { NotVisited, + PreVisited, /**< A CallExpr to this FunctionDecl is in the + worklist, but the body has not yet been + visited. */ + PostVisited /**< A CallExpr to this FunctionDecl is in the + worklist, and the body has been visited. */ + } K; + + /// A DenseMap that records visited states of FunctionDecls. + llvm::DenseMap<const FunctionDecl *, Kind> VisitedFunctions; + + /// The CallExpr whose body is currently being visited. This is used for + /// generating bug reports. This is null while visiting the body of a + /// constructor or destructor. + const CallExpr *visitingCallExpr; + +public: + WalkAST(BugReporter &br, AnalysisDeclContext *ac) + : BR(br), + AC(ac), + visitingCallExpr(0) {} + + bool hasWork() const { return !WList.empty(); } + + /// This method adds a CallExpr to the worklist and marks the callee as + /// being PreVisited. + void Enqueue(WorkListUnit WLUnit) { + const FunctionDecl *FD = WLUnit->getDirectCallee(); + if (!FD || !FD->getBody()) + return; + Kind &K = VisitedFunctions[FD]; + if (K != NotVisited) + return; + K = PreVisited; + WList.push_back(WLUnit); + } + + /// This method returns an item from the worklist without removing it. + WorkListUnit Dequeue() { + assert(!WList.empty()); + return WList.back(); + } + + void Execute() { + while (hasWork()) { + WorkListUnit WLUnit = Dequeue(); + const FunctionDecl *FD = WLUnit->getDirectCallee(); + assert(FD && FD->getBody()); + + if (VisitedFunctions[FD] == PreVisited) { + // If the callee is PreVisited, walk its body. + // Visit the body. + SaveAndRestore<const CallExpr *> SaveCall(visitingCallExpr, WLUnit); + Visit(FD->getBody()); + + // Mark the function as being PostVisited to indicate we have + // scanned the body. + VisitedFunctions[FD] = PostVisited; + continue; + } + + // Otherwise, the callee is PostVisited. + // Remove it from the worklist. + assert(VisitedFunctions[FD] == PostVisited); + WList.pop_back(); + } + } + + // Stmt visitor methods. + void VisitCallExpr(CallExpr *CE); + void VisitCXXMemberCallExpr(CallExpr *CE); + void VisitStmt(Stmt *S) { VisitChildren(S); } + void VisitChildren(Stmt *S); + + void ReportVirtualCall(const CallExpr *CE, bool isPure); + +}; +} // end anonymous namespace + +//===----------------------------------------------------------------------===// +// AST walking. +//===----------------------------------------------------------------------===// + +void WalkAST::VisitChildren(Stmt *S) { + for (Stmt::child_iterator I = S->child_begin(), E = S->child_end(); I!=E; ++I) + if (Stmt *child = *I) + Visit(child); +} + +void WalkAST::VisitCallExpr(CallExpr *CE) { + VisitChildren(CE); + Enqueue(CE); +} + +void WalkAST::VisitCXXMemberCallExpr(CallExpr *CE) { + VisitChildren(CE); + bool callIsNonVirtual = false; + + // Several situations to elide for checking. + if (MemberExpr *CME = dyn_cast<MemberExpr>(CE->getCallee())) { + // If the member access is fully qualified (i.e., X::F), then treat + // this as a non-virtual call and do not warn. + if (CME->getQualifier()) + callIsNonVirtual = true; + + // Elide analyzing the call entirely if the base pointer is not 'this'. + if (Expr *base = CME->getBase()->IgnoreImpCasts()) + if (!isa<CXXThisExpr>(base)) + return; + } + + // Get the callee. + const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(CE->getDirectCallee()); + if (MD && MD->isVirtual() && !callIsNonVirtual) + ReportVirtualCall(CE, MD->isPure()); + + Enqueue(CE); +} + +void WalkAST::ReportVirtualCall(const CallExpr *CE, bool isPure) { + SmallString<100> buf; + llvm::raw_svector_ostream os(buf); + + os << "Call Path : "; + // Name of current visiting CallExpr. + os << *CE->getDirectCallee(); + + // Name of the CallExpr whose body is current walking. + if (visitingCallExpr) + os << " <-- " << *visitingCallExpr->getDirectCallee(); + // Names of FunctionDecls in worklist with state PostVisited. + for (SmallVectorImpl<const CallExpr *>::iterator I = WList.end(), + E = WList.begin(); I != E; --I) { + const FunctionDecl *FD = (*(I-1))->getDirectCallee(); + assert(FD); + if (VisitedFunctions[FD] == PostVisited) + os << " <-- " << *FD; + } + + PathDiagnosticLocation CELoc = + PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); + SourceRange R = CE->getCallee()->getSourceRange(); + + if (isPure) { + os << "\n" << "Call pure virtual functions during construction or " + << "destruction may leads undefined behaviour"; + BR.EmitBasicReport(AC->getDecl(), + "Call pure virtual function during construction or " + "Destruction", + "Cplusplus", + os.str(), CELoc, &R, 1); + return; + } + else { + os << "\n" << "Call virtual functions during construction or " + << "destruction will never go to a more derived class"; + BR.EmitBasicReport(AC->getDecl(), + "Call virtual function during construction or " + "Destruction", + "Cplusplus", + os.str(), CELoc, &R, 1); + return; + } +} + +//===----------------------------------------------------------------------===// +// VirtualCallChecker +//===----------------------------------------------------------------------===// + +namespace { +class VirtualCallChecker : public Checker<check::ASTDecl<CXXRecordDecl> > { +public: + void checkASTDecl(const CXXRecordDecl *RD, AnalysisManager& mgr, + BugReporter &BR) const { + WalkAST walker(BR, mgr.getAnalysisDeclContext(RD)); + + // Check the constructors. + for (CXXRecordDecl::ctor_iterator I = RD->ctor_begin(), E = RD->ctor_end(); + I != E; ++I) { + if (!I->isCopyOrMoveConstructor()) + if (Stmt *Body = I->getBody()) { + walker.Visit(Body); + walker.Execute(); + } + } + + // Check the destructor. + if (CXXDestructorDecl *DD = RD->getDestructor()) + if (Stmt *Body = DD->getBody()) { + walker.Visit(Body); + walker.Execute(); + } + } +}; +} + +void ento::registerVirtualCallChecker(CheckerManager &mgr) { + mgr.registerChecker<VirtualCallChecker>(); +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/AggExprVisitor.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/AggExprVisitor.cpp deleted file mode 100644 index 0936d61..0000000 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/AggExprVisitor.cpp +++ /dev/null @@ -1,69 +0,0 @@ -//=-- AggExprVisitor.cpp - evaluating expressions of C++ class type -*- 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 AggExprVisitor class, which contains lots of boiler -// plate code for evaluating expressions of C++ class type. -// -//===----------------------------------------------------------------------===// - -#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" -#include "clang/AST/StmtVisitor.h" - -using namespace clang; -using namespace ento; - -namespace { -/// AggExprVisitor is designed after AggExprEmitter of the CodeGen module. It -/// is used for evaluating exprs of C++ object type. Evaluating such exprs -/// requires a destination pointer pointing to the object being evaluated -/// into. Passing such a pointer around would pollute the Visit* interface of -/// ExprEngine. AggExprVisitor encapsulates code that goes through various -/// cast and construct exprs (and others), and at the final point, dispatches -/// back to the ExprEngine to let the real evaluation logic happen. -class AggExprVisitor : public StmtVisitor<AggExprVisitor> { - const MemRegion *Dest; - ExplodedNode *Pred; - ExplodedNodeSet &DstSet; - ExprEngine &Eng; - -public: - AggExprVisitor(const MemRegion *dest, ExplodedNode *N, ExplodedNodeSet &dst, - ExprEngine &eng) - : Dest(dest), Pred(N), DstSet(dst), Eng(eng) {} - - void VisitCastExpr(CastExpr *E); - void VisitCXXConstructExpr(CXXConstructExpr *E); - void VisitCXXMemberCallExpr(CXXMemberCallExpr *E); -}; -} - -void AggExprVisitor::VisitCastExpr(CastExpr *E) { - switch (E->getCastKind()) { - default: - llvm_unreachable("Unhandled cast kind"); - case CK_NoOp: - case CK_ConstructorConversion: - case CK_UserDefinedConversion: - Visit(E->getSubExpr()); - break; - } -} - -void AggExprVisitor::VisitCXXConstructExpr(CXXConstructExpr *E) { - Eng.VisitCXXConstructExpr(E, Dest, Pred, DstSet); -} - -void AggExprVisitor::VisitCXXMemberCallExpr(CXXMemberCallExpr *E) { - Eng.Visit(E, Pred, DstSet); -} - -void ExprEngine::VisitAggExpr(const Expr *E, const MemRegion *Dest, - ExplodedNode *Pred, ExplodedNodeSet &Dst) { - AggExprVisitor(Dest, Pred, Dst, *this).Visit(const_cast<Expr *>(E)); -} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/AnalysisManager.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/AnalysisManager.cpp index 17ec70d..82ac8bd 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/AnalysisManager.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/AnalysisManager.cpp @@ -14,6 +14,8 @@ using namespace clang; using namespace ento; +void AnalysisManager::anchor() { } + AnalysisManager::AnalysisManager(ASTContext &ctx, DiagnosticsEngine &diags, const LangOptions &lang, PathDiagnosticConsumer *pd, @@ -25,17 +27,27 @@ AnalysisManager::AnalysisManager(ASTContext &ctx, DiagnosticsEngine &diags, bool vizdot, bool vizubi, AnalysisPurgeMode purge, bool eager, bool trim, - bool inlinecall, bool useUnoptimizedCFG, + bool useUnoptimizedCFG, bool addImplicitDtors, bool addInitializers, - bool eagerlyTrimEGraph) + bool eagerlyTrimEGraph, + AnalysisIPAMode ipa, + unsigned inlineMaxStack, + unsigned inlineMaxFunctionSize, + AnalysisInliningMode IMode, + bool NoRetry) : AnaCtxMgr(useUnoptimizedCFG, addImplicitDtors, addInitializers), - Ctx(ctx), Diags(diags), LangInfo(lang), PD(pd), + Ctx(ctx), Diags(diags), LangOpts(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) + EagerlyAssume(eager), TrimGraph(trim), + EagerlyTrimEGraph(eagerlyTrimEGraph), + IPAMode(ipa), + InlineMaxStackDepth(inlineMaxStack), + InlineMaxFunctionSize(inlineMaxFunctionSize), + InliningMode(IMode), + NoRetryExhausted(NoRetry) { AnaCtxMgr.getCFGBuildOptions().setAllAlwaysAdd(); } @@ -46,7 +58,7 @@ AnalysisManager::AnalysisManager(ASTContext &ctx, DiagnosticsEngine &diags, ParentAM.AnaCtxMgr.getCFGBuildOptions().AddImplicitDtors, ParentAM.AnaCtxMgr.getCFGBuildOptions().AddInitializers), Ctx(ctx), Diags(diags), - LangInfo(ParentAM.LangInfo), PD(ParentAM.getPathDiagnosticConsumer()), + LangOpts(ParentAM.LangOpts), PD(ParentAM.getPathDiagnosticConsumer()), CreateStoreMgr(ParentAM.CreateStoreMgr), CreateConstraintMgr(ParentAM.CreateConstraintMgr), CheckerMgr(ParentAM.CheckerMgr), @@ -59,15 +71,19 @@ AnalysisManager::AnalysisManager(ASTContext &ctx, DiagnosticsEngine &diags, PurgeDead(ParentAM.PurgeDead), EagerlyAssume(ParentAM.EagerlyAssume), TrimGraph(ParentAM.TrimGraph), - InlineCall(ParentAM.InlineCall), - EagerlyTrimEGraph(ParentAM.EagerlyTrimEGraph) + EagerlyTrimEGraph(ParentAM.EagerlyTrimEGraph), + IPAMode(ParentAM.IPAMode), + InlineMaxStackDepth(ParentAM.InlineMaxStackDepth), + InlineMaxFunctionSize(ParentAM.InlineMaxFunctionSize), + InliningMode(ParentAM.InliningMode), + NoRetryExhausted(ParentAM.NoRetryExhausted) { AnaCtxMgr.getCFGBuildOptions().setAllAlwaysAdd(); } -AnalysisContext * -AnalysisManager::getAnalysisContextInAnotherTU(const Decl *D) { +AnalysisDeclContext * +AnalysisManager::getAnalysisDeclContextInAnotherTU(const Decl *D) { idx::Entity Ent = idx::Entity::get(const_cast<Decl *>(D), Idxer->getProgram()); FunctionDecl *FuncDef; @@ -77,7 +93,7 @@ AnalysisManager::getAnalysisContextInAnotherTU(const Decl *D) { if (FuncDef == 0) return 0; - // This AnalysisContext wraps function definition in another translation unit. + // This AnalysisDeclContext wraps function definition in another translation unit. // But it is still owned by the AnalysisManager associated with the current // translation unit. return AnaCtxMgr.getContext(FuncDef, TU); diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/BasicConstraintManager.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/BasicConstraintManager.cpp index 6c748b6..2d9addd 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/BasicConstraintManager.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/BasicConstraintManager.cpp @@ -56,59 +56,59 @@ public: : SimpleConstraintManager(subengine), ISetFactory(statemgr.getAllocator()) {} - const ProgramState *assumeSymNE(const ProgramState *state, + ProgramStateRef assumeSymNE(ProgramStateRef state, SymbolRef sym, const llvm::APSInt& V, const llvm::APSInt& Adjustment); - const ProgramState *assumeSymEQ(const ProgramState *state, + ProgramStateRef assumeSymEQ(ProgramStateRef state, SymbolRef sym, const llvm::APSInt& V, const llvm::APSInt& Adjustment); - const ProgramState *assumeSymLT(const ProgramState *state, + ProgramStateRef assumeSymLT(ProgramStateRef state, SymbolRef sym, const llvm::APSInt& V, const llvm::APSInt& Adjustment); - const ProgramState *assumeSymGT(const ProgramState *state, + ProgramStateRef assumeSymGT(ProgramStateRef state, SymbolRef sym, const llvm::APSInt& V, const llvm::APSInt& Adjustment); - const ProgramState *assumeSymGE(const ProgramState *state, + ProgramStateRef assumeSymGE(ProgramStateRef state, SymbolRef sym, const llvm::APSInt& V, const llvm::APSInt& Adjustment); - const ProgramState *assumeSymLE(const ProgramState *state, + ProgramStateRef assumeSymLE(ProgramStateRef state, SymbolRef sym, const llvm::APSInt& V, const llvm::APSInt& Adjustment); - const ProgramState *AddEQ(const ProgramState *state, + ProgramStateRef AddEQ(ProgramStateRef state, SymbolRef sym, const llvm::APSInt& V); - const ProgramState *AddNE(const ProgramState *state, + ProgramStateRef AddNE(ProgramStateRef state, SymbolRef sym, const llvm::APSInt& V); - const llvm::APSInt* getSymVal(const ProgramState *state, + const llvm::APSInt* getSymVal(ProgramStateRef state, SymbolRef sym) const; - bool isNotEqual(const ProgramState *state, + bool isNotEqual(ProgramStateRef state, SymbolRef sym, const llvm::APSInt& V) const; - bool isEqual(const ProgramState *state, + bool isEqual(ProgramStateRef state, SymbolRef sym, const llvm::APSInt& V) const; - const ProgramState *removeDeadBindings(const ProgramState *state, + ProgramStateRef removeDeadBindings(ProgramStateRef state, SymbolReaper& SymReaper); - void print(const ProgramState *state, + void print(ProgramStateRef state, raw_ostream &Out, const char* nl, const char *sep); @@ -122,8 +122,8 @@ ento::CreateBasicConstraintManager(ProgramStateManager& statemgr, return new BasicConstraintManager(statemgr, subengine); } -const ProgramState* -BasicConstraintManager::assumeSymNE(const ProgramState *state, +ProgramStateRef +BasicConstraintManager::assumeSymNE(ProgramStateRef state, SymbolRef sym, const llvm::APSInt &V, const llvm::APSInt &Adjustment) { @@ -143,8 +143,8 @@ BasicConstraintManager::assumeSymNE(const ProgramState *state, return AddNE(state, sym, Adjusted); } -const ProgramState* -BasicConstraintManager::assumeSymEQ(const ProgramState *state, +ProgramStateRef +BasicConstraintManager::assumeSymEQ(ProgramStateRef state, SymbolRef sym, const llvm::APSInt &V, const llvm::APSInt &Adjustment) { @@ -165,8 +165,8 @@ BasicConstraintManager::assumeSymEQ(const ProgramState *state, } // The logic for these will be handled in another ConstraintManager. -const ProgramState* -BasicConstraintManager::assumeSymLT(const ProgramState *state, +ProgramStateRef +BasicConstraintManager::assumeSymLT(ProgramStateRef state, SymbolRef sym, const llvm::APSInt &V, const llvm::APSInt &Adjustment) { @@ -180,8 +180,8 @@ BasicConstraintManager::assumeSymLT(const ProgramState *state, return assumeSymNE(state, sym, V, Adjustment); } -const ProgramState* -BasicConstraintManager::assumeSymGT(const ProgramState *state, +ProgramStateRef +BasicConstraintManager::assumeSymGT(ProgramStateRef state, SymbolRef sym, const llvm::APSInt &V, const llvm::APSInt &Adjustment) { @@ -195,8 +195,8 @@ BasicConstraintManager::assumeSymGT(const ProgramState *state, return assumeSymNE(state, sym, V, Adjustment); } -const ProgramState* -BasicConstraintManager::assumeSymGE(const ProgramState *state, +ProgramStateRef +BasicConstraintManager::assumeSymGE(ProgramStateRef state, SymbolRef sym, const llvm::APSInt &V, const llvm::APSInt &Adjustment) { @@ -224,8 +224,8 @@ BasicConstraintManager::assumeSymGE(const ProgramState *state, return state; } -const ProgramState* -BasicConstraintManager::assumeSymLE(const ProgramState *state, +ProgramStateRef +BasicConstraintManager::assumeSymLE(ProgramStateRef state, SymbolRef sym, const llvm::APSInt &V, const llvm::APSInt &Adjustment) { @@ -253,14 +253,14 @@ BasicConstraintManager::assumeSymLE(const ProgramState *state, return state; } -const ProgramState *BasicConstraintManager::AddEQ(const ProgramState *state, +ProgramStateRef BasicConstraintManager::AddEQ(ProgramStateRef 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 ProgramState *BasicConstraintManager::AddNE(const ProgramState *state, +ProgramStateRef BasicConstraintManager::AddNE(ProgramStateRef state, SymbolRef sym, const llvm::APSInt& V) { @@ -275,13 +275,13 @@ const ProgramState *BasicConstraintManager::AddNE(const ProgramState *state, return state->set<ConstNotEq>(sym, S); } -const llvm::APSInt* BasicConstraintManager::getSymVal(const ProgramState *state, +const llvm::APSInt* BasicConstraintManager::getSymVal(ProgramStateRef state, SymbolRef sym) const { const ConstEqTy::data_type* T = state->get<ConstEq>(sym); return T ? *T : NULL; } -bool BasicConstraintManager::isNotEqual(const ProgramState *state, +bool BasicConstraintManager::isNotEqual(ProgramStateRef state, SymbolRef sym, const llvm::APSInt& V) const { @@ -292,7 +292,7 @@ bool BasicConstraintManager::isNotEqual(const ProgramState *state, return T ? T->contains(&state->getBasicVals().getValue(V)) : false; } -bool BasicConstraintManager::isEqual(const ProgramState *state, +bool BasicConstraintManager::isEqual(ProgramStateRef state, SymbolRef sym, const llvm::APSInt& V) const { // Retrieve the EQ-set associated with the given symbol. @@ -303,8 +303,8 @@ bool BasicConstraintManager::isEqual(const ProgramState *state, /// Scan all symbols referenced by the constraints. If the symbol is not alive /// as marked in LSymbols, mark it as dead in DSymbols. -const ProgramState* -BasicConstraintManager::removeDeadBindings(const ProgramState *state, +ProgramStateRef +BasicConstraintManager::removeDeadBindings(ProgramStateRef state, SymbolReaper& SymReaper) { ConstEqTy CE = state->get<ConstEq>(); @@ -329,7 +329,7 @@ BasicConstraintManager::removeDeadBindings(const ProgramState *state, return state->set<ConstNotEq>(CNE); } -void BasicConstraintManager::print(const ProgramState *state, +void BasicConstraintManager::print(ProgramStateRef state, raw_ostream &Out, const char* nl, const char *sep) { // Print equality constraints. diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/BugReporter.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/BugReporter.cpp index fbbdb04..a264212 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/BugReporter.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/BugReporter.cpp @@ -17,6 +17,7 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" #include "clang/AST/ASTContext.h" #include "clang/Analysis/CFG.h" +#include "clang/AST/DeclObjC.h" #include "clang/AST/Expr.h" #include "clang/AST/ParentMap.h" #include "clang/AST/StmtObjC.h" @@ -25,8 +26,10 @@ #include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" #include "llvm/Support/raw_ostream.h" #include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/SmallString.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/OwningPtr.h" +#include "llvm/ADT/IntrusiveRefCntPtr.h" #include <queue> using namespace clang; @@ -34,6 +37,8 @@ using namespace ento; BugReporterVisitor::~BugReporterVisitor() {} +void BugReporterContext::anchor() {} + //===----------------------------------------------------------------------===// // Helper routines for walking the ExplodedGraph and fetching statements. //===----------------------------------------------------------------------===// @@ -106,6 +111,59 @@ GetCurrentOrNextStmt(const ExplodedNode *N) { } //===----------------------------------------------------------------------===// +// Diagnostic cleanup. +//===----------------------------------------------------------------------===// + +/// Recursively scan through a path and prune out calls and macros pieces +/// that aren't needed. Return true if afterwards the path contains +/// "interesting stuff" which means it should be pruned from the parent path. +static bool RemoveUneededCalls(PathPieces &pieces) { + bool containsSomethingInteresting = false; + const unsigned N = pieces.size(); + + for (unsigned i = 0 ; i < N ; ++i) { + // Remove the front piece from the path. If it is still something we + // want to keep once we are done, we will push it back on the end. + IntrusiveRefCntPtr<PathDiagnosticPiece> piece(pieces.front()); + pieces.pop_front(); + + switch (piece->getKind()) { + case PathDiagnosticPiece::Call: { + PathDiagnosticCallPiece *call = cast<PathDiagnosticCallPiece>(piece); + // Recursively clean out the subclass. Keep this call around if + // it contains any informative diagnostics. + if (!RemoveUneededCalls(call->path)) + continue; + containsSomethingInteresting = true; + break; + } + case PathDiagnosticPiece::Macro: { + PathDiagnosticMacroPiece *macro = cast<PathDiagnosticMacroPiece>(piece); + if (!RemoveUneededCalls(macro->subPieces)) + continue; + containsSomethingInteresting = true; + break; + } + case PathDiagnosticPiece::Event: { + PathDiagnosticEventPiece *event = cast<PathDiagnosticEventPiece>(piece); + // We never throw away an event, but we do throw it away wholesale + // as part of a path if we throw the entire path away. + if (event->isPrunable()) + continue; + containsSomethingInteresting = true; + break; + } + case PathDiagnosticPiece::ControlFlow: + break; + } + + pieces.push_back(piece); + } + + return containsSomethingInteresting; +} + +//===----------------------------------------------------------------------===// // PathDiagnosticBuilder and its associated routines and helper objects. //===----------------------------------------------------------------------===// @@ -128,14 +186,17 @@ public: class PathDiagnosticBuilder : public BugReporterContext { BugReport *R; PathDiagnosticConsumer *PDC; - llvm::OwningPtr<ParentMap> PM; + OwningPtr<ParentMap> PM; NodeMapClosure NMC; public: + const LocationContext *LC; + PathDiagnosticBuilder(GRBugReporter &br, BugReport *r, NodeBackMap *Backmap, PathDiagnosticConsumer *pdc) : BugReporterContext(br), - R(r), PDC(pdc), NMC(Backmap) {} + R(r), PDC(pdc), NMC(Backmap), LC(r->getErrorNode()->getLocationContext()) + {} PathDiagnosticLocation ExecutionContinues(const ExplodedNode *N); @@ -145,12 +206,8 @@ public: BugReport *getBugReport() { return R; } Decl const &getCodeDecl() { return R->getErrorNode()->getCodeDecl(); } - - const LocationContext* getLocationContext() { - return R->getErrorNode()->getLocationContext(); - } - - ParentMap& getParentMap() { return R->getErrorNode()->getParentMap(); } + + ParentMap& getParentMap() { return LC->getParentMap(); } const Stmt *getParent(const Stmt *S) { return getParentMap().getParent(S); @@ -173,7 +230,7 @@ public: PathDiagnosticLocation PathDiagnosticBuilder::ExecutionContinues(const ExplodedNode *N) { if (const Stmt *S = GetNextStmt(N)) - return PathDiagnosticLocation(S, getSourceManager(), getLocationContext()); + return PathDiagnosticLocation(S, getSourceManager(), LC); return PathDiagnosticLocation::createDeclEnd(N->getLocationContext(), getSourceManager()); @@ -234,7 +291,6 @@ PathDiagnosticBuilder::getEnclosingStmtLocation(const Stmt *S) { 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); @@ -322,208 +378,87 @@ PathDiagnosticBuilder::getEnclosingStmtLocation(const Stmt *S) { } //===----------------------------------------------------------------------===// -// ScanNotableSymbols: closure-like callback for scanning Store bindings. +// "Minimal" path diagnostic generation algorithm. //===----------------------------------------------------------------------===// - -static const VarDecl* GetMostRecentVarDeclBinding(const ExplodedNode *N, - ProgramStateManager& VMgr, - SVal X) { - - for ( ; N ; N = N->pred_empty() ? 0 : *N->pred_begin()) { - - ProgramPoint P = N->getLocation(); - - if (!isa<PostStmt>(P)) - continue; - - const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(cast<PostStmt>(P).getStmt()); - - if (!DR) - continue; - - SVal Y = N->getState()->getSVal(DR); - - if (X != Y) - continue; - - const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl()); - - if (!VD) - continue; - - return VD; - } - - return 0; -} - -namespace { -class NotableSymbolHandler -: public StoreManager::BindingsHandler { - - SymbolRef Sym; - const ProgramState *PrevSt; - const Stmt *S; - ProgramStateManager& VMgr; - const ExplodedNode *Pred; - PathDiagnostic& PD; - BugReporter& BR; - -public: - - 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) { - - SymbolRef ScanSym = V.getAsSymbol(); - - if (ScanSym != Sym) - return true; - - // Check if the previous state has this binding. - SVal X = PrevSt->getSVal(loc::MemRegionVal(R)); - - if (X == V) // Same binding? - return true; - - // Different binding. Only handle assignments for now. We don't pull - // this check out of the loop because we will eventually handle other - // cases. - - VarDecl *VD = 0; - - if (const BinaryOperator* B = dyn_cast<BinaryOperator>(S)) { - if (!B->isAssignmentOp()) - return true; - - // What variable did we assign to? - 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)) { - // 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. - VD = dyn_cast<VarDecl>(*DS->decl_begin()); - } - - if (!VD) - return true; - - // What is the most recently referenced variable with this binding? - const VarDecl *MostRecent = GetMostRecentVarDeclBinding(Pred, VMgr, V); - - if (!MostRecent) - return true; - - // Create the diagnostic. - if (Loc::isLocType(VD->getType())) { - 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; +typedef std::pair<PathDiagnosticCallPiece*, const ExplodedNode*> StackDiagPair; +typedef SmallVector<StackDiagPair, 6> StackDiagVector; + +static void updateStackPiecesWithMessage(PathDiagnosticPiece *P, + StackDiagVector &CallStack) { + // If the piece contains a special message, add it to all the call + // pieces on the active stack. + if (PathDiagnosticEventPiece *ep = + dyn_cast<PathDiagnosticEventPiece>(P)) { + + if (ep->hasCallStackHint()) + for (StackDiagVector::iterator I = CallStack.begin(), + E = CallStack.end(); I != E; ++I) { + PathDiagnosticCallPiece *CP = I->first; + const ExplodedNode *N = I->second; + std::string stackMsg = ep->getCallStackMessage(N); + + // The last message on the path to final bug is the most important + // one. Since we traverse the path backwards, do not add the message + // if one has been previously added. + if (!CP->hasCallStackMessage()) + CP->setCallStackMessage(stackMsg); + } } -}; -} - -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 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? - ProgramStateManager& VMgr = cast<GRBugReporter>(BR).getStateManager(); - NotableSymbolHandler H(Sym, PrevSt, S, VMgr, Pred, PD, BR); - cast<GRBugReporter>(BR).getStateManager().iterBindings(N->getState(), H); } -namespace { -class ScanNotableSymbols -: public StoreManager::BindingsHandler { - - llvm::SmallSet<SymbolRef, 10> AlreadyProcessed; - const ExplodedNode *N; - const Stmt *S; - GRBugReporter& BR; - PathDiagnostic& PD; - -public: - ScanNotableSymbols(const ExplodedNode *n, const Stmt *s, - GRBugReporter& br, PathDiagnostic& pd) - : N(n), S(s), BR(br), PD(pd) {} - - bool HandleBinding(StoreManager& SMgr, Store store, - const MemRegion* R, SVal V) { - - SymbolRef ScanSym = V.getAsSymbol(); - - if (!ScanSym) - return true; - - if (!BR.isNotable(ScanSym)) - return true; - - if (AlreadyProcessed.count(ScanSym)) - return true; - - AlreadyProcessed.insert(ScanSym); - - HandleNotableSymbol(N, S, ScanSym, BR, PD); - return true; - } -}; -} // end anonymous namespace - -//===----------------------------------------------------------------------===// -// "Minimal" path diagnostic generation algorithm. -//===----------------------------------------------------------------------===// - -static void CompactPathDiagnostic(PathDiagnostic &PD, const SourceManager& SM); +static void CompactPathDiagnostic(PathPieces &path, const SourceManager& SM); static void GenerateMinimalPathDiagnostic(PathDiagnostic& PD, PathDiagnosticBuilder &PDB, - const ExplodedNode *N) { + const ExplodedNode *N, + ArrayRef<BugReporterVisitor *> visitors) { SourceManager& SMgr = PDB.getSourceManager(); - const LocationContext *LC = PDB.getLocationContext(); + const LocationContext *LC = PDB.LC; const ExplodedNode *NextNode = N->pred_empty() ? NULL : *(N->pred_begin()); + + StackDiagVector CallStack; + while (NextNode) { N = NextNode; + PDB.LC = N->getLocationContext(); NextNode = GetPredecessorNode(N); ProgramPoint P = N->getLocation(); + + if (const CallExit *CE = dyn_cast<CallExit>(&P)) { + PathDiagnosticCallPiece *C = + PathDiagnosticCallPiece::construct(N, *CE, SMgr); + PD.getActivePath().push_front(C); + PD.pushActivePath(&C->path); + CallStack.push_back(StackDiagPair(C, N)); + continue; + } + + if (const CallEnter *CE = dyn_cast<CallEnter>(&P)) { + PD.popActivePath(); + // The current active path should never be empty. Either we + // just added a bunch of stuff to the top-level path, or + // we have a previous CallExit. If the front of the active + // path is not a PathDiagnosticCallPiece, it means that the + // path terminated within a function call. We must then take the + // current contents of the active path and place it within + // a new PathDiagnosticCallPiece. + assert(!PD.getActivePath().empty()); + PathDiagnosticCallPiece *C = + dyn_cast<PathDiagnosticCallPiece>(PD.getActivePath().front()); + if (!C) { + const Decl *Caller = CE->getLocationContext()->getDecl(); + C = PathDiagnosticCallPiece::construct(PD.getActivePath(), Caller); + } + C->setCallee(*CE, SMgr); + if (!CallStack.empty()) { + assert(CallStack.back().first == C); + CallStack.pop_back(); + } + continue; + } if (const BlockEdge *BE = dyn_cast<BlockEdge>(&P)) { const CFGBlock *Src = BE->getSrc(); @@ -554,8 +489,8 @@ static void GenerateMinimalPathDiagnostic(PathDiagnostic& PD, os << "Control jumps to line " << End.asLocation().getExpansionLineNumber(); - PD.push_front(new PathDiagnosticControlFlowPiece(Start, End, - os.str())); + PD.getActivePath().push_front(new PathDiagnosticControlFlowPiece(Start, End, + os.str())); break; } @@ -606,13 +541,13 @@ static void GenerateMinimalPathDiagnostic(PathDiagnostic& PD, break; } } - PD.push_front(new PathDiagnosticControlFlowPiece(Start, End, + PD.getActivePath().push_front(new PathDiagnosticControlFlowPiece(Start, End, os.str())); } else { os << "'Default' branch taken. "; const PathDiagnosticLocation &End = PDB.ExecutionContinues(os, N); - PD.push_front(new PathDiagnosticControlFlowPiece(Start, End, + PD.getActivePath().push_front(new PathDiagnosticControlFlowPiece(Start, End, os.str())); } @@ -624,7 +559,7 @@ static void GenerateMinimalPathDiagnostic(PathDiagnostic& PD, std::string sbuf; llvm::raw_string_ostream os(sbuf); PathDiagnosticLocation End = PDB.ExecutionContinues(os, N); - PD.push_front(new PathDiagnosticControlFlowPiece(Start, End, + PD.getActivePath().push_front(new PathDiagnosticControlFlowPiece(Start, End, os.str())); break; } @@ -646,7 +581,7 @@ static void GenerateMinimalPathDiagnostic(PathDiagnostic& PD, if (const Stmt *S = End.asStmt()) End = PDB.getEnclosingStmtLocation(S); - PD.push_front(new PathDiagnosticControlFlowPiece(Start, End, + PD.getActivePath().push_front(new PathDiagnosticControlFlowPiece(Start, End, os.str())); break; } @@ -669,14 +604,14 @@ static void GenerateMinimalPathDiagnostic(PathDiagnostic& PD, PathDiagnosticLocation End(B->getLHS(), SMgr, LC); PathDiagnosticLocation Start = PathDiagnosticLocation::createOperatorLoc(B, SMgr); - PD.push_front(new PathDiagnosticControlFlowPiece(Start, End, + PD.getActivePath().push_front(new PathDiagnosticControlFlowPiece(Start, End, os.str())); } else { os << "true"; PathDiagnosticLocation Start(B->getLHS(), SMgr, LC); PathDiagnosticLocation End = PDB.ExecutionContinues(N); - PD.push_front(new PathDiagnosticControlFlowPiece(Start, End, + PD.getActivePath().push_front(new PathDiagnosticControlFlowPiece(Start, End, os.str())); } } @@ -688,7 +623,7 @@ static void GenerateMinimalPathDiagnostic(PathDiagnostic& PD, os << "false"; PathDiagnosticLocation Start(B->getLHS(), SMgr, LC); PathDiagnosticLocation End = PDB.ExecutionContinues(N); - PD.push_front(new PathDiagnosticControlFlowPiece(Start, End, + PD.getActivePath().push_front(new PathDiagnosticControlFlowPiece(Start, End, os.str())); } else { @@ -696,7 +631,7 @@ static void GenerateMinimalPathDiagnostic(PathDiagnostic& PD, PathDiagnosticLocation End(B->getLHS(), SMgr, LC); PathDiagnosticLocation Start = PathDiagnosticLocation::createOperatorLoc(B, SMgr); - PD.push_front(new PathDiagnosticControlFlowPiece(Start, End, + PD.getActivePath().push_front(new PathDiagnosticControlFlowPiece(Start, End, os.str())); } } @@ -715,7 +650,7 @@ static void GenerateMinimalPathDiagnostic(PathDiagnostic& PD, if (const Stmt *S = End.asStmt()) End = PDB.getEnclosingStmtLocation(S); - PD.push_front(new PathDiagnosticControlFlowPiece(Start, End, + PD.getActivePath().push_front(new PathDiagnosticControlFlowPiece(Start, End, os.str())); } else { @@ -724,7 +659,7 @@ static void GenerateMinimalPathDiagnostic(PathDiagnostic& PD, if (const Stmt *S = End.asStmt()) End = PDB.getEnclosingStmtLocation(S); - PD.push_front(new PathDiagnosticControlFlowPiece(Start, End, + PD.getActivePath().push_front(new PathDiagnosticControlFlowPiece(Start, End, "Loop condition is false. Exiting loop")); } @@ -742,7 +677,7 @@ static void GenerateMinimalPathDiagnostic(PathDiagnostic& PD, if (const Stmt *S = End.asStmt()) End = PDB.getEnclosingStmtLocation(S); - PD.push_front(new PathDiagnosticControlFlowPiece(Start, End, + PD.getActivePath().push_front(new PathDiagnosticControlFlowPiece(Start, End, os.str())); } else { @@ -750,7 +685,7 @@ static void GenerateMinimalPathDiagnostic(PathDiagnostic& PD, if (const Stmt *S = End.asStmt()) End = PDB.getEnclosingStmtLocation(S); - PD.push_front(new PathDiagnosticControlFlowPiece(Start, End, + PD.getActivePath().push_front(new PathDiagnosticControlFlowPiece(Start, End, "Loop condition is true. Entering loop body")); } @@ -764,10 +699,10 @@ static void GenerateMinimalPathDiagnostic(PathDiagnostic& PD, End = PDB.getEnclosingStmtLocation(S); if (*(Src->succ_begin()+1) == Dst) - PD.push_front(new PathDiagnosticControlFlowPiece(Start, End, + PD.getActivePath().push_front(new PathDiagnosticControlFlowPiece(Start, End, "Taking false branch")); else - PD.push_front(new PathDiagnosticControlFlowPiece(Start, End, + PD.getActivePath().push_front(new PathDiagnosticControlFlowPiece(Start, End, "Taking true branch")); break; @@ -778,24 +713,20 @@ static void GenerateMinimalPathDiagnostic(PathDiagnostic& PD, if (NextNode) { // 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); + for (ArrayRef<BugReporterVisitor *>::iterator I = visitors.begin(), + E = visitors.end(); + I != E; ++I) { + if (PathDiagnosticPiece *p = (*I)->VisitNode(N, NextNode, PDB, *R)) { + PD.getActivePath().push_front(p); + updateStackPiecesWithMessage(p, CallStack); + } } } - - 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); - PDB.getStateManager().iterBindings(N->getState(), SNS); - } } // After constructing the full PathDiagnostic, do a pass over it to compact // PathDiagnosticPieces that occur within a macro. - CompactPathDiagnostic(PD, PDB.getSourceManager()); + CompactPathDiagnostic(PD.getMutablePieces(), PDB.getSourceManager()); } //===----------------------------------------------------------------------===// @@ -879,7 +810,7 @@ class EdgeBuilder { } if (S != Original) - L = PathDiagnosticLocation(S, L.getManager(), PDB.getLocationContext()); + L = PathDiagnosticLocation(S, L.getManager(), PDB.LC); } if (firstCharOnly) @@ -902,8 +833,8 @@ public: // If the PathDiagnostic already has pieces, add the enclosing statement // of the first piece as a context as well. - if (!PD.empty()) { - PrevLoc = PD.begin()->getLocation(); + if (!PD.path.empty()) { + PrevLoc = (*PD.path.begin())->getLocation(); if (const Stmt *S = PrevLoc.asStmt()) addExtendedContext(PDB.getEnclosingStmtLocation(S).asStmt()); @@ -916,12 +847,18 @@ public: // Finally, add an initial edge from the start location of the first // statement (if it doesn't already exist). PathDiagnosticLocation L = PathDiagnosticLocation::createDeclBegin( - PDB.getLocationContext(), + PDB.LC, PDB.getSourceManager()); if (L.isValid()) rawAddEdge(L); } + void flushLocations() { + while (!CLocs.empty()) + popLocation(); + PrevLoc = PathDiagnosticLocation(); + } + void addEdge(PathDiagnosticLocation NewLoc, bool alwaysAdd = false); void rawAddEdge(PathDiagnosticLocation NewLoc); @@ -988,7 +925,7 @@ bool EdgeBuilder::containsLocation(const PathDiagnosticLocation &Container, SM.getExpansionColumnNumber(ContaineeRBeg)) && (ContainerEndLine != ContaineeEndLine || SM.getExpansionColumnNumber(ContainerREnd) >= - SM.getExpansionColumnNumber(ContainerREnd))); + SM.getExpansionColumnNumber(ContaineeREnd))); } void EdgeBuilder::rawAddEdge(PathDiagnosticLocation NewLoc) { @@ -1008,7 +945,7 @@ void EdgeBuilder::rawAddEdge(PathDiagnosticLocation NewLoc) { PrevLocClean.asLocation().getExpansionLoc()) return; - PD.push_front(new PathDiagnosticControlFlowPiece(NewLocClean, PrevLocClean)); + PD.getActivePath().push_front(new PathDiagnosticControlFlowPiece(NewLocClean, PrevLocClean)); PrevLoc = NewLoc; } @@ -1093,7 +1030,7 @@ void EdgeBuilder::addContext(const Stmt *S) { if (!S) return; - PathDiagnosticLocation L(S, PDB.getSourceManager(), PDB.getLocationContext()); + PathDiagnosticLocation L(S, PDB.getSourceManager(), PDB.LC); while (!CLocs.empty()) { const PathDiagnosticLocation &TopContextLoc = CLocs.back(); @@ -1116,9 +1053,11 @@ void EdgeBuilder::addContext(const Stmt *S) { static void GenerateExtensivePathDiagnostic(PathDiagnostic& PD, PathDiagnosticBuilder &PDB, - const ExplodedNode *N) { + const ExplodedNode *N, + ArrayRef<BugReporterVisitor *> visitors) { EdgeBuilder EB(PD, PDB); const SourceManager& SM = PDB.getSourceManager(); + StackDiagVector CallStack; const ExplodedNode *NextNode = N->pred_empty() ? NULL : *(N->pred_begin()); while (NextNode) { @@ -1127,14 +1066,74 @@ static void GenerateExtensivePathDiagnostic(PathDiagnostic& PD, ProgramPoint P = N->getLocation(); do { + if (const CallExit *CE = dyn_cast<CallExit>(&P)) { + const StackFrameContext *LCtx = + CE->getLocationContext()->getCurrentStackFrame(); + PathDiagnosticLocation Loc(LCtx->getCallSite(), + PDB.getSourceManager(), + LCtx); + EB.addEdge(Loc, true); + EB.flushLocations(); + PathDiagnosticCallPiece *C = + PathDiagnosticCallPiece::construct(N, *CE, SM); + PD.getActivePath().push_front(C); + PD.pushActivePath(&C->path); + CallStack.push_back(StackDiagPair(C, N)); + break; + } + + // Pop the call hierarchy if we are done walking the contents + // of a function call. + if (const CallEnter *CE = dyn_cast<CallEnter>(&P)) { + // Add an edge to the start of the function. + const Decl *D = CE->getCalleeContext()->getDecl(); + PathDiagnosticLocation pos = + PathDiagnosticLocation::createBegin(D, SM); + EB.addEdge(pos); + + // Flush all locations, and pop the active path. + EB.flushLocations(); + PD.popActivePath(); + assert(!PD.getActivePath().empty()); + PDB.LC = N->getLocationContext(); + + // The current active path should never be empty. Either we + // just added a bunch of stuff to the top-level path, or + // we have a previous CallExit. If the front of the active + // path is not a PathDiagnosticCallPiece, it means that the + // path terminated within a function call. We must then take the + // current contents of the active path and place it within + // a new PathDiagnosticCallPiece. + PathDiagnosticCallPiece *C = + dyn_cast<PathDiagnosticCallPiece>(PD.getActivePath().front()); + if (!C) { + const Decl * Caller = CE->getLocationContext()->getDecl(); + C = PathDiagnosticCallPiece::construct(PD.getActivePath(), Caller); + } + C->setCallee(*CE, SM); + EB.addContext(CE->getCallExpr()); + + if (!CallStack.empty()) { + assert(CallStack.back().first == C); + CallStack.pop_back(); + } + break; + } + + // Note that is important that we update the LocationContext + // after looking at CallExits. CallExit basically adds an + // edge in the *caller*, so we don't want to update the LocationContext + // too soon. + PDB.LC = N->getLocationContext(); + // Block edges. - if (const BlockEdge *BE = dyn_cast<BlockEdge>(&P)) { + if (const BlockEdge *BE = dyn_cast<BlockEdge>(&P)) { const CFGBlock &Blk = *BE->getSrc(); const Stmt *Term = Blk.getTerminator(); // Are we jumping to the head of a loop? Add a special diagnostic. if (const Stmt *Loop = BE->getDst()->getLoopTarget()) { - PathDiagnosticLocation L(Loop, SM, PDB.getLocationContext()); + PathDiagnosticLocation L(Loop, SM, PDB.LC); const CompoundStmt *CS = NULL; if (!Term) { @@ -1147,9 +1146,10 @@ static void GenerateExtensivePathDiagnostic(PathDiagnostic& PD, PathDiagnosticEventPiece *p = new PathDiagnosticEventPiece(L, "Looping back to the head of the loop"); + p->setPrunable(true); EB.addEdge(p->getLocation(), true); - PD.push_front(p); + PD.getActivePath().push_front(p); if (CS) { PathDiagnosticLocation BL = @@ -1177,6 +1177,8 @@ static void GenerateExtensivePathDiagnostic(PathDiagnostic& PD, break; } + + } while (0); if (!NextNode) @@ -1184,12 +1186,15 @@ static void GenerateExtensivePathDiagnostic(PathDiagnostic& PD, // Add pieces from custom visitors. BugReport *R = PDB.getBugReport(); - for (BugReport::visitor_iterator I = R->visitor_begin(), - E = R->visitor_end(); I!=E; ++I) { + for (ArrayRef<BugReporterVisitor *>::iterator I = visitors.begin(), + E = visitors.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); + PD.getActivePath().push_front(p); + updateStackPiecesWithMessage(p, CallStack); + if (const Stmt *S = Loc.asStmt()) EB.addExtendedContext(PDB.getEnclosingStmtLocation(S).asStmt()); } @@ -1204,10 +1209,14 @@ BugType::~BugType() { } void BugType::FlushReports(BugReporter &BR) {} +void BuiltinBug::anchor() {} + //===----------------------------------------------------------------------===// // Methods for BugReport and subclasses. //===----------------------------------------------------------------------===// +void BugReport::NodeResolver::anchor() {} + void BugReport::addVisitor(BugReporterVisitor* visitor) { if (!visitor) return; @@ -1222,7 +1231,8 @@ void BugReport::addVisitor(BugReporterVisitor* visitor) { } CallbacksSet.InsertNode(visitor, InsertPos); - Callbacks = F.add(visitor, Callbacks); + Callbacks.push_back(visitor); + ++ConfigurationChangeToken; } BugReport::~BugReport() { @@ -1231,10 +1241,24 @@ BugReport::~BugReport() { } } +const Decl *BugReport::getDeclWithIssue() const { + if (DeclWithIssue) + return DeclWithIssue; + + const ExplodedNode *N = getErrorNode(); + if (!N) + return 0; + + const LocationContext *LC = N->getLocationContext(); + return LC->getCurrentStackFrame()->getDecl(); +} + void BugReport::Profile(llvm::FoldingSetNodeID& hash) const { hash.AddPointer(&BT); hash.AddString(Description); - if (Location.isValid()) { + if (UniqueingLocation.isValid()) { + UniqueingLocation.Profile(hash); + } else if (Location.isValid()) { Location.Profile(hash); } else { assert(ErrorNode); @@ -1251,6 +1275,61 @@ void BugReport::Profile(llvm::FoldingSetNodeID& hash) const { } } +void BugReport::markInteresting(SymbolRef sym) { + if (!sym) + return; + + // If the symbol wasn't already in our set, note a configuration change. + if (interestingSymbols.insert(sym).second) + ++ConfigurationChangeToken; + + if (const SymbolMetadata *meta = dyn_cast<SymbolMetadata>(sym)) + interestingRegions.insert(meta->getRegion()); +} + +void BugReport::markInteresting(const MemRegion *R) { + if (!R) + return; + + // If the base region wasn't already in our set, note a configuration change. + R = R->getBaseRegion(); + if (interestingRegions.insert(R).second) + ++ConfigurationChangeToken; + + if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(R)) + interestingSymbols.insert(SR->getSymbol()); +} + +void BugReport::markInteresting(SVal V) { + markInteresting(V.getAsRegion()); + markInteresting(V.getAsSymbol()); +} + +bool BugReport::isInteresting(SVal V) const { + return isInteresting(V.getAsRegion()) || isInteresting(V.getAsSymbol()); +} + +bool BugReport::isInteresting(SymbolRef sym) const { + if (!sym) + return false; + // We don't currently consider metadata symbols to be interesting + // even if we know their region is interesting. Is that correct behavior? + return interestingSymbols.count(sym); +} + +bool BugReport::isInteresting(const MemRegion *R) const { + if (!R) + return false; + R = R->getBaseRegion(); + bool b = interestingRegions.count(R); + if (b) + return true; + if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(R)) + return interestingSymbols.count(SR->getSymbol()); + return false; +} + + const Stmt *BugReport::getStmt() const { if (!ErrorNode) return 0; @@ -1316,10 +1395,7 @@ PathDiagnosticLocation BugReport::getLocation(const SourceManager &SM) const { // Methods for BugReporter and subclasses. //===----------------------------------------------------------------------===// -BugReportEquivClass::~BugReportEquivClass() { - for (iterator I=begin(), E=end(); I!=E; ++I) delete *I; -} - +BugReportEquivClass::~BugReportEquivClass() { } GRBugReporter::~GRBugReporter() { } BugReporterData::~BugReporterData() {} @@ -1394,8 +1470,8 @@ MakeReportGraph(const ExplodedGraph* G, // Create owning pointers for GTrim and NMap just to ensure that they are // released when this function exists. - llvm::OwningPtr<ExplodedGraph> AutoReleaseGTrim(GTrim); - llvm::OwningPtr<InterExplodedGraphMap> AutoReleaseNMap(NMap); + OwningPtr<ExplodedGraph> AutoReleaseGTrim(GTrim); + OwningPtr<InterExplodedGraphMap> AutoReleaseNMap(NMap); // Find the (first) error node in the trimmed graph. We just need to consult // the node map (NMap) which maps from nodes in the original graph to nodes @@ -1513,19 +1589,28 @@ MakeReportGraph(const ExplodedGraph* G, /// CompactPathDiagnostic - This function postprocesses a PathDiagnostic object /// and collapses PathDiagosticPieces that are expanded by macros. -static void CompactPathDiagnostic(PathDiagnostic &PD, const SourceManager& SM) { - typedef std::vector<std::pair<PathDiagnosticMacroPiece*, SourceLocation> > - MacroStackTy; +static void CompactPathDiagnostic(PathPieces &path, const SourceManager& SM) { + typedef std::vector<std::pair<IntrusiveRefCntPtr<PathDiagnosticMacroPiece>, + SourceLocation> > MacroStackTy; - typedef std::vector<PathDiagnosticPiece*> + typedef std::vector<IntrusiveRefCntPtr<PathDiagnosticPiece> > PiecesTy; MacroStackTy MacroStack; PiecesTy Pieces; - for (PathDiagnostic::iterator I = PD.begin(), E = PD.end(); I!=E; ++I) { + for (PathPieces::const_iterator I = path.begin(), E = path.end(); + I!=E; ++I) { + + PathDiagnosticPiece *piece = I->getPtr(); + + // Recursively compact calls. + if (PathDiagnosticCallPiece *call=dyn_cast<PathDiagnosticCallPiece>(piece)){ + CompactPathDiagnostic(call->path, SM); + } + // Get the location of the PathDiagnosticPiece. - const FullSourceLoc Loc = I->getLocation().asLocation(); + const FullSourceLoc Loc = piece->getLocation().asLocation(); // Determine the instantiation location, which is the location we group // related PathDiagnosticPieces. @@ -1535,7 +1620,7 @@ static void CompactPathDiagnostic(PathDiagnostic &PD, const SourceManager& SM) { if (Loc.isFileID()) { MacroStack.clear(); - Pieces.push_back(&*I); + Pieces.push_back(piece); continue; } @@ -1543,13 +1628,13 @@ static void CompactPathDiagnostic(PathDiagnostic &PD, const SourceManager& SM) { // Is the PathDiagnosticPiece within the same macro group? if (!MacroStack.empty() && InstantiationLoc == MacroStack.back().second) { - MacroStack.back().first->push_back(&*I); + MacroStack.back().first->subPieces.push_back(piece); continue; } // We aren't in the same group. Are we descending into a new macro // or are part of an old one? - PathDiagnosticMacroPiece *MacroGroup = 0; + IntrusiveRefCntPtr<PathDiagnosticMacroPiece> MacroGroup; SourceLocation ParentInstantiationLoc = InstantiationLoc.isMacroID() ? SM.getExpansionLoc(Loc) : @@ -1574,10 +1659,10 @@ static void CompactPathDiagnostic(PathDiagnostic &PD, const SourceManager& SM) { // Create a new macro group and add it to the stack. PathDiagnosticMacroPiece *NewGroup = new PathDiagnosticMacroPiece( - PathDiagnosticLocation::createSingleLocation(I->getLocation())); + PathDiagnosticLocation::createSingleLocation(piece->getLocation())); if (MacroGroup) - MacroGroup->push_back(NewGroup); + MacroGroup->subPieces.push_back(NewGroup); else { assert(InstantiationLoc.isFileID()); Pieces.push_back(NewGroup); @@ -1588,21 +1673,14 @@ static void CompactPathDiagnostic(PathDiagnostic &PD, const SourceManager& SM) { } // Finally, add the PathDiagnosticPiece to the group. - MacroGroup->push_back(&*I); + MacroGroup->subPieces.push_back(piece); } // Now take the pieces and construct a new PathDiagnostic. - PD.resetPath(false); + path.clear(); - for (PiecesTy::iterator I=Pieces.begin(), E=Pieces.end(); I!=E; ++I) { - if (PathDiagnosticMacroPiece *MP=dyn_cast<PathDiagnosticMacroPiece>(*I)) - if (!MP->containsEvent()) { - delete MP; - continue; - } - - PD.push_back(*I); - } + for (PiecesTy::iterator I=Pieces.begin(), E=Pieces.end(); I!=E; ++I) + path.push_back(*I); } void GRBugReporter::GeneratePathDiagnostic(PathDiagnostic& PD, @@ -1626,8 +1704,8 @@ void GRBugReporter::GeneratePathDiagnostic(PathDiagnostic& PD, BugReport *R = bugReports[GPair.second.second]; assert(R && "No original report found for sliced graph."); - llvm::OwningPtr<ExplodedGraph> ReportGraph(GPair.first.first); - llvm::OwningPtr<NodeBackMap> BackMap(GPair.first.second); + OwningPtr<ExplodedGraph> ReportGraph(GPair.first.first); + OwningPtr<NodeBackMap> BackMap(GPair.first.second); const ExplodedNode *N = GPair.second.first; // Start building the path diagnostic... @@ -1638,32 +1716,61 @@ void GRBugReporter::GeneratePathDiagnostic(PathDiagnostic& PD, 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; + BugReport::VisitorList visitors; + unsigned originalReportConfigToken, finalReportConfigToken; + + // While generating diagnostics, it's possible the visitors will decide + // new symbols and regions are interesting, or add other visitors based on + // the information they find. If they do, we need to regenerate the path + // based on our new report configuration. + do { + // Get a clean copy of all the visitors. + for (BugReport::visitor_iterator I = R->visitor_begin(), + E = R->visitor_end(); I != E; ++I) + visitors.push_back((*I)->clone()); + + // Clear out the active path from any previous work. + PD.getActivePath().clear(); + originalReportConfigToken = R->getConfigurationChangeToken(); + + // Generate the very last diagnostic piece - the piece is visible before + // the trace is expanded. + PathDiagnosticPiece *LastPiece = 0; + for (BugReport::visitor_iterator I = visitors.begin(), E = visitors.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; + if (!LastPiece) + LastPiece = BugReporterVisitor::getDefaultEndPath(PDB, N, *R); + if (LastPiece) + PD.getActivePath().push_back(LastPiece); + else + return; - switch (PDB.getGenerationScheme()) { + switch (PDB.getGenerationScheme()) { case PathDiagnosticConsumer::Extensive: - GenerateExtensivePathDiagnostic(PD, PDB, N); + GenerateExtensivePathDiagnostic(PD, PDB, N, visitors); break; case PathDiagnosticConsumer::Minimal: - GenerateMinimalPathDiagnostic(PD, PDB, N); + GenerateMinimalPathDiagnostic(PD, PDB, N, visitors); break; - } + } + + // Clean up the visitors we used. + llvm::DeleteContainerPointers(visitors); + + // Did anything change while generating this path? + finalReportConfigToken = R->getConfigurationChangeToken(); + } while(finalReportConfigToken != originalReportConfigToken); + + // Finally, prune the diagnostic path of uninteresting stuff. + bool hasSomethingInteresting = RemoveUneededCalls(PD.getMutablePieces()); + assert(hasSomethingInteresting); + (void) hasSomethingInteresting; } void BugReporter::Register(BugType *BT) { @@ -1711,17 +1818,17 @@ FindReportInEquivalenceClass(BugReportEquivClass& EQ, BugReportEquivClass::iterator I = EQ.begin(), E = EQ.end(); assert(I != E); - BugReport *R = *I; - BugType& BT = R->getBugType(); + BugType& BT = I->getBugType(); // If we don't need to suppress any of the nodes because they are // post-dominated by a sink, simply add all the nodes in the equivalence class // to 'Nodes'. Any of the reports will serve as a "representative" report. if (!BT.isSuppressOnSink()) { + BugReport *R = I; for (BugReportEquivClass::iterator I=EQ.begin(), E=EQ.end(); I!=E; ++I) { const ExplodedNode *N = I->getErrorNode(); if (N) { - R = *I; + R = I; bugReports.push_back(R); } } @@ -1737,8 +1844,7 @@ FindReportInEquivalenceClass(BugReportEquivClass& EQ, BugReport *exampleReport = 0; for (; I != E; ++I) { - R = *I; - const ExplodedNode *errorNode = R->getErrorNode(); + const ExplodedNode *errorNode = I->getErrorNode(); if (!errorNode) continue; @@ -1748,9 +1854,9 @@ FindReportInEquivalenceClass(BugReportEquivClass& EQ, } // No successors? By definition this nodes isn't post-dominated by a sink. if (errorNode->succ_empty()) { - bugReports.push_back(R); + bugReports.push_back(I); if (!exampleReport) - exampleReport = R; + exampleReport = I; continue; } @@ -1774,9 +1880,9 @@ FindReportInEquivalenceClass(BugReportEquivClass& EQ, if (Succ->succ_empty()) { // If we found an end-of-path node that is not a sink. if (!Succ->isSink()) { - bugReports.push_back(R); + bugReports.push_back(I); if (!exampleReport) - exampleReport = R; + exampleReport = I; WL.clear(); break; } @@ -1857,8 +1963,9 @@ void BugReporter::FlushReport(BugReportEquivClass& EQ) { // Probably doesn't make a difference in practice. BugType& BT = exampleReport->getBugType(); - llvm::OwningPtr<PathDiagnostic> - D(new PathDiagnostic(exampleReport->getBugType().getName(), + OwningPtr<PathDiagnostic> + D(new PathDiagnostic(exampleReport->getDeclWithIssue(), + exampleReport->getBugType().getName(), !PD || PD->useVerboseDescription() ? exampleReport->getDescription() : exampleReport->getShortDescription(), @@ -1866,9 +1973,6 @@ void BugReporter::FlushReport(BugReportEquivClass& EQ) { if (!bugReports.empty()) GeneratePathDiagnostic(*D.get(), bugReports); - - if (IsCachedDiagnostic(exampleReport, D.get())) - return; // Get the meta data. const BugReport::ExtraTextList &Meta = @@ -1883,24 +1987,23 @@ void BugReporter::FlushReport(BugReportEquivClass& EQ) { llvm::tie(Beg, End) = exampleReport->getRanges(); DiagnosticsEngine &Diag = getDiagnostic(); - // Search the description for '%', as that will be interpretted as a - // format character by FormatDiagnostics. - StringRef desc = exampleReport->getShortDescription(); - unsigned ErrorDiag; - { - llvm::SmallString<512> TmpStr; + if (!IsCachedDiagnostic(exampleReport, D.get())) { + // Search the description for '%', as that will be interpretted as a + // format character by FormatDiagnostics. + StringRef desc = exampleReport->getShortDescription(); + + SmallString<512> TmpStr; llvm::raw_svector_ostream Out(TmpStr); - for (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(DiagnosticsEngine::Warning, TmpStr); - } + unsigned ErrorDiag = Diag.getCustomDiagID(DiagnosticsEngine::Warning, TmpStr); - { DiagnosticBuilder diagBuilder = Diag.Report( exampleReport->getLocation(getSourceManager()).asLocation(), ErrorDiag); for (BugReport::ranges_iterator I = Beg; I != End; ++I) @@ -1911,25 +2014,21 @@ void BugReporter::FlushReport(BugReportEquivClass& EQ) { if (!PD) return; - if (D->empty()) { + if (D->path.empty()) { PathDiagnosticPiece *piece = new PathDiagnosticEventPiece( exampleReport->getLocation(getSourceManager()), exampleReport->getDescription()); + for ( ; Beg != End; ++Beg) + piece->addRange(*Beg); - for ( ; Beg != End; ++Beg) piece->addRange(*Beg); - D->push_back(piece); + D->getActivePath().push_back(piece); } PD->HandlePathDiagnostic(D.take()); } -void BugReporter::EmitBasicReport(StringRef name, StringRef str, - PathDiagnosticLocation Loc, - SourceRange* RBeg, unsigned NumRanges) { - EmitBasicReport(name, "", str, Loc, RBeg, NumRanges); -} - -void BugReporter::EmitBasicReport(StringRef name, +void BugReporter::EmitBasicReport(const Decl *DeclWithIssue, + StringRef name, StringRef category, StringRef str, PathDiagnosticLocation Loc, SourceRange* RBeg, unsigned NumRanges) { @@ -1937,13 +2036,14 @@ void BugReporter::EmitBasicReport(StringRef name, // 'BT' is owned by BugReporter. BugType *BT = getBugTypeForName(name, category); BugReport *R = new BugReport(*BT, str, Loc); + R->setDeclWithIssue(DeclWithIssue); for ( ; NumRanges > 0 ; --NumRanges, ++RBeg) R->addRange(*RBeg); EmitReport(R); } BugType *BugReporter::getBugTypeForName(StringRef name, StringRef category) { - llvm::SmallString<136> fullDesc; + SmallString<136> fullDesc; llvm::raw_svector_ostream(fullDesc) << name << ":" << category; llvm::StringMapEntry<BugType *> & entry = StrBugTypes.GetOrCreateValue(fullDesc); diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp index 1abd8ba..6532486 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp @@ -20,6 +20,7 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" +#include "llvm/ADT/SmallString.h" using namespace clang; using namespace ento; @@ -84,26 +85,8 @@ 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()); - } - } - - if (!L.isValid()) { - const Stmt *S = BR.getStmt(); - - if (!S) - return NULL; - - L = PathDiagnosticLocation(S, BRC.getSourceManager(), - PP.getLocationContext()); - } + PathDiagnosticLocation L = + PathDiagnosticLocation::createEndOfPath(EndPathNode,BRC.getSourceManager()); BugReport::ranges_iterator Beg, End; llvm::tie(Beg, End) = BR.getRanges(); @@ -138,17 +121,20 @@ PathDiagnosticPiece *FindLastStoreBRVisitor::VisitNode(const ExplodedNode *N, if (!StoreSite) { const ExplodedNode *Node = N, *Last = NULL; - for ( ; Node ; Last = Node, Node = Node->getFirstPred()) { + for ( ; 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()) { + // Record the last seen initialization point. Last = Node; break; } } + // Does the region still bind to value V? If not, we are done + // looking for store sites. if (Node->getState()->getSVal(R) != V) break; } @@ -165,7 +151,7 @@ PathDiagnosticPiece *FindLastStoreBRVisitor::VisitNode(const ExplodedNode *N, return NULL; satisfied = true; - llvm::SmallString<256> sbuf; + SmallString<256> sbuf; llvm::raw_svector_ostream os(sbuf); if (const PostStmt *PS = N->getLocationAs<PostStmt>()) { @@ -301,7 +287,8 @@ TrackConstraintBRVisitor::VisitNode(const ExplodedNode *N, BugReporterVisitor * bugreporter::getTrackNullOrUndefValueVisitor(const ExplodedNode *N, - const Stmt *S) { + const Stmt *S, + BugReport *report) { if (!S || !N) return 0; @@ -321,25 +308,27 @@ bugreporter::getTrackNullOrUndefValueVisitor(const ExplodedNode *N, if (!N) return 0; - const ProgramState *state = N->getState(); - - // Walk through lvalue-to-rvalue conversions. - if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(S)) { - if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) { - const VarRegion *R = - StateMgr.getRegionManager().getVarRegion(VD, N->getLocationContext()); + ProgramStateRef state = N->getState(); - // What did we load? - SVal V = state->getSVal(loc::MemRegionVal(R)); + // Walk through lvalue-to-rvalue conversions. + const Expr *Ex = dyn_cast<Expr>(S); + if (Ex) { + Ex = Ex->IgnoreParenLValueCasts(); + if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(Ex)) { + if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) { + const VarRegion *R = + StateMgr.getRegionManager().getVarRegion(VD, N->getLocationContext()); - if (isa<loc::ConcreteInt>(V) || isa<nonloc::ConcreteInt>(V) - || V.isUndef()) { + // What did we load? + SVal V = state->getSVal(loc::MemRegionVal(R)); + report->markInteresting(R); + report->markInteresting(V); return new FindLastStoreBRVisitor(V, R); } } } - SVal V = state->getSValAsScalarOrLoc(S); + SVal V = state->getSValAsScalarOrLoc(S, N->getLocationContext()); // Uncomment this to find cases where we aren't properly getting the // base value that was dereferenced. @@ -353,7 +342,7 @@ bugreporter::getTrackNullOrUndefValueVisitor(const ExplodedNode *N, } if (R) { - assert(isa<SymbolicRegion>(R)); + report->markInteresting(R); return new TrackConstraintBRVisitor(loc::MemRegionVal(R), false); } } @@ -366,7 +355,7 @@ FindLastStoreBRVisitor::createVisitorObject(const ExplodedNode *N, const MemRegion *R) { assert(R && "The memory region is null."); - const ProgramState *state = N->getState(); + ProgramStateRef state = N->getState(); SVal V = state->getSVal(R); if (V.isUnknown()) return 0; @@ -388,8 +377,8 @@ PathDiagnosticPiece *NilReceiverBRVisitor::VisitNode(const ExplodedNode *N, const Expr *Receiver = ME->getInstanceReceiver(); if (!Receiver) return 0; - const ProgramState *state = N->getState(); - const SVal &V = state->getSVal(Receiver); + ProgramStateRef state = N->getState(); + const SVal &V = state->getSVal(Receiver, N->getLocationContext()); const DefinedOrUnknownSVal *DV = dyn_cast<DefinedOrUnknownSVal>(&V); if (!DV) return 0; @@ -400,11 +389,11 @@ PathDiagnosticPiece *NilReceiverBRVisitor::VisitNode(const ExplodedNode *N, // 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)); + BR.addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, Receiver, &BR)); // Issue a message saying that the method was skipped. PathDiagnosticLocation L(Receiver, BRC.getSourceManager(), N->getLocationContext()); - return new PathDiagnosticEventPiece(L, "No method actually called " + return new PathDiagnosticEventPiece(L, "No method is called " "because the receiver is nil"); } @@ -419,7 +408,7 @@ void FindLastStoreBRVisitor::registerStatementVarDecls(BugReport &BR, const Stmt *Head = WorkList.front(); WorkList.pop_front(); - const ProgramState *state = N->getState(); + ProgramStateRef state = N->getState(); ProgramStateManager &StateMgr = state->getStateManager(); if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(Head)) { @@ -428,7 +417,7 @@ void FindLastStoreBRVisitor::registerStatementVarDecls(BugReport &BR, StateMgr.getRegionManager().getVarRegion(VD, N->getLocationContext()); // What did we load? - SVal V = state->getSVal(S); + SVal V = state->getSVal(S, N->getLocationContext()); if (isa<loc::ConcreteInt>(V) || isa<nonloc::ConcreteInt>(V)) { // Register a new visitor with the BugReport. @@ -450,11 +439,22 @@ PathDiagnosticPiece *ConditionBRVisitor::VisitNode(const ExplodedNode *N, const ExplodedNode *Prev, BugReporterContext &BRC, BugReport &BR) { + PathDiagnosticPiece *piece = VisitNodeImpl(N, Prev, BRC, BR); + if (PathDiagnosticEventPiece *ev = + dyn_cast_or_null<PathDiagnosticEventPiece>(piece)) + ev->setPrunable(true, /* override */ false); + return piece; +} + +PathDiagnosticPiece *ConditionBRVisitor::VisitNodeImpl(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(); + ProgramStateRef CurrentState = N->getState(); + ProgramStateRef 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 @@ -468,7 +468,7 @@ PathDiagnosticPiece *ConditionBRVisitor::VisitNode(const ExplodedNode *N, 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 VisitTerminator(term, N, srcBlk, BE->getDst(), BR, BRC); return 0; } @@ -482,10 +482,10 @@ PathDiagnosticPiece *ConditionBRVisitor::VisitNode(const ExplodedNode *N, const ProgramPointTag *tag = PS->getTag(); if (tag == tags.first) return VisitTrueTest(cast<Expr>(PS->getStmt()), true, - BRC, N->getLocationContext()); + BRC, BR, N); if (tag == tags.second) return VisitTrueTest(cast<Expr>(PS->getStmt()), false, - BRC, N->getLocationContext()); + BRC, BR, N); return 0; } @@ -498,6 +498,7 @@ ConditionBRVisitor::VisitTerminator(const Stmt *Term, const ExplodedNode *N, const CFGBlock *srcBlk, const CFGBlock *dstBlk, + BugReport &R, BugReporterContext &BRC) { const Expr *Cond = 0; @@ -516,14 +517,15 @@ ConditionBRVisitor::VisitTerminator(const Stmt *Term, assert(srcBlk->succ_size() == 2); const bool tookTrue = *(srcBlk->succ_begin()) == dstBlk; return VisitTrueTest(Cond->IgnoreParenNoopCasts(BRC.getASTContext()), - tookTrue, BRC, N->getLocationContext()); + tookTrue, BRC, R, N); } PathDiagnosticPiece * ConditionBRVisitor::VisitTrueTest(const Expr *Cond, bool tookTrue, BugReporterContext &BRC, - const LocationContext *LC) { + BugReport &R, + const ExplodedNode *N) { const Expr *Ex = Cond; @@ -533,9 +535,11 @@ ConditionBRVisitor::VisitTrueTest(const Expr *Cond, default: return 0; case Stmt::BinaryOperatorClass: - return VisitTrueTest(Cond, cast<BinaryOperator>(Ex), tookTrue, BRC, LC); + return VisitTrueTest(Cond, cast<BinaryOperator>(Ex), tookTrue, BRC, + R, N); case Stmt::DeclRefExprClass: - return VisitTrueTest(Cond, cast<DeclRefExpr>(Ex), tookTrue, BRC, LC); + return VisitTrueTest(Cond, cast<DeclRefExpr>(Ex), tookTrue, BRC, + R, N); case Stmt::UnaryOperatorClass: { const UnaryOperator *UO = cast<UnaryOperator>(Ex); if (UO->getOpcode() == UO_LNot) { @@ -550,14 +554,31 @@ ConditionBRVisitor::VisitTrueTest(const Expr *Cond, } bool ConditionBRVisitor::patternMatch(const Expr *Ex, llvm::raw_ostream &Out, - BugReporterContext &BRC) { + BugReporterContext &BRC, + BugReport &report, + const ExplodedNode *N, + llvm::Optional<bool> &prunable) { const Expr *OriginalExpr = Ex; Ex = Ex->IgnoreParenCasts(); if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(Ex)) { const bool quotes = isa<VarDecl>(DR->getDecl()); - if (quotes) + if (quotes) { Out << '\''; + const LocationContext *LCtx = N->getLocationContext(); + const ProgramState *state = N->getState().getPtr(); + if (const MemRegion *R = state->getLValue(cast<VarDecl>(DR->getDecl()), + LCtx).getAsRegion()) { + if (report.isInteresting(R)) + prunable = false; + else { + const ProgramState *state = N->getState().getPtr(); + SVal V = state->getSVal(R); + if (report.isInteresting(V)) + prunable = false; + } + } + } Out << DR->getDecl()->getDeclName().getAsString(); if (quotes) Out << '\''; @@ -591,31 +612,43 @@ ConditionBRVisitor::VisitTrueTest(const Expr *Cond, const BinaryOperator *BExpr, const bool tookTrue, BugReporterContext &BRC, - const LocationContext *LC) { + BugReport &R, + const ExplodedNode *N) { bool shouldInvert = false; + llvm::Optional<bool> shouldPrune; - llvm::SmallString<128> LhsString, RhsString; + 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); + llvm::raw_svector_ostream OutLHS(LhsString), OutRHS(RhsString); + const bool isVarLHS = patternMatch(BExpr->getLHS(), OutLHS, BRC, R, N, + shouldPrune); + const bool isVarRHS = patternMatch(BExpr->getRHS(), OutRHS, BRC, R, N, + shouldPrune); shouldInvert = !isVarLHS && isVarRHS; } + BinaryOperator::Opcode Op = BExpr->getOpcode(); + + if (BinaryOperator::isAssignmentOp(Op)) { + // For assignment operators, all that we care about is that the LHS + // evaluates to "true" or "false". + return VisitConditionVariable(LhsString, BExpr->getLHS(), tookTrue, + BRC, R, N); + } + + // For non-assignment operations, we require that we can understand + // both the LHS and RHS. if (LhsString.empty() || RhsString.empty()) return 0; - - // Should we invert the strings if the LHS is not a variable name? - llvm::SmallString<256> buf; + // Should we invert the strings if the LHS is not a variable name? + 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; @@ -637,7 +670,7 @@ ConditionBRVisitor::VisitTrueTest(const Expr *Cond, return 0; } - switch (BExpr->getOpcode()) { + switch (Op) { case BO_EQ: Out << "equal to "; break; @@ -650,9 +683,55 @@ ConditionBRVisitor::VisitTrueTest(const Expr *Cond, } Out << (shouldInvert ? LhsString : RhsString); + const LocationContext *LCtx = N->getLocationContext(); + PathDiagnosticLocation Loc(Cond, BRC.getSourceManager(), LCtx); + PathDiagnosticEventPiece *event = + new PathDiagnosticEventPiece(Loc, Out.str()); + if (shouldPrune.hasValue()) + event->setPrunable(shouldPrune.getValue()); + return event; +} - PathDiagnosticLocation Loc(Cond, BRC.getSourceManager(), LC); - return new PathDiagnosticEventPiece(Loc, Out.str()); +PathDiagnosticPiece * +ConditionBRVisitor::VisitConditionVariable(StringRef LhsString, + const Expr *CondVarExpr, + const bool tookTrue, + BugReporterContext &BRC, + BugReport &report, + const ExplodedNode *N) { + SmallString<256> buf; + llvm::raw_svector_ostream Out(buf); + Out << "Assuming " << LhsString << " is "; + + QualType Ty = CondVarExpr->getType(); + + if (Ty->isPointerType()) + Out << (tookTrue ? "not null" : "null"); + else if (Ty->isObjCObjectPointerType()) + Out << (tookTrue ? "not nil" : "nil"); + else if (Ty->isBooleanType()) + Out << (tookTrue ? "true" : "false"); + else if (Ty->isIntegerType()) + Out << (tookTrue ? "non-zero" : "zero"); + else + return 0; + + const LocationContext *LCtx = N->getLocationContext(); + PathDiagnosticLocation Loc(CondVarExpr, BRC.getSourceManager(), LCtx); + PathDiagnosticEventPiece *event = + new PathDiagnosticEventPiece(Loc, Out.str()); + + if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(CondVarExpr)) { + if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) { + const ProgramState *state = N->getState().getPtr(); + if (const MemRegion *R = state->getLValue(VD, LCtx).getAsRegion()) { + if (report.isInteresting(R)) + event->setPrunable(false); + } + } + } + + return event; } PathDiagnosticPiece * @@ -660,13 +739,14 @@ ConditionBRVisitor::VisitTrueTest(const Expr *Cond, const DeclRefExpr *DR, const bool tookTrue, BugReporterContext &BRC, - const LocationContext *LC) { + BugReport &report, + const ExplodedNode *N) { const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl()); if (!VD) return 0; - llvm::SmallString<256> Buf; + SmallString<256> Buf; llvm::raw_svector_ostream Out(Buf); Out << "Assuming '"; @@ -684,6 +764,21 @@ ConditionBRVisitor::VisitTrueTest(const Expr *Cond, else return 0; - PathDiagnosticLocation Loc(Cond, BRC.getSourceManager(), LC); - return new PathDiagnosticEventPiece(Loc, Out.str()); + const LocationContext *LCtx = N->getLocationContext(); + PathDiagnosticLocation Loc(Cond, BRC.getSourceManager(), LCtx); + PathDiagnosticEventPiece *event = + new PathDiagnosticEventPiece(Loc, Out.str()); + + const ProgramState *state = N->getState().getPtr(); + if (const MemRegion *R = state->getLValue(VD, LCtx).getAsRegion()) { + if (report.isInteresting(R)) + event->setPrunable(false); + else { + SVal V = state->getSVal(R); + if (report.isInteresting(V)) + event->setPrunable(false); + } + } + return event; } + diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/Checker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/Checker.cpp index a3bf2c2..07e0aac 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/Checker.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/Checker.cpp @@ -11,6 +11,7 @@ // //===----------------------------------------------------------------------===// +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" #include "clang/StaticAnalyzer/Core/Checker.h" using namespace clang; @@ -20,3 +21,11 @@ StringRef CheckerBase::getTagDescription() const { // FIXME: We want to return the package + name of the checker here. return "A Checker"; } + +void Checker<check::_VoidCheck, check::_VoidCheck, check::_VoidCheck, + check::_VoidCheck, check::_VoidCheck, check::_VoidCheck, + check::_VoidCheck, check::_VoidCheck, check::_VoidCheck, + check::_VoidCheck, check::_VoidCheck, check::_VoidCheck, + check::_VoidCheck, check::_VoidCheck, check::_VoidCheck, + check::_VoidCheck, check::_VoidCheck, check::_VoidCheck + >::anchor() { } diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/CheckerContext.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/CheckerContext.cpp index 5356edc..0a047d9 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/CheckerContext.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/CheckerContext.cpp @@ -13,21 +13,71 @@ //===----------------------------------------------------------------------===// #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "clang/Basic/Builtins.h" +#include "clang/Lex/Lexer.h" + using namespace clang; using namespace ento; -CheckerContext::~CheckerContext() { - // Do we need to autotransition? 'Dst' can get populated in a variety of - // ways, including 'addTransition()' adding the predecessor node to Dst - // without actually generated a new node. We also shouldn't autotransition - // 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 != Pred->getState()) { - static SimpleProgramPointTag autoTransitionTag("CheckerContext : auto"); - addTransition(ST, &autoTransitionTag); - } - else - Dst.Add(Pred); +const FunctionDecl *CheckerContext::getCalleeDecl(const CallExpr *CE) const { + ProgramStateRef State = getState(); + const Expr *Callee = CE->getCallee(); + SVal L = State->getSVal(Callee, Pred->getLocationContext()); + return L.getAsFunctionDecl(); +} + +StringRef CheckerContext::getCalleeName(const FunctionDecl *FunDecl) const { + if (!FunDecl) + return StringRef(); + IdentifierInfo *funI = FunDecl->getIdentifier(); + if (!funI) + return StringRef(); + return funI->getName(); +} + + +bool CheckerContext::isCLibraryFunction(const FunctionDecl *FD, + StringRef Name) { + return isCLibraryFunction(FD, Name, getASTContext()); +} + +bool CheckerContext::isCLibraryFunction(const FunctionDecl *FD, + StringRef Name, ASTContext &Context) { + // To avoid false positives (Ex: finding user defined functions with + // similar names), only perform fuzzy name matching when it's a builtin. + // Using a string compare is slow, we might want to switch on BuiltinID here. + unsigned BId = FD->getBuiltinID(); + if (BId != 0) { + StringRef BName = Context.BuiltinInfo.GetName(BId); + if (BName.find(Name) != StringRef::npos) + return true; } + + const IdentifierInfo *II = FD->getIdentifier(); + // If this is a special C++ name without IdentifierInfo, it can't be a + // C library function. + if (!II) + return false; + + StringRef FName = II->getName(); + if (FName.equals(Name)) + return true; + + if (FName.startswith("__inline") && (FName.find(Name) != StringRef::npos)) + return true; + + if (FName.startswith("__") && FName.endswith("_chk") && + FName.find(Name) != StringRef::npos) + return true; + + return false; +} + +StringRef CheckerContext::getMacroNameOrSpelling(SourceLocation &Loc) { + if (Loc.isMacroID()) + return Lexer::getImmediateMacroName(Loc, getSourceManager(), + getLangOpts()); + SmallVector<char, 16> buf; + return Lexer::getSpelling(Loc, buf, getSourceManager(), getLangOpts()); } + diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/CheckerManager.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/CheckerManager.cpp index acacfb0..0bcc343 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/CheckerManager.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/CheckerManager.cpp @@ -93,6 +93,9 @@ template <typename CHECK_CTX> static void expandGraphWithCheckers(CHECK_CTX checkCtx, ExplodedNodeSet &Dst, const ExplodedNodeSet &Src) { + const NodeBuilderContext &BldrCtx = checkCtx.Eng.getBuilderContext(); + if (Src.empty()) + return; typename CHECK_CTX::CheckersTy::const_iterator I = checkCtx.checkers_begin(), E = checkCtx.checkers_end(); @@ -113,9 +116,15 @@ static void expandGraphWithCheckers(CHECK_CTX checkCtx, CurrSet->clear(); } + NodeBuilder B(*PrevSet, *CurrSet, BldrCtx); for (ExplodedNodeSet::iterator NI = PrevSet->begin(), NE = PrevSet->end(); - NI != NE; ++NI) - checkCtx.runChecker(*I, *CurrSet, *NI); + NI != NE; ++NI) { + checkCtx.runChecker(*I, B, *NI); + } + + // If all the produced transitions are sinks, stop. + if (CurrSet->empty()) + return; // Update which NodeSet is the current one. PrevSet = CurrSet; @@ -129,23 +138,24 @@ namespace { const CheckersTy &Checkers; const Stmt *S; ExprEngine &Eng; + bool wasInlined; CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); } CheckersTy::const_iterator checkers_end() { return Checkers.end(); } CheckStmtContext(bool isPreVisit, const CheckersTy &checkers, - const Stmt *s, ExprEngine &eng) - : IsPreVisit(isPreVisit), Checkers(checkers), S(s), Eng(eng) { } + const Stmt *s, ExprEngine &eng, bool wasInlined = false) + : IsPreVisit(isPreVisit), Checkers(checkers), S(s), Eng(eng), + wasInlined(wasInlined) {} void runChecker(CheckerManager::CheckStmtFunc checkFn, - ExplodedNodeSet &Dst, ExplodedNode *Pred) { + NodeBuilder &Bldr, ExplodedNode *Pred) { // FIXME: Remove respondsToCallback from CheckerContext; 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); - + CheckerContext C(Bldr, Eng, Pred, L, wasInlined); checkFn(S, C); } }; @@ -156,9 +166,10 @@ void CheckerManager::runCheckersForStmt(bool isPreVisit, ExplodedNodeSet &Dst, const ExplodedNodeSet &Src, const Stmt *S, - ExprEngine &Eng) { + ExprEngine &Eng, + bool wasInlined) { CheckStmtContext C(isPreVisit, *getCachedStmtCheckersFor(S, isPreVisit), - S, Eng); + S, Eng, wasInlined); expandGraphWithCheckers(C, Dst, Src); } @@ -178,12 +189,14 @@ namespace { : IsPreVisit(isPreVisit), Checkers(checkers), Msg(msg), Eng(eng) { } void runChecker(CheckerManager::CheckObjCMessageFunc checkFn, - ExplodedNodeSet &Dst, ExplodedNode *Pred) { + NodeBuilder &Bldr, ExplodedNode *Pred) { 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); + const ProgramPoint &L = + ProgramPoint::getProgramPoint(Msg.getMessageExpr(), + K, Pred->getLocationContext(), + checkFn.Checker); + CheckerContext C(Bldr, Eng, Pred, L); checkFn(Msg, C); } @@ -209,35 +222,44 @@ namespace { const CheckersTy &Checkers; SVal Loc; bool IsLoad; - const Stmt *S; + const Stmt *NodeEx; /* Will become a CFGStmt */ + const Stmt *BoundEx; ExprEngine &Eng; CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); } CheckersTy::const_iterator checkers_end() { return Checkers.end(); } CheckLocationContext(const CheckersTy &checkers, - SVal loc, bool isLoad, const Stmt *s, ExprEngine &eng) - : Checkers(checkers), Loc(loc), IsLoad(isLoad), S(s), Eng(eng) { } + SVal loc, bool isLoad, const Stmt *NodeEx, + const Stmt *BoundEx, + ExprEngine &eng) + : Checkers(checkers), Loc(loc), IsLoad(isLoad), NodeEx(NodeEx), + BoundEx(BoundEx), Eng(eng) {} void runChecker(CheckerManager::CheckLocationFunc checkFn, - ExplodedNodeSet &Dst, ExplodedNode *Pred) { + NodeBuilder &Bldr, ExplodedNode *Pred) { 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); + const ProgramPoint &L = + ProgramPoint::getProgramPoint(NodeEx, K, + Pred->getLocationContext(), + checkFn.Checker); + CheckerContext C(Bldr, Eng, Pred, L); + checkFn(Loc, IsLoad, BoundEx, C); } }; } /// \brief Run checkers for load/store of a location. + void CheckerManager::runCheckersForLocation(ExplodedNodeSet &Dst, const ExplodedNodeSet &Src, SVal location, bool isLoad, - const Stmt *S, ExprEngine &Eng) { - CheckLocationContext C(LocationCheckers, location, isLoad, S, Eng); + const Stmt *NodeEx, + const Stmt *BoundEx, + ExprEngine &Eng) { + CheckLocationContext C(LocationCheckers, location, isLoad, NodeEx, + BoundEx, Eng); expandGraphWithCheckers(C, Dst, Src); } @@ -249,20 +271,21 @@ namespace { SVal Val; const Stmt *S; ExprEngine &Eng; + ProgramPoint::Kind PointKind; CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); } CheckersTy::const_iterator checkers_end() { return Checkers.end(); } CheckBindContext(const CheckersTy &checkers, - SVal loc, SVal val, const Stmt *s, ExprEngine &eng) - : Checkers(checkers), Loc(loc), Val(val), S(s), Eng(eng) { } + SVal loc, SVal val, const Stmt *s, ExprEngine &eng, + ProgramPoint::Kind PK) + : Checkers(checkers), Loc(loc), Val(val), S(s), Eng(eng), PointKind(PK) {} void runChecker(CheckerManager::CheckBindFunc checkFn, - ExplodedNodeSet &Dst, ExplodedNode *Pred) { - ProgramPoint::Kind K = ProgramPoint::PreStmtKind; - const ProgramPoint &L = ProgramPoint::getProgramPoint(S, K, + NodeBuilder &Bldr, ExplodedNode *Pred) { + const ProgramPoint &L = ProgramPoint::getProgramPoint(S, PointKind, Pred->getLocationContext(), checkFn.Checker); - CheckerContext C(Dst, Eng.getBuilder(), Eng, Pred, L, 0); + CheckerContext C(Bldr, Eng, Pred, L); checkFn(Loc, Val, S, C); } @@ -273,8 +296,9 @@ namespace { void CheckerManager::runCheckersForBind(ExplodedNodeSet &Dst, const ExplodedNodeSet &Src, SVal location, SVal val, - const Stmt *S, ExprEngine &Eng) { - CheckBindContext C(BindCheckers, location, val, S, Eng); + const Stmt *S, ExprEngine &Eng, + ProgramPoint::Kind PointKind) { + CheckBindContext C(BindCheckers, location, val, S, Eng, PointKind); expandGraphWithCheckers(C, Dst, Src); } @@ -286,27 +310,65 @@ void CheckerManager::runCheckersForEndAnalysis(ExplodedGraph &G, } /// \brief Run checkers for end of path. -void CheckerManager::runCheckersForEndPath(EndOfFunctionNodeBuilder &B, +// Note, We do not chain the checker output (like in expandGraphWithCheckers) +// for this callback since end of path nodes are expected to be final. +void CheckerManager::runCheckersForEndPath(NodeBuilderContext &BC, + ExplodedNodeSet &Dst, ExprEngine &Eng) { + ExplodedNode *Pred = BC.Pred; + + // We define the builder outside of the loop bacause if at least one checkers + // creates a sucsessor for Pred, we do not need to generate an + // autotransition for it. + NodeBuilder Bldr(Pred, Dst, BC); for (unsigned i = 0, e = EndPathCheckers.size(); i != e; ++i) { - CheckEndPathFunc fn = EndPathCheckers[i]; - EndOfFunctionNodeBuilder specialB = B.withCheckerTag(fn.Checker); - fn(specialB, Eng); + CheckEndPathFunc checkFn = EndPathCheckers[i]; + + const ProgramPoint &L = BlockEntrance(BC.Block, + Pred->getLocationContext(), + checkFn.Checker); + CheckerContext C(Bldr, Eng, Pred, L); + checkFn(C); } } +namespace { + struct CheckBranchConditionContext { + typedef std::vector<CheckerManager::CheckBranchConditionFunc> CheckersTy; + const CheckersTy &Checkers; + const Stmt *Condition; + ExprEngine &Eng; + + CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); } + CheckersTy::const_iterator checkers_end() { return Checkers.end(); } + + CheckBranchConditionContext(const CheckersTy &checkers, + const Stmt *Cond, ExprEngine &eng) + : Checkers(checkers), Condition(Cond), Eng(eng) {} + + void runChecker(CheckerManager::CheckBranchConditionFunc checkFn, + NodeBuilder &Bldr, ExplodedNode *Pred) { + ProgramPoint L = PostCondition(Condition, Pred->getLocationContext(), + checkFn.Checker); + CheckerContext C(Bldr, Eng, Pred, L); + checkFn(Condition, C); + } + }; +} + /// \brief Run checkers for branch condition. -void CheckerManager::runCheckersForBranchCondition(const Stmt *condition, - BranchNodeBuilder &B, +void CheckerManager::runCheckersForBranchCondition(const Stmt *Condition, + ExplodedNodeSet &Dst, + ExplodedNode *Pred, ExprEngine &Eng) { - for (unsigned i = 0, e = BranchConditionCheckers.size(); i != e; ++i) { - CheckBranchConditionFunc fn = BranchConditionCheckers[i]; - fn(condition, B, Eng); - } + ExplodedNodeSet Src; + Src.insert(Pred); + CheckBranchConditionContext C(BranchConditionCheckers, Condition, Eng); + expandGraphWithCheckers(C, Dst, Src); } /// \brief Run checkers for live symbols. -void CheckerManager::runCheckersForLiveSymbols(const ProgramState *state, +void CheckerManager::runCheckersForLiveSymbols(ProgramStateRef state, SymbolReaper &SymReaper) { for (unsigned i = 0, e = LiveSymbolsCheckers.size(); i != e; ++i) LiveSymbolsCheckers[i](state, SymReaper); @@ -328,11 +390,11 @@ namespace { : Checkers(checkers), SR(sr), S(s), Eng(eng) { } void runChecker(CheckerManager::CheckDeadSymbolsFunc checkFn, - ExplodedNodeSet &Dst, ExplodedNode *Pred) { + NodeBuilder &Bldr, ExplodedNode *Pred) { 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); + CheckerContext C(Bldr, Eng, Pred, L); checkFn(SR, C); } @@ -350,7 +412,7 @@ void CheckerManager::runCheckersForDeadSymbols(ExplodedNodeSet &Dst, } /// \brief True if at least one checker wants to check region changes. -bool CheckerManager::wantsRegionChangeUpdate(const ProgramState *state) { +bool CheckerManager::wantsRegionChangeUpdate(ProgramStateRef state) { for (unsigned i = 0, e = RegionChangesCheckers.size(); i != e; ++i) if (RegionChangesCheckers[i].WantUpdateFn(state)) return true; @@ -359,25 +421,26 @@ bool CheckerManager::wantsRegionChangeUpdate(const ProgramState *state) { } /// \brief Run checkers for region changes. -const ProgramState * -CheckerManager::runCheckersForRegionChanges(const ProgramState *state, +ProgramStateRef +CheckerManager::runCheckersForRegionChanges(ProgramStateRef state, const StoreManager::InvalidatedSymbols *invalidated, ArrayRef<const MemRegion *> ExplicitRegions, - ArrayRef<const MemRegion *> Regions) { + ArrayRef<const MemRegion *> Regions, + const CallOrObjCMessage *Call) { 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, - ExplicitRegions, Regions); + ExplicitRegions, Regions, Call); } return state; } /// \brief Run checkers for handling assumptions on symbolic values. -const ProgramState * -CheckerManager::runCheckersForEvalAssume(const ProgramState *state, +ProgramStateRef +CheckerManager::runCheckersForEvalAssume(ProgramStateRef 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), @@ -437,11 +500,12 @@ void CheckerManager::runCheckersForEvalCall(ExplodedNodeSet &Dst, } #endif + ExplodedNodeSet checkDst; + NodeBuilder B(Pred, checkDst, Eng.getBuilderContext()); // 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; ProgramPoint::Kind K = ProgramPoint::PostStmtKind; const ProgramPoint &L = ProgramPoint::getProgramPoint(CE, K, Pred->getLocationContext(), EI->Checker); @@ -449,7 +513,7 @@ void CheckerManager::runCheckersForEvalCall(ExplodedNodeSet &Dst, { // 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); + CheckerContext C(B, Eng, Pred, L); evaluated = (*EI)(CE, C); } assert(!(evaluated && anyEvaluated) @@ -483,7 +547,7 @@ void CheckerManager::runCheckersOnEndOfTranslationUnit( } void CheckerManager::runCheckersForPrintState(raw_ostream &Out, - const ProgramState *State, + ProgramStateRef State, const char *NL, const char *Sep) { for (llvm::DenseMap<CheckerTag, CheckerRef>::iterator I = CheckerTags.begin(), E = CheckerTags.end(); I != E; ++I) diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/CheckerRegistry.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/CheckerRegistry.cpp index 13401ac..9791e2ec 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/CheckerRegistry.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/CheckerRegistry.cpp @@ -9,12 +9,13 @@ #include "clang/StaticAnalyzer/Core/CheckerRegistry.h" #include "clang/StaticAnalyzer/Core/CheckerOptInfo.h" +#include "llvm/ADT/SetVector.h" using namespace clang; using namespace ento; static const char PackageSeparator = '.'; -typedef llvm::DenseSet<const CheckerRegistry::CheckerInfo *> CheckerInfoSet; +typedef llvm::SetVector<const CheckerRegistry::CheckerInfo *> CheckerInfoSet; static bool checkerNameLT(const CheckerRegistry::CheckerInfo &a, @@ -72,7 +73,7 @@ static void collectCheckers(const CheckerRegistry::CheckerInfoList &checkers, if (opt.isEnabled()) collected.insert(&*i); else - collected.erase(&*i); + collected.remove(&*i); } } diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/CoreEngine.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/CoreEngine.cpp index 5252198..eb986af 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/CoreEngine.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/CoreEngine.cpp @@ -12,6 +12,8 @@ // //===----------------------------------------------------------------------===// +#define DEBUG_TYPE "CoreEngine" + #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" @@ -20,9 +22,16 @@ #include "clang/AST/StmtCXX.h" #include "llvm/Support/Casting.h" #include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/Statistic.h" + using namespace clang; using namespace ento; +STATISTIC(NumReachedMaxSteps, + "The # of times we reached the max number of steps."); +STATISTIC(NumPathsExplored, + "The # of paths explored by the analyzer."); + //===----------------------------------------------------------------------===// // Worklist classes for exploration of reachable states. //===----------------------------------------------------------------------===// @@ -152,7 +161,7 @@ WorkList* WorkList::makeBFSBlockDFSContents() { /// ExecuteWorkList - Run the worklist algorithm for a maximum number of steps. bool CoreEngine::ExecuteWorkList(const LocationContext *L, unsigned Steps, - const ProgramState *InitState) { + ProgramStateRef InitState) { if (G->num_roots() == 0) { // Initialize the analysis by constructing // the root if none exists. @@ -165,6 +174,11 @@ bool CoreEngine::ExecuteWorkList(const LocationContext *L, unsigned Steps, assert (Entry->succ_size() == 1 && "Entry block must have 1 successor."); + // Mark the entry block as visited. + FunctionSummaries->markVisitedBasicBlock(Entry->getBlockID(), + L->getDecl(), + L->getCFG()->getNumBlockIDs()); + // Get the solitary successor. const CFGBlock *Succ = *(Entry->succ_begin()); @@ -187,8 +201,10 @@ bool CoreEngine::ExecuteWorkList(const LocationContext *L, unsigned Steps, while (WList->hasWork()) { if (!UnlimitedSteps) { - if (Steps == 0) + if (Steps == 0) { + NumReachedMaxSteps++; break; + } --Steps; } @@ -200,67 +216,80 @@ bool CoreEngine::ExecuteWorkList(const LocationContext *L, unsigned Steps, // Retrieve the node. ExplodedNode *Node = WU.getNode(); - // Dispatch on the location type. - switch (Node->getLocation().getKind()) { - case ProgramPoint::BlockEdgeKind: - HandleBlockEdge(cast<BlockEdge>(Node->getLocation()), Node); - break; - - case ProgramPoint::BlockEntranceKind: - HandleBlockEntrance(cast<BlockEntrance>(Node->getLocation()), Node); - break; - - case ProgramPoint::BlockExitKind: - assert (false && "BlockExit location never occur in forward analysis."); - break; + dispatchWorkItem(Node, Node->getLocation(), WU); + } + SubEng.processEndWorklist(hasWorkRemaining()); + return WList->hasWork(); +} - case ProgramPoint::CallEnterKind: - HandleCallEnter(cast<CallEnter>(Node->getLocation()), WU.getBlock(), - WU.getIndex(), Node); - break; +void CoreEngine::dispatchWorkItem(ExplodedNode* Pred, ProgramPoint Loc, + const WorkListUnit& WU) { + // Dispatch on the location type. + switch (Loc.getKind()) { + case ProgramPoint::BlockEdgeKind: + HandleBlockEdge(cast<BlockEdge>(Loc), Pred); + break; + + case ProgramPoint::BlockEntranceKind: + HandleBlockEntrance(cast<BlockEntrance>(Loc), Pred); + break; + + case ProgramPoint::BlockExitKind: + assert (false && "BlockExit location never occur in forward analysis."); + break; + + case ProgramPoint::CallEnterKind: { + CallEnter CEnter = cast<CallEnter>(Loc); + if (AnalyzedCallees) + if (const CallExpr* CE = + dyn_cast_or_null<CallExpr>(CEnter.getCallExpr())) + if (const Decl *CD = CE->getCalleeDecl()) + AnalyzedCallees->insert(CD); + SubEng.processCallEnter(CEnter, Pred); + break; + } - case ProgramPoint::CallExitKind: - HandleCallExit(cast<CallExit>(Node->getLocation()), Node); - break; + case ProgramPoint::CallExitKind: + SubEng.processCallExit(Pred); + break; - default: - assert(isa<PostStmt>(Node->getLocation()) || - isa<PostInitializer>(Node->getLocation())); - HandlePostStmt(WU.getBlock(), WU.getIndex(), Node); - break; + case ProgramPoint::EpsilonKind: { + assert(Pred->hasSinglePred() && + "Assume epsilon has exactly one predecessor by construction"); + ExplodedNode *PNode = Pred->getFirstPred(); + dispatchWorkItem(Pred, PNode->getLocation(), WU); + break; } + default: + assert(isa<PostStmt>(Loc) || + isa<PostInitializer>(Loc)); + HandlePostStmt(WU.getBlock(), WU.getIndex(), Pred); + break; } - - SubEng.processEndWorklist(hasWorkRemaining()); - return WList->hasWork(); } -void CoreEngine::ExecuteWorkListWithInitialState(const LocationContext *L, +bool CoreEngine::ExecuteWorkListWithInitialState(const LocationContext *L, unsigned Steps, - const ProgramState *InitState, + ProgramStateRef InitState, ExplodedNodeSet &Dst) { - ExecuteWorkList(L, Steps, InitState); - for (SmallVectorImpl<ExplodedNode*>::iterator I = G->EndNodes.begin(), - E = G->EndNodes.end(); I != E; ++I) { + bool DidNotFinish = ExecuteWorkList(L, Steps, InitState); + for (ExplodedGraph::eop_iterator I = G->eop_begin(), + E = G->eop_end(); I != E; ++I) { Dst.Add(*I); } -} - -void CoreEngine::HandleCallEnter(const CallEnter &L, const CFGBlock *Block, - unsigned Index, ExplodedNode *Pred) { - CallEnterNodeBuilder Builder(*this, Pred, L.getCallExpr(), - L.getCalleeContext(), Block, Index); - SubEng.processCallEnter(Builder); -} - -void CoreEngine::HandleCallExit(const CallExit &L, ExplodedNode *Pred) { - CallExitNodeBuilder Builder(*this, Pred); - SubEng.processCallExit(Builder); + return DidNotFinish; } void CoreEngine::HandleBlockEdge(const BlockEdge &L, ExplodedNode *Pred) { const CFGBlock *Blk = L.getDst(); + NodeBuilderContext BuilderCtx(*this, Blk, Pred); + + // Mark this block as visited. + const LocationContext *LC = Pred->getLocationContext(); + FunctionSummaries->markVisitedBasicBlock(Blk->getBlockID(), + LC->getDecl(), + LC->getCFG()->getNumBlockIDs()); // Check if we are entering the EXIT block. if (Blk == &(L.getLocationContext()->getCFG()->getExit())) { @@ -269,53 +298,42 @@ void CoreEngine::HandleBlockEdge(const BlockEdge &L, ExplodedNode *Pred) { && "EXIT block cannot contain Stmts."); // Process the final state transition. - EndOfFunctionNodeBuilder Builder(Blk, Pred, this); - SubEng.processEndOfFunction(Builder); + SubEng.processEndOfFunction(BuilderCtx); // This path is done. Don't enqueue any more nodes. return; } - // Call into the subengine to process entering the CFGBlock. + // Call into the SubEngine to process entering the CFGBlock. ExplodedNodeSet dstNodes; BlockEntrance BE(Blk, Pred->getLocationContext()); - GenericNodeBuilder<BlockEntrance> nodeBuilder(*this, Pred, BE); - SubEng.processCFGBlockEntrance(dstNodes, nodeBuilder); + NodeBuilderWithSinks nodeBuilder(Pred, dstNodes, BuilderCtx, BE); + SubEng.processCFGBlockEntrance(L, nodeBuilder); - if (dstNodes.empty()) { - if (!nodeBuilder.hasGeneratedNode) { - // Auto-generate a node and enqueue it to the worklist. - generateNode(BE, Pred->State, Pred); - } - } - else { - for (ExplodedNodeSet::iterator I = dstNodes.begin(), E = dstNodes.end(); - I != E; ++I) { - WList->enqueue(*I); - } + // Auto-generate a node. + if (!nodeBuilder.hasGeneratedNodes()) { + nodeBuilder.generateNode(Pred->State, Pred); } - for (SmallVectorImpl<ExplodedNode*>::const_iterator - I = nodeBuilder.sinks().begin(), E = nodeBuilder.sinks().end(); - I != E; ++I) { - blocksExhausted.push_back(std::make_pair(L, *I)); - } + // Enqueue nodes onto the worklist. + enqueue(dstNodes); } void CoreEngine::HandleBlockEntrance(const BlockEntrance &L, ExplodedNode *Pred) { // Increment the block counter. + const LocationContext *LC = Pred->getLocationContext(); + unsigned BlockId = L.getBlock()->getBlockID(); BlockCounter Counter = WList->getBlockCounter(); - Counter = BCounterFactory.IncrementCount(Counter, - Pred->getLocationContext()->getCurrentStackFrame(), - L.getBlock()->getBlockID()); + Counter = BCounterFactory.IncrementCount(Counter, LC->getCurrentStackFrame(), + BlockId); WList->setBlockCounter(Counter); // Process the entrance of the block. if (CFGElement E = L.getFirstElement()) { - StmtNodeBuilder Builder(L.getBlock(), 0, Pred, this); - SubEng.processCFGElement(E, Builder); + NodeBuilderContext Ctx(*this, L.getBlock(), Pred); + SubEng.processCFGElement(E, Pred, 0, &Ctx); } else HandleBlockExit(L.getBlock(), Pred); @@ -345,6 +363,19 @@ void CoreEngine::HandleBlockExit(const CFGBlock * B, ExplodedNode *Pred) { HandleBranch(cast<ChooseExpr>(Term)->getCond(), Term, B, Pred); return; + case Stmt::CXXTryStmtClass: { + // Generate a node for each of the successors. + // Our logic for EH analysis can certainly be improved. + for (CFGBlock::const_succ_iterator it = B->succ_begin(), + et = B->succ_end(); it != et; ++it) { + if (const CFGBlock *succ = *it) { + generateNode(BlockEdge(B, succ, Pred->getLocationContext()), + Pred->State, Pred); + } + } + return; + } + case Stmt::DoStmtClass: HandleBranch(cast<DoStmt>(Term)->getCond(), Term, B, Pred); return; @@ -417,31 +448,35 @@ void CoreEngine::HandleBlockExit(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); + NodeBuilderContext Ctx(*this, B, Pred); + ExplodedNodeSet Dst; + SubEng.processBranch(Cond, Term, Ctx, Pred, Dst, + *(B->succ_begin()), *(B->succ_begin()+1)); + // Enqueue the new frontier onto the worklist. + enqueue(Dst); } void CoreEngine::HandlePostStmt(const CFGBlock *B, unsigned StmtIdx, ExplodedNode *Pred) { - assert (!B->empty()); + assert(B); + assert(!B->empty()); if (StmtIdx == B->size()) HandleBlockExit(B, Pred); else { - StmtNodeBuilder Builder(B, StmtIdx, Pred, this); - SubEng.processCFGElement((*B)[StmtIdx], Builder); + NodeBuilderContext Ctx(*this, B, Pred); + SubEng.processCFGElement((*B)[StmtIdx], Pred, StmtIdx, &Ctx); } } /// generateNode - Utility method to generate nodes, hook up successors, /// and add nodes to the worklist. void CoreEngine::generateNode(const ProgramPoint &Loc, - const ProgramState *State, + ProgramStateRef State, ExplodedNode *Pred) { bool IsNew; - ExplodedNode *Node = G->getNode(Loc, State, &IsNew); + ExplodedNode *Node = G->getNode(Loc, State, false, &IsNew); if (Pred) Node->addPredecessor(Pred, *G); // Link 'Node' with its predecessor. @@ -454,225 +489,181 @@ void CoreEngine::generateNode(const ProgramPoint &Loc, if (IsNew) WList->enqueue(Node); } -ExplodedNode * -GenericNodeBuilderImpl::generateNodeImpl(const ProgramState *state, - ExplodedNode *pred, - ProgramPoint programPoint, - bool asSink) { - - hasGeneratedNode = true; - bool isNew; - ExplodedNode *node = engine.getGraph().getNode(programPoint, state, &isNew); - if (pred) - node->addPredecessor(pred, engine.getGraph()); - if (isNew) { - if (asSink) { - node->markAsSink(); - sinksGenerated.push_back(node); - } - return node; - } - return 0; -} - -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); -} - -StmtNodeBuilder::~StmtNodeBuilder() { - for (DeferredTy::iterator I=Deferred.begin(), E=Deferred.end(); I!=E; ++I) - if (!(*I)->isSink()) - GenerateAutoTransition(*I); -} - -void StmtNodeBuilder::GenerateAutoTransition(ExplodedNode *N) { +void CoreEngine::enqueueStmtNode(ExplodedNode *N, + const CFGBlock *Block, unsigned Idx) { + assert(Block); assert (!N->isSink()); // Check if this node entered a callee. if (isa<CallEnter>(N->getLocation())) { // Still use the index of the CallExpr. It's needed to create the callee // StackFrameContext. - Eng.WList->enqueue(N, &B, Idx); + WList->enqueue(N, Block, Idx); return; } // Do not create extra nodes. Move to the next CFG element. if (isa<PostInitializer>(N->getLocation())) { - Eng.WList->enqueue(N, &B, Idx+1); + WList->enqueue(N, Block, Idx+1); return; } - PostStmt Loc(getStmt(), N->getLocationContext()); + if (isa<EpsilonPoint>(N->getLocation())) { + WList->enqueue(N, Block, Idx); + return; + } + + const CFGStmt *CS = (*Block)[Idx].getAs<CFGStmt>(); + const Stmt *St = CS ? CS->getStmt() : 0; + PostStmt Loc(St, N->getLocationContext()); if (Loc == N->getLocation()) { // Note: 'N' should be a fresh node because otherwise it shouldn't be // a member of Deferred. - Eng.WList->enqueue(N, &B, Idx+1); + WList->enqueue(N, Block, Idx+1); return; } bool IsNew; - ExplodedNode *Succ = Eng.G->getNode(Loc, N->State, &IsNew); - Succ->addPredecessor(N, *Eng.G); + ExplodedNode *Succ = G->getNode(Loc, N->getState(), false, &IsNew); + Succ->addPredecessor(N, *G); if (IsNew) - Eng.WList->enqueue(Succ, &B, Idx+1); + WList->enqueue(Succ, Block, Idx+1); } -ExplodedNode *StmtNodeBuilder::MakeNode(ExplodedNodeSet &Dst, - const Stmt *S, - ExplodedNode *Pred, - const ProgramState *St, - ProgramPoint::Kind K) { - - ExplodedNode *N = generateNode(S, St, Pred, K); +ExplodedNode *CoreEngine::generateCallExitNode(ExplodedNode *N) { + // Create a CallExit node and enqueue it. + const StackFrameContext *LocCtx + = cast<StackFrameContext>(N->getLocationContext()); + const Stmt *CE = LocCtx->getCallSite(); - if (N) { - if (BuildSinks) - N->markAsSink(); - else - Dst.Add(N); - } - - return N; -} + // Use the the callee location context. + CallExit Loc(CE, LocCtx); -ExplodedNode* -StmtNodeBuilder::generateNodeInternal(const Stmt *S, - const ProgramState *state, - ExplodedNode *Pred, - ProgramPoint::Kind K, - const ProgramPointTag *tag) { - - const ProgramPoint &L = ProgramPoint::getProgramPoint(S, K, - Pred->getLocationContext(), tag); - return generateNodeInternal(L, state, Pred); + bool isNew; + ExplodedNode *Node = G->getNode(Loc, N->getState(), false, &isNew); + Node->addPredecessor(N, *G); + return isNew ? Node : 0; } -ExplodedNode* -StmtNodeBuilder::generateNodeInternal(const ProgramPoint &Loc, - const ProgramState *State, - ExplodedNode *Pred) { - bool IsNew; - ExplodedNode *N = Eng.G->getNode(Loc, State, &IsNew); - N->addPredecessor(Pred, *Eng.G); - Deferred.erase(Pred); - if (IsNew) { - Deferred.insert(N); - return N; +void CoreEngine::enqueue(ExplodedNodeSet &Set) { + for (ExplodedNodeSet::iterator I = Set.begin(), + E = Set.end(); I != E; ++I) { + WList->enqueue(*I); } +} - return NULL; +void CoreEngine::enqueue(ExplodedNodeSet &Set, + const CFGBlock *Block, unsigned Idx) { + for (ExplodedNodeSet::iterator I = Set.begin(), + E = Set.end(); I != E; ++I) { + enqueueStmtNode(*I, Block, Idx); + } } -// This function generate a new ExplodedNode but not a new branch(block edge). -ExplodedNode *BranchNodeBuilder::generateNode(const Stmt *Condition, - const ProgramState *State) { - bool IsNew; - - ExplodedNode *Succ - = Eng.G->getNode(PostCondition(Condition, Pred->getLocationContext()), State, - &IsNew); - - Succ->addPredecessor(Pred, *Eng.G); - - Pred = Succ; - - if (IsNew) - return Succ; - - return NULL; +void CoreEngine::enqueueEndOfFunction(ExplodedNodeSet &Set) { + for (ExplodedNodeSet::iterator I = Set.begin(), E = Set.end(); I != E; ++I) { + ExplodedNode *N = *I; + // If we are in an inlined call, generate CallExit node. + if (N->getLocationContext()->getParent()) { + N = generateCallExitNode(N); + if (N) + WList->enqueue(N); + } else { + G->addEndOfPath(N); + NumPathsExplored++; + } + } } -ExplodedNode *BranchNodeBuilder::generateNode(const ProgramState *State, - bool branch) { - // If the branch has been marked infeasible we should not generate a node. - if (!isFeasible(branch)) - return NULL; +void NodeBuilder::anchor() { } +ExplodedNode* NodeBuilder::generateNodeImpl(const ProgramPoint &Loc, + ProgramStateRef State, + ExplodedNode *FromN, + bool MarkAsSink) { + HasGeneratedNodes = true; bool IsNew; + ExplodedNode *N = C.Eng.G->getNode(Loc, State, MarkAsSink, &IsNew); + N->addPredecessor(FromN, *C.Eng.G); + Frontier.erase(FromN); - ExplodedNode *Succ = - Eng.G->getNode(BlockEdge(Src,branch ? DstT:DstF,Pred->getLocationContext()), - State, &IsNew); + if (!IsNew) + return 0; - Succ->addPredecessor(Pred, *Eng.G); + if (!MarkAsSink) + Frontier.Add(N); - if (branch) - GeneratedTrue = true; - else - GeneratedFalse = true; + return N; +} - if (IsNew) { - Deferred.push_back(Succ); - return Succ; - } +void NodeBuilderWithSinks::anchor() { } - return NULL; +StmtNodeBuilder::~StmtNodeBuilder() { + if (EnclosingBldr) + for (ExplodedNodeSet::iterator I = Frontier.begin(), + E = Frontier.end(); I != E; ++I ) + EnclosingBldr->addNodes(*I); } -BranchNodeBuilder::~BranchNodeBuilder() { - if (!GeneratedTrue) generateNode(Pred->State, true); - if (!GeneratedFalse) generateNode(Pred->State, false); +void BranchNodeBuilder::anchor() { } - for (DeferredTy::iterator I=Deferred.begin(), E=Deferred.end(); I!=E; ++I) - if (!(*I)->isSink()) Eng.WList->enqueue(*I); -} +ExplodedNode *BranchNodeBuilder::generateNode(ProgramStateRef State, + bool branch, + ExplodedNode *NodePred) { + // If the branch has been marked infeasible we should not generate a node. + if (!isFeasible(branch)) + return NULL; + ProgramPoint Loc = BlockEdge(C.Block, branch ? DstT:DstF, + NodePred->getLocationContext()); + ExplodedNode *Succ = generateNodeImpl(Loc, State, NodePred); + return Succ; +} ExplodedNode* IndirectGotoNodeBuilder::generateNode(const iterator &I, - const ProgramState *St, - bool isSink) { + ProgramStateRef St, + bool IsSink) { bool IsNew; - ExplodedNode *Succ = Eng.G->getNode(BlockEdge(Src, I.getBlock(), - Pred->getLocationContext()), St, &IsNew); - + Pred->getLocationContext()), St, + IsSink, &IsNew); Succ->addPredecessor(Pred, *Eng.G); - if (IsNew) { + if (!IsNew) + return 0; - if (isSink) - Succ->markAsSink(); - else - Eng.WList->enqueue(Succ); - - return Succ; - } + if (!IsSink) + Eng.WList->enqueue(Succ); - return NULL; + return Succ; } ExplodedNode* SwitchNodeBuilder::generateCaseStmtNode(const iterator &I, - const ProgramState *St) { + ProgramStateRef St) { bool IsNew; ExplodedNode *Succ = Eng.G->getNode(BlockEdge(Src, I.getBlock(), - Pred->getLocationContext()), - St, &IsNew); + Pred->getLocationContext()), St, + false, &IsNew); Succ->addPredecessor(Pred, *Eng.G); - if (IsNew) { - Eng.WList->enqueue(Succ); - return Succ; - } - return NULL; + if (!IsNew) + return 0; + + Eng.WList->enqueue(Succ); + return Succ; } ExplodedNode* -SwitchNodeBuilder::generateDefaultCaseNode(const ProgramState *St, - bool isSink) { +SwitchNodeBuilder::generateDefaultCaseNode(ProgramStateRef St, + bool IsSink) { // Get the block for the default case. assert(Src->succ_rbegin() != Src->succ_rend()); CFGBlock *DefaultBlock = *Src->succ_rbegin(); @@ -683,145 +674,16 @@ SwitchNodeBuilder::generateDefaultCaseNode(const ProgramState *St, return NULL; bool IsNew; - ExplodedNode *Succ = Eng.G->getNode(BlockEdge(Src, DefaultBlock, - Pred->getLocationContext()), St, &IsNew); + Pred->getLocationContext()), St, + IsSink, &IsNew); Succ->addPredecessor(Pred, *Eng.G); - if (IsNew) { - if (isSink) - Succ->markAsSink(); - else - Eng.WList->enqueue(Succ); - - return Succ; - } - - return NULL; -} - -EndOfFunctionNodeBuilder::~EndOfFunctionNodeBuilder() { - // Auto-generate an EOP node if one has not been generated. - if (!hasGeneratedNode) { - // If we are in an inlined call, generate CallExit node. - if (Pred->getLocationContext()->getParent()) - GenerateCallExitNode(Pred->State); - else - generateNode(Pred->State); - } -} - -ExplodedNode* -EndOfFunctionNodeBuilder::generateNode(const ProgramState *State, - ExplodedNode *P, - const ProgramPointTag *tag) { - hasGeneratedNode = true; - bool IsNew; - - ExplodedNode *Node = Eng.G->getNode(BlockEntrance(&B, - Pred->getLocationContext(), tag ? tag : Tag), - State, &IsNew); - - Node->addPredecessor(P ? P : Pred, *Eng.G); - - if (IsNew) { - Eng.G->addEndOfPath(Node); - return Node; - } - - return NULL; -} - -void EndOfFunctionNodeBuilder::GenerateCallExitNode(const ProgramState *state) { - hasGeneratedNode = true; - // Create a CallExit node and enqueue it. - const StackFrameContext *LocCtx - = cast<StackFrameContext>(Pred->getLocationContext()); - const Stmt *CE = LocCtx->getCallSite(); + if (!IsNew) + return 0; - // Use the the callee location context. - CallExit Loc(CE, LocCtx); - - bool isNew; - ExplodedNode *Node = Eng.G->getNode(Loc, state, &isNew); - Node->addPredecessor(Pred, *Eng.G); - - if (isNew) - Eng.WList->enqueue(Node); -} - - -void CallEnterNodeBuilder::generateNode(const ProgramState *state) { - // Check if the callee is in the same translation unit. - if (CalleeCtx->getTranslationUnit() != - Pred->getLocationContext()->getTranslationUnit()) { - // Create a new engine. We must be careful that the new engine should not - // reference data structures owned by the old engine. - - AnalysisManager &OldMgr = Eng.SubEng.getAnalysisManager(); - - // Get the callee's translation unit. - idx::TranslationUnit *TU = CalleeCtx->getTranslationUnit(); - - // 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); - - // Create the new engine. - // FIXME: This cast isn't really safe. - bool GCEnabled = static_cast<ExprEngine&>(Eng.SubEng).isObjCGCEnabled(); - ExprEngine NewEng(AMgr, GCEnabled); - - // Create the new LocationContext. - AnalysisContext *NewAnaCtx = AMgr.getAnalysisContext(CalleeCtx->getDecl(), - CalleeCtx->getTranslationUnit()); - const StackFrameContext *OldLocCtx = CalleeCtx; - const StackFrameContext *NewLocCtx = AMgr.getStackFrame(NewAnaCtx, - OldLocCtx->getParent(), - OldLocCtx->getCallSite(), - OldLocCtx->getCallSiteBlock(), - OldLocCtx->getIndex()); - - // Now create an initial state for the new engine. - const ProgramState *NewState = - NewEng.getStateManager().MarshalState(state, NewLocCtx); - ExplodedNodeSet ReturnNodes; - NewEng.ExecuteWorkListWithInitialState(NewLocCtx, AMgr.getMaxNodes(), - NewState, ReturnNodes); - return; - } - - // Get the callee entry block. - const CFGBlock *Entry = &(CalleeCtx->getCFG()->getEntry()); - assert(Entry->empty()); - assert(Entry->succ_size() == 1); - - // Get the solitary successor. - const CFGBlock *SuccB = *(Entry->succ_begin()); - - // Construct an edge representing the starting location in the callee. - BlockEdge Loc(Entry, SuccB, CalleeCtx); - - bool isNew; - ExplodedNode *Node = Eng.G->getNode(Loc, state, &isNew); - Node->addPredecessor(const_cast<ExplodedNode*>(Pred), *Eng.G); - - if (isNew) - Eng.WList->enqueue(Node); -} + if (!IsSink) + Eng.WList->enqueue(Succ); -void CallExitNodeBuilder::generateNode(const ProgramState *state) { - // Get the callee's location context. - const StackFrameContext *LocCtx - = cast<StackFrameContext>(Pred->getLocationContext()); - // When exiting an implicit automatic obj dtor call, the callsite is the Stmt - // that triggers the dtor. - PostStmt Loc(LocCtx->getCallSite(), LocCtx->getParent()); - bool isNew; - ExplodedNode *Node = Eng.G->getNode(Loc, state, &isNew); - Node->addPredecessor(const_cast<ExplodedNode*>(Pred), *Eng.G); - if (isNew) - Eng.WList->enqueue(Node, LocCtx->getCallSiteBlock(), - LocCtx->getIndex() + 1); + return Succ; } diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/Environment.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/Environment.cpp index e1b982c..b5ea3db 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/Environment.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/Environment.cpp @@ -11,6 +11,7 @@ // //===----------------------------------------------------------------------===// +#include "clang/AST/ExprCXX.h" #include "clang/AST/ExprObjC.h" #include "clang/Analysis/AnalysisContext.h" #include "clang/Analysis/CFG.h" @@ -19,7 +20,7 @@ using namespace clang; using namespace ento; -SVal Environment::lookupExpr(const Stmt *E) const { +SVal Environment::lookupExpr(const EnvironmentEntry &E) const { const SVal* X = ExprBindings.lookup(E); if (X) { SVal V = *X; @@ -28,17 +29,21 @@ SVal Environment::lookupExpr(const Stmt *E) const { return UnknownVal(); } -SVal Environment::getSVal(const Stmt *E, SValBuilder& svalBuilder, - bool useOnlyDirectBindings) const { +SVal Environment::getSVal(const EnvironmentEntry &Entry, + SValBuilder& svalBuilder, + bool useOnlyDirectBindings) const { if (useOnlyDirectBindings) { // This branch is rarely taken, but can be exercised by // checkers that explicitly bind values to arbitrary // expressions. It is crucial that we do not ignore any // expression here, and do a direct lookup. - return lookupExpr(E); + return lookupExpr(Entry); } + const Stmt *E = Entry.getStmt(); + const LocationContext *LCtx = Entry.getLocationContext(); + for (;;) { if (const Expr *Ex = dyn_cast<Expr>(E)) E = Ex->IgnoreParens(); @@ -55,13 +60,12 @@ SVal Environment::getSVal(const Stmt *E, SValBuilder& svalBuilder, case Stmt::GenericSelectionExprClass: llvm_unreachable("ParenExprs and GenericSelectionExprs should " "have been handled by IgnoreParens()"); - return UnknownVal(); case Stmt::CharacterLiteralClass: { const CharacterLiteral* C = cast<CharacterLiteral>(E); return svalBuilder.makeIntVal(C->getValue(), C->getType()); } case Stmt::CXXBoolLiteralExprClass: { - const SVal *X = ExprBindings.lookup(E); + const SVal *X = ExprBindings.lookup(EnvironmentEntry(E, LCtx)); if (X) return *X; else @@ -69,12 +73,15 @@ SVal Environment::getSVal(const Stmt *E, SValBuilder& svalBuilder, } case Stmt::IntegerLiteralClass: { // In C++, this expression may have been bound to a temporary object. - SVal const *X = ExprBindings.lookup(E); + SVal const *X = ExprBindings.lookup(EnvironmentEntry(E, LCtx)); if (X) return *X; else return svalBuilder.makeIntVal(cast<IntegerLiteral>(E)); } + case Stmt::ObjCBoolLiteralExprClass: + return svalBuilder.makeBoolVal(cast<ObjCBoolLiteralExpr>(E)); + // For special C0xx nullptr case, make a null pointer SVal. case Stmt::CXXNullPtrLiteralExprClass: return svalBuilder.makeNull(); @@ -86,6 +93,24 @@ SVal Environment::getSVal(const Stmt *E, SValBuilder& svalBuilder, continue; case Stmt::ObjCPropertyRefExprClass: return loc::ObjCPropRef(cast<ObjCPropertyRefExpr>(E)); + case Stmt::ObjCStringLiteralClass: { + MemRegionManager &MRMgr = svalBuilder.getRegionManager(); + const ObjCStringLiteral *SL = cast<ObjCStringLiteral>(E); + return svalBuilder.makeLoc(MRMgr.getObjCStringRegion(SL)); + } + case Stmt::StringLiteralClass: { + MemRegionManager &MRMgr = svalBuilder.getRegionManager(); + const StringLiteral *SL = cast<StringLiteral>(E); + return svalBuilder.makeLoc(MRMgr.getStringRegion(SL)); + } + case Stmt::ReturnStmtClass: { + const ReturnStmt *RS = cast<ReturnStmt>(E); + if (const Expr *RE = RS->getRetValue()) { + E = RE; + continue; + } + return UndefinedVal(); + } // Handle all other Stmt* using a lookup. default: @@ -93,32 +118,33 @@ SVal Environment::getSVal(const Stmt *E, SValBuilder& svalBuilder, }; break; } - return lookupExpr(E); + return lookupExpr(EnvironmentEntry(E, LCtx)); } -Environment EnvironmentManager::bindExpr(Environment Env, const Stmt *S, - SVal V, bool Invalidate) { - assert(S); - +Environment EnvironmentManager::bindExpr(Environment Env, + const EnvironmentEntry &E, + SVal V, + bool Invalidate) { if (V.isUnknown()) { if (Invalidate) - return Environment(F.remove(Env.ExprBindings, S)); + return Environment(F.remove(Env.ExprBindings, E)); else return Env; } - - return Environment(F.add(Env.ExprBindings, S, V)); + return Environment(F.add(Env.ExprBindings, E, V)); } -static inline const Stmt *MakeLocation(const Stmt *S) { - return (const Stmt*) (((uintptr_t) S) | 0x1); +static inline EnvironmentEntry MakeLocation(const EnvironmentEntry &E) { + const Stmt *S = E.getStmt(); + S = (const Stmt*) (((uintptr_t) S) | 0x1); + return EnvironmentEntry(S, E.getLocationContext()); } Environment EnvironmentManager::bindExprAndLocation(Environment Env, - const Stmt *S, + const EnvironmentEntry &E, SVal location, SVal V) { - return Environment(F.add(F.add(Env.ExprBindings, MakeLocation(S), location), - S, V)); + return Environment(F.add(F.add(Env.ExprBindings, MakeLocation(E), location), + E, V)); } namespace { @@ -126,14 +152,22 @@ class MarkLiveCallback : public SymbolVisitor { SymbolReaper &SymReaper; public: MarkLiveCallback(SymbolReaper &symreaper) : SymReaper(symreaper) {} - bool VisitSymbol(SymbolRef sym) { SymReaper.markLive(sym); return true; } + bool VisitSymbol(SymbolRef sym) { + SymReaper.markLive(sym); + return true; + } + bool VisitMemRegion(const MemRegion *R) { + SymReaper.markLive(R); + return true; + } }; } // end anonymous namespace -// In addition to mapping from Stmt * - > SVals in the Environment, we also -// maintain a mapping from Stmt * -> SVals (locations) that were used during -// a load and store. -static inline bool IsLocation(const Stmt *S) { +// In addition to mapping from EnvironmentEntry - > SVals in the Environment, +// we also maintain a mapping from EnvironmentEntry -> SVals (locations) +// that were used during a load and store. +static inline bool IsLocation(const EnvironmentEntry &E) { + const Stmt *S = E.getStmt(); return (bool) (((uintptr_t) S) & 0x1); } @@ -147,19 +181,19 @@ static inline bool IsLocation(const Stmt *S) { Environment EnvironmentManager::removeDeadBindings(Environment Env, SymbolReaper &SymReaper, - const ProgramState *ST) { + ProgramStateRef ST) { // We construct a new Environment object entirely, as this is cheaper than // individually removing all the subexpression bindings (which will greatly // outnumber block-level expression bindings). Environment NewEnv = getInitialEnvironment(); - SmallVector<std::pair<const Stmt*, SVal>, 10> deferredLocations; + SmallVector<std::pair<EnvironmentEntry, SVal>, 10> deferredLocations; MarkLiveCallback CB(SymReaper); ScanReachableSymbols RSScaner(ST, CB); - llvm::ImmutableMapRef<const Stmt*,SVal> + llvm::ImmutableMapRef<EnvironmentEntry,SVal> EBMapRef(NewEnv.ExprBindings.getRootWithoutRetain(), F.getTreeFactory()); @@ -167,7 +201,7 @@ EnvironmentManager::removeDeadBindings(Environment Env, for (Environment::iterator I = Env.begin(), E = Env.end(); I != E; ++I) { - const Stmt *BlkExpr = I.getKey(); + const EnvironmentEntry &BlkExpr = I.getKey(); // For recorded locations (used when evaluating loads and stores), we // consider them live only when their associated normal expression is // also live. @@ -179,7 +213,7 @@ EnvironmentManager::removeDeadBindings(Environment Env, } const SVal &X = I.getData(); - if (SymReaper.isLive(BlkExpr)) { + if (SymReaper.isLive(BlkExpr.getStmt(), BlkExpr.getLocationContext())) { // Copy the binding to the new map. EBMapRef = EBMapRef.add(BlkExpr, X); @@ -204,13 +238,58 @@ EnvironmentManager::removeDeadBindings(Environment Env, // Go through he deferred locations and add them to the new environment if // the correspond Stmt* is in the map as well. - for (SmallVectorImpl<std::pair<const Stmt*, SVal> >::iterator + for (SmallVectorImpl<std::pair<EnvironmentEntry, SVal> >::iterator I = deferredLocations.begin(), E = deferredLocations.end(); I != E; ++I) { - const Stmt *S = (Stmt*) (((uintptr_t) I->first) & (uintptr_t) ~0x1); - if (EBMapRef.lookup(S)) - EBMapRef = EBMapRef.add(I->first, I->second); + const EnvironmentEntry &En = I->first; + const Stmt *S = (Stmt*) (((uintptr_t) En.getStmt()) & (uintptr_t) ~0x1); + if (EBMapRef.lookup(EnvironmentEntry(S, En.getLocationContext()))) + EBMapRef = EBMapRef.add(En, I->second); } NewEnv.ExprBindings = EBMapRef.asImmutableMap(); return NewEnv; } + +void Environment::print(raw_ostream &Out, const char *NL, + const char *Sep) const { + printAux(Out, false, NL, Sep); + printAux(Out, true, NL, Sep); +} + +void Environment::printAux(raw_ostream &Out, bool printLocations, + const char *NL, + const char *Sep) const{ + + bool isFirst = true; + + for (Environment::iterator I = begin(), E = end(); I != E; ++I) { + const EnvironmentEntry &En = I.getKey(); + if (IsLocation(En)) { + if (!printLocations) + continue; + } + else { + if (printLocations) + continue; + } + + if (isFirst) { + Out << NL << NL + << (printLocations ? "Load/Store locations:" : "Expressions:") + << NL; + isFirst = false; + } else { + Out << NL; + } + + const Stmt *S = En.getStmt(); + if (printLocations) { + S = (Stmt*) (((uintptr_t) S) & ((uintptr_t) ~0x1)); + } + + Out << " (" << (void*) En.getLocationContext() << ',' << (void*) S << ") "; + LangOptions LO; // FIXME. + S->printPretty(Out, 0, PrintingPolicy(LO)); + Out << " : " << I.getData(); + } +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ExplodedGraph.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ExplodedGraph.cpp index 5762a21..0dcbe1f 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ExplodedGraph.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ExplodedGraph.cpp @@ -15,6 +15,7 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" #include "clang/AST/Stmt.h" +#include "clang/AST/ParentMap.h" #include "llvm/ADT/DenseSet.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/SmallVector.h" @@ -44,25 +45,18 @@ void ExplodedNode::SetAuditor(ExplodedNode::Auditor* A) { // Cleanup. //===----------------------------------------------------------------------===// -typedef std::vector<ExplodedNode*> NodeList; -static inline NodeList*& getNodeList(void *&p) { return (NodeList*&) p; } +static const unsigned CounterTop = 1000; -ExplodedGraph::~ExplodedGraph() { - if (reclaimNodes) { - delete getNodeList(recentlyAllocatedNodes); - delete getNodeList(freeNodes); - } -} +ExplodedGraph::ExplodedGraph() + : NumNodes(0), reclaimNodes(false), reclaimCounter(CounterTop) {} + +ExplodedGraph::~ExplodedGraph() {} //===----------------------------------------------------------------------===// // Node reclamation. //===----------------------------------------------------------------------===// -void ExplodedGraph::reclaimRecentlyAllocatedNodes() { - if (!recentlyAllocatedNodes) - return; - NodeList &nl = *getNodeList(recentlyAllocatedNodes); - +bool ExplodedGraph::shouldCollect(const ExplodedNode *node) { // Reclaimn all nodes that match *all* the following criteria: // // (1) 1 predecessor (that has one successor) @@ -72,61 +66,86 @@ void ExplodedGraph::reclaimRecentlyAllocatedNodes() { // (5) The 'store' is the same as the predecessor. // (6) The 'GDM' is the same as the predecessor. // (7) The LocationContext is the same as the predecessor. - // (8) The PostStmt is for a non-CFGElement expression. - - for (NodeList::iterator i = nl.begin(), e = nl.end() ; i != e; ++i) { - ExplodedNode *node = *i; - - // Conditions 1 and 2. - if (node->pred_size() != 1 || node->succ_size() != 1) - continue; + // (8) The PostStmt is for a non-consumed Stmt or Expr. - ExplodedNode *pred = *(node->pred_begin()); - if (pred->succ_size() != 1) - continue; + // Conditions 1 and 2. + if (node->pred_size() != 1 || node->succ_size() != 1) + return false; - ExplodedNode *succ = *(node->succ_begin()); - if (succ->pred_size() != 1) - continue; + const ExplodedNode *pred = *(node->pred_begin()); + if (pred->succ_size() != 1) + return false; + + const ExplodedNode *succ = *(node->succ_begin()); + if (succ->pred_size() != 1) + return false; + + // Condition 3. + ProgramPoint progPoint = node->getLocation(); + if (!isa<PostStmt>(progPoint) || + (isa<CallEnter>(progPoint) || isa<CallExit>(progPoint))) + return false; + + // Condition 4. + PostStmt ps = cast<PostStmt>(progPoint); + if (ps.getTag()) + return false; + + if (isa<BinaryOperator>(ps.getStmt())) + return false; + + // Conditions 5, 6, and 7. + ProgramStateRef state = node->getState(); + ProgramStateRef pred_state = pred->getState(); + if (state->store != pred_state->store || state->GDM != pred_state->GDM || + progPoint.getLocationContext() != pred->getLocationContext()) + return false; + + // Condition 8. + if (const Expr *Ex = dyn_cast<Expr>(ps.getStmt())) { + ParentMap &PM = progPoint.getLocationContext()->getParentMap(); + if (!PM.isConsumedExpr(Ex)) + return false; + } + + return true; +} - // Condition 3. - ProgramPoint progPoint = node->getLocation(); - if (!isa<PostStmt>(progPoint)) - continue; - // Condition 4. - PostStmt ps = cast<PostStmt>(progPoint); - if (ps.getTag()) - continue; +void ExplodedGraph::collectNode(ExplodedNode *node) { + // Removing a node means: + // (a) changing the predecessors successor to the successor of this node + // (b) changing the successors predecessor to the predecessor of this node + // (c) Putting 'node' onto freeNodes. + assert(node->pred_size() == 1 || node->succ_size() == 1); + ExplodedNode *pred = *(node->pred_begin()); + ExplodedNode *succ = *(node->succ_begin()); + pred->replaceSuccessor(succ); + succ->replacePredecessor(pred); + FreeNodes.push_back(node); + Nodes.RemoveNode(node); + --NumNodes; + node->~ExplodedNode(); +} - if (isa<BinaryOperator>(ps.getStmt())) - continue; +void ExplodedGraph::reclaimRecentlyAllocatedNodes() { + if (ChangedNodes.empty()) + return; - // Conditions 5, 6, and 7. - const ProgramState *state = node->getState(); - const ProgramState *pred_state = pred->getState(); - if (state->store != pred_state->store || state->GDM != pred_state->GDM || - progPoint.getLocationContext() != pred->getLocationContext()) - continue; + // Only periodically relcaim nodes so that we can build up a set of + // nodes that meet the reclamation criteria. Freshly created nodes + // by definition have no successor, and thus cannot be reclaimed (see below). + assert(reclaimCounter > 0); + if (--reclaimCounter != 0) + return; + reclaimCounter = CounterTop; - // Condition 8. - if (node->getCFG().isBlkExpr(ps.getStmt())) - continue; - - // If we reach here, we can remove the node. This means: - // (a) changing the predecessors successor to the successor of this node - // (b) changing the successors predecessor to the predecessor of this node - // (c) Putting 'node' onto freeNodes. - pred->replaceSuccessor(succ); - succ->replacePredecessor(pred); - if (!freeNodes) - freeNodes = new NodeList(); - getNodeList(freeNodes)->push_back(node); - Nodes.RemoveNode(node); - --NumNodes; - node->~ExplodedNode(); + for (NodeVector::iterator it = ChangedNodes.begin(), et = ChangedNodes.end(); + it != et; ++it) { + ExplodedNode *node = *it; + if (shouldCollect(node)) + collectNode(node); } - - nl.clear(); + ChangedNodes.clear(); } //===----------------------------------------------------------------------===// @@ -215,36 +234,33 @@ ExplodedNode** ExplodedNode::NodeGroup::end() const { } ExplodedNode *ExplodedGraph::getNode(const ProgramPoint &L, - const ProgramState *State, bool* IsNew) { + ProgramStateRef State, + bool IsSink, + bool* IsNew) { // Profile 'State' to determine if we already have an existing node. llvm::FoldingSetNodeID profile; void *InsertPos = 0; - NodeTy::Profile(profile, L, State); + NodeTy::Profile(profile, L, State, IsSink); NodeTy* V = Nodes.FindNodeOrInsertPos(profile, InsertPos); if (!V) { - if (freeNodes && !getNodeList(freeNodes)->empty()) { - NodeList *nl = getNodeList(freeNodes); - V = nl->back(); - nl->pop_back(); + if (!FreeNodes.empty()) { + V = FreeNodes.back(); + FreeNodes.pop_back(); } else { // Allocate a new node. V = (NodeTy*) getAllocator().Allocate<NodeTy>(); } - new (V) NodeTy(L, State); + new (V) NodeTy(L, State, IsSink); - if (reclaimNodes) { - if (!recentlyAllocatedNodes) - recentlyAllocatedNodes = new NodeList(); - getNodeList(recentlyAllocatedNodes)->push_back(V); - } + if (reclaimNodes) + ChangedNodes.push_back(V); // Insert the node into the node set and return it. Nodes.InsertNode(V, InsertPos); - ++NumNodes; if (IsNew) *IsNew = true; @@ -265,7 +281,7 @@ ExplodedGraph::Trim(const NodeTy* const* NBeg, const NodeTy* const* NEnd, assert (NBeg < NEnd); - llvm::OwningPtr<InterExplodedGraphMap> M(new InterExplodedGraphMap()); + OwningPtr<InterExplodedGraphMap> M(new InterExplodedGraphMap()); ExplodedGraph* G = TrimInternal(NBeg, NEnd, M.get(), InverseMap); @@ -334,7 +350,7 @@ ExplodedGraph::TrimInternal(const ExplodedNode* const* BeginSources, // Create the corresponding node in the new graph and record the mapping // from the old node to the new node. - ExplodedNode *NewN = G->getNode(N->getLocation(), N->State, NULL); + ExplodedNode *NewN = G->getNode(N->getLocation(), N->State, N->isSink(), 0); Pass2[N] = NewN; // Also record the reverse mapping from the new node to the old node. @@ -372,15 +388,13 @@ ExplodedGraph::TrimInternal(const ExplodedNode* const* BeginSources, if (Pass1.count(*I)) WL2.push_back(*I); } - - // Finally, explicitly mark all nodes without any successors as sinks. - if (N->isSink()) - NewN->markAsSink(); } return G; } +void InterExplodedGraphMap::anchor() { } + ExplodedNode* InterExplodedGraphMap::getMappedNode(const ExplodedNode *N) const { llvm::DenseMap<const ExplodedNode*, ExplodedNode*>::const_iterator I = diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp index ac9cf0b..d2da9aa 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -13,22 +13,24 @@ // //===----------------------------------------------------------------------===// +#define DEBUG_TYPE "ExprEngine" + #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngineBuilders.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h" #include "clang/AST/CharUnits.h" #include "clang/AST/ParentMap.h" #include "clang/AST/StmtObjC.h" +#include "clang/AST/StmtCXX.h" #include "clang/AST/DeclCXX.h" #include "clang/Basic/Builtins.h" #include "clang/Basic/SourceManager.h" -#include "clang/Basic/SourceManager.h" #include "clang/Basic/PrettyStackTrace.h" #include "llvm/Support/raw_ostream.h" #include "llvm/ADT/ImmutableList.h" +#include "llvm/ADT/Statistic.h" #ifndef NDEBUG #include "llvm/Support/GraphWriter.h" @@ -38,6 +40,19 @@ using namespace clang; using namespace ento; using llvm::APSInt; +STATISTIC(NumRemoveDeadBindings, + "The # of times RemoveDeadBindings is called"); +STATISTIC(NumRemoveDeadBindingsSkipped, + "The # of times RemoveDeadBindings is skipped"); +STATISTIC(NumMaxBlockCountReached, + "The # of aborted paths due to reaching the maximum block count in " + "a top level function"); +STATISTIC(NumMaxBlockCountReachedInInlined, + "The # of aborted paths due to reaching the maximum block count in " + "an inlined function"); +STATISTIC(NumTimesRetriedWithoutInlining, + "The # of times we re-evaluated a call without inlining"); + //===----------------------------------------------------------------------===// // Utility functions. //===----------------------------------------------------------------------===// @@ -51,17 +66,20 @@ static inline Selector GetNullarySelector(const char* name, ASTContext &Ctx) { // Engine construction and deletion. //===----------------------------------------------------------------------===// -ExprEngine::ExprEngine(AnalysisManager &mgr, bool gcEnabled) +ExprEngine::ExprEngine(AnalysisManager &mgr, bool gcEnabled, + SetOfConstDecls *VisitedCallees, + FunctionSummariesTy *FS) : AMgr(mgr), - Engine(*this), + AnalysisDeclContexts(mgr.getAnalysisDeclContextManager()), + Engine(*this, VisitedCallees, FS), G(Engine.getGraph()), - Builder(NULL), StateMgr(getContext(), mgr.getStoreManagerCreator(), mgr.getConstraintManagerCreator(), G.getAllocator(), *this), SymMgr(StateMgr.getSymbolManager()), svalBuilder(StateMgr.getSValBuilder()), - EntryNode(NULL), currentStmt(NULL), + EntryNode(NULL), + currentStmt(NULL), currentStmtIdx(0), currentBuilderContext(0), NSExceptionII(NULL), NSExceptionInstanceRaiseSelectors(NULL), RaiseSel(GetNullarySelector("raise", getContext())), ObjCGCEnabled(gcEnabled), BR(mgr, *this) { @@ -81,15 +99,15 @@ ExprEngine::~ExprEngine() { // Utility methods. //===----------------------------------------------------------------------===// -const ProgramState *ExprEngine::getInitialState(const LocationContext *InitLoc) { - const ProgramState *state = StateMgr.getInitialState(InitLoc); +ProgramStateRef ExprEngine::getInitialState(const LocationContext *InitLoc) { + ProgramStateRef state = StateMgr.getInitialState(InitLoc); + const Decl *D = InitLoc->getDecl(); // Preconditions. - // FIXME: It would be nice if we had a more general mechanism to add // such preconditions. Some day. do { - const Decl *D = InitLoc->getDecl(); + if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) { // Precondition: the first argument of 'main' is an integer guaranteed // to be > 0. @@ -117,49 +135,45 @@ const ProgramState *ExprEngine::getInitialState(const LocationContext *InitLoc) if (!Constraint) break; - if (const ProgramState *newState = state->assume(*Constraint, true)) + if (ProgramStateRef newState = state->assume(*Constraint, true)) state = newState; - - break; } - - if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D)) { - // Precondition: 'self' is always non-null upon entry to an Objective-C - // method. - const ImplicitParamDecl *SelfD = MD->getSelfDecl(); - const MemRegion *R = state->getRegion(SelfD, InitLoc); - SVal V = state->getSVal(loc::MemRegionVal(R)); - - if (const Loc *LV = dyn_cast<Loc>(&V)) { - // Assume that the pointer value in 'self' is non-null. - state = state->assume(*LV, true); - assert(state && "'self' cannot be null"); - } + break; + } + while (0); + + if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D)) { + // Precondition: 'self' is always non-null upon entry to an Objective-C + // method. + const ImplicitParamDecl *SelfD = MD->getSelfDecl(); + const MemRegion *R = state->getRegion(SelfD, InitLoc); + SVal V = state->getSVal(loc::MemRegionVal(R)); + + if (const Loc *LV = dyn_cast<Loc>(&V)) { + // Assume that the pointer value in 'self' is non-null. + state = state->assume(*LV, true); + assert(state && "'self' cannot be null"); } - } while (0); - - return state; -} + } -bool -ExprEngine::doesInvalidateGlobals(const CallOrObjCMessage &callOrMessage) const -{ - if (callOrMessage.isFunctionCall() && !callOrMessage.isCXXCall()) { - SVal calleeV = callOrMessage.getFunctionCallee(); - if (const FunctionTextRegion *codeR = - dyn_cast_or_null<FunctionTextRegion>(calleeV.getAsRegion())) { - - const FunctionDecl *fd = codeR->getDecl(); - if (const IdentifierInfo *ii = fd->getIdentifier()) { - StringRef fname = ii->getName(); - if (fname == "strlen") - return false; + if (const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(D)) { + if (!MD->isStatic()) { + // Precondition: 'this' is always non-null upon entry to the + // top-level function. This is our starting assumption for + // analyzing an "open" program. + const StackFrameContext *SFC = InitLoc->getCurrentStackFrame(); + if (SFC->getParent() == 0) { + loc::MemRegionVal L(getCXXThisRegion(MD, SFC)); + SVal V = state->getSVal(L); + if (const Loc *LV = dyn_cast<Loc>(&V)) { + state = state->assume(*LV, true); + assert(state && "'this' cannot be null"); + } } } } - - // The conservative answer: invalidates globals. - return true; + + return state; } //===----------------------------------------------------------------------===// @@ -168,25 +182,26 @@ ExprEngine::doesInvalidateGlobals(const CallOrObjCMessage &callOrMessage) const /// evalAssume - Called by ConstraintManager. Used to call checker-specific /// logic for handling assumptions on symbolic values. -const ProgramState *ExprEngine::processAssume(const ProgramState *state, +ProgramStateRef ExprEngine::processAssume(ProgramStateRef state, SVal cond, bool assumption) { return getCheckerManager().runCheckersForEvalAssume(state, cond, assumption); } -bool ExprEngine::wantsRegionChangeUpdate(const ProgramState *state) { +bool ExprEngine::wantsRegionChangeUpdate(ProgramStateRef state) { return getCheckerManager().wantsRegionChangeUpdate(state); } -const ProgramState * -ExprEngine::processRegionChanges(const ProgramState *state, +ProgramStateRef +ExprEngine::processRegionChanges(ProgramStateRef state, const StoreManager::InvalidatedSymbols *invalidated, ArrayRef<const MemRegion *> Explicits, - ArrayRef<const MemRegion *> Regions) { + ArrayRef<const MemRegion *> Regions, + const CallOrObjCMessage *Call) { return getCheckerManager().runCheckersForRegionChanges(state, invalidated, - Explicits, Regions); + Explicits, Regions, Call); } -void ExprEngine::printState(raw_ostream &Out, const ProgramState *State, +void ExprEngine::printState(raw_ostream &Out, ProgramStateRef State, const char *NL, const char *Sep) { getCheckerManager().runCheckersForPrintState(Out, State, NL, Sep); } @@ -195,54 +210,77 @@ void ExprEngine::processEndWorklist(bool hasWorkRemaining) { getCheckerManager().runCheckersForEndAnalysis(G, BR, *this); } -void ExprEngine::processCFGElement(const CFGElement E, - StmtNodeBuilder& builder) { +void ExprEngine::processCFGElement(const CFGElement E, ExplodedNode *Pred, + unsigned StmtIdx, NodeBuilderContext *Ctx) { + currentStmtIdx = StmtIdx; + currentBuilderContext = Ctx; + switch (E.getKind()) { case CFGElement::Invalid: llvm_unreachable("Unexpected CFGElement kind."); case CFGElement::Statement: - ProcessStmt(const_cast<Stmt*>(E.getAs<CFGStmt>()->getStmt()), builder); + ProcessStmt(const_cast<Stmt*>(E.getAs<CFGStmt>()->getStmt()), Pred); return; case CFGElement::Initializer: - ProcessInitializer(E.getAs<CFGInitializer>()->getInitializer(), builder); + ProcessInitializer(E.getAs<CFGInitializer>()->getInitializer(), Pred); return; case CFGElement::AutomaticObjectDtor: case CFGElement::BaseDtor: case CFGElement::MemberDtor: case CFGElement::TemporaryDtor: - ProcessImplicitDtor(*E.getAs<CFGImplicitDtor>(), builder); + ProcessImplicitDtor(*E.getAs<CFGImplicitDtor>(), Pred); return; } } -void ExprEngine::ProcessStmt(const CFGStmt S, StmtNodeBuilder& builder) { - // TODO: Use RAII to remove the unnecessary, tagged nodes. - //RegisterCreatedNodes registerCreatedNodes(getGraph()); +static bool shouldRemoveDeadBindings(AnalysisManager &AMgr, + const CFGStmt S, + const ExplodedNode *Pred, + const LocationContext *LC) { + + // Are we never purging state values? + if (AMgr.getPurgeMode() == PurgeNone) + return false; + + // Is this the beginning of a basic block? + if (isa<BlockEntrance>(Pred->getLocation())) + return true; + // Is this on a non-expression? + if (!isa<Expr>(S.getStmt())) + return true; + + // Run before processing a call. + if (isa<CallExpr>(S.getStmt())) + return true; + + // Is this an expression that is consumed by another expression? If so, + // postpone cleaning out the state. + ParentMap &PM = LC->getAnalysisDeclContext()->getParentMap(); + return !PM.isConsumedExpr(cast<Expr>(S.getStmt())); +} + +void ExprEngine::ProcessStmt(const CFGStmt S, + ExplodedNode *Pred) { // Reclaim any unnecessary nodes in the ExplodedGraph. G.reclaimRecentlyAllocatedNodes(); - // Recycle any unused states in the ProgramStateManager. - StateMgr.recycleUnusedStates(); currentStmt = S.getStmt(); PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(), currentStmt->getLocStart(), "Error evaluating statement"); - // A tag to track convenience transitions, which can be removed at cleanup. - static SimpleProgramPointTag cleanupTag("ExprEngine : Clean Node"); - Builder = &builder; - EntryNode = builder.getPredecessor(); + EntryNode = Pred; - const ProgramState *EntryState = EntryNode->getState(); + ProgramStateRef EntryState = EntryNode->getState(); CleanedState = EntryState; - ExplodedNode *CleanedNode = 0; // Create the cleaned state. const LocationContext *LC = EntryNode->getLocationContext(); SymbolReaper SymReaper(LC, currentStmt, SymMgr, getStoreManager()); - if (AMgr.getPurgeMode() != PurgeNone) { + if (shouldRemoveDeadBindings(AMgr, S, Pred, LC)) { + NumRemoveDeadBindings++; getCheckerManager().runCheckersForLiveSymbols(CleanedState, SymReaper); const StackFrameContext *SFC = LC->getCurrentStackFrame(); @@ -251,25 +289,23 @@ void ExprEngine::ProcessStmt(const CFGStmt S, StmtNodeBuilder& builder) { // and the store. TODO: The function should just return new env and store, // not a new state. CleanedState = StateMgr.removeDeadBindings(CleanedState, SFC, SymReaper); + } else { + NumRemoveDeadBindingsSkipped++; } // Process any special transfer function for dead symbols. ExplodedNodeSet Tmp; + // A tag to track convenience transitions, which can be removed at cleanup. + static SimpleProgramPointTag cleanupTag("ExprEngine : Clean Node"); + if (!SymReaper.hasDeadSymbols()) { // Generate a CleanedNode that has the environment and store cleaned // up. Since no symbols are dead, we can optimize and not clean out // the constraint manager. - CleanedNode = - Builder->generateNode(currentStmt, CleanedState, EntryNode, &cleanupTag); - Tmp.Add(CleanedNode); + StmtNodeBuilder Bldr(Pred, Tmp, *currentBuilderContext); + Bldr.generateNode(currentStmt, EntryNode, CleanedState, false, &cleanupTag); } else { - SaveAndRestore<bool> OldSink(Builder->BuildSinks); - SaveOr OldHasGen(Builder->hasGeneratedNode); - - SaveAndRestore<bool> OldPurgeDeadSymbols(Builder->PurgingDeadSymbols); - Builder->PurgingDeadSymbols = true; - // Call checkers with the non-cleaned state so that they could query the // values of the soon to be dead symbols. ExplodedNodeSet CheckedSet; @@ -279,9 +315,10 @@ void ExprEngine::ProcessStmt(const CFGStmt S, StmtNodeBuilder& builder) { // For each node in CheckedSet, generate CleanedNodes that have the // environment, the store, and the constraints cleaned up but have the // user-supplied states as the predecessors. + StmtNodeBuilder Bldr(CheckedSet, Tmp, *currentBuilderContext); for (ExplodedNodeSet::const_iterator I = CheckedSet.begin(), E = CheckedSet.end(); I != E; ++I) { - const ProgramState *CheckerState = (*I)->getState(); + ProgramStateRef CheckerState = (*I)->getState(); // The constraint manager has not been cleaned up yet, so clean up now. CheckerState = getConstraintManager().removeDeadBindings(CheckerState, @@ -296,109 +333,109 @@ void ExprEngine::ProcessStmt(const CFGStmt S, StmtNodeBuilder& builder) { // Create a state based on CleanedState with CheckerState GDM and // generate a transition to that state. - const ProgramState *CleanedCheckerSt = + ProgramStateRef CleanedCheckerSt = StateMgr.getPersistentStateWithGDM(CleanedState, CheckerState); - ExplodedNode *CleanedNode = Builder->generateNode(currentStmt, - CleanedCheckerSt, *I, - &cleanupTag); - Tmp.Add(CleanedNode); + Bldr.generateNode(currentStmt, *I, CleanedCheckerSt, false, &cleanupTag, + ProgramPoint::PostPurgeDeadSymbolsKind); } } + ExplodedNodeSet Dst; for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end(); I!=E; ++I) { - // TODO: Remove Dest set, it's no longer needed. - ExplodedNodeSet Dst; + ExplodedNodeSet DstI; // Visit the statement. - Visit(currentStmt, *I, Dst); + Visit(currentStmt, *I, DstI); + Dst.insert(DstI); } + // Enqueue the new nodes onto the work list. + Engine.enqueue(Dst, currentBuilderContext->getBlock(), currentStmtIdx); + // NULL out these variables to cleanup. CleanedState = NULL; EntryNode = NULL; currentStmt = 0; - Builder = NULL; } void ExprEngine::ProcessInitializer(const CFGInitializer Init, - StmtNodeBuilder &builder) { + ExplodedNode *Pred) { + ExplodedNodeSet Dst; + // We don't set EntryNode and currentStmt. And we don't clean up state. const CXXCtorInitializer *BMI = Init.getInitializer(); - - ExplodedNode *pred = builder.getPredecessor(); - - const StackFrameContext *stackFrame = cast<StackFrameContext>(pred->getLocationContext()); - const CXXConstructorDecl *decl = cast<CXXConstructorDecl>(stackFrame->getDecl()); + const StackFrameContext *stackFrame = + cast<StackFrameContext>(Pred->getLocationContext()); + const CXXConstructorDecl *decl = + cast<CXXConstructorDecl>(stackFrame->getDecl()); const CXXThisRegion *thisReg = getCXXThisRegion(decl, stackFrame); - SVal thisVal = pred->getState()->getSVal(thisReg); + SVal thisVal = Pred->getState()->getSVal(thisReg); if (BMI->isAnyMemberInitializer()) { - ExplodedNodeSet Dst; - // Evaluate the initializer. - Visit(BMI->getInit(), pred, Dst); - for (ExplodedNodeSet::iterator I = Dst.begin(), E = Dst.end(); I != E; ++I){ - ExplodedNode *Pred = *I; - const ProgramState *state = Pred->getState(); + StmtNodeBuilder Bldr(Pred, Dst, *currentBuilderContext); + ProgramStateRef state = Pred->getState(); - const FieldDecl *FD = BMI->getAnyMember(); + const FieldDecl *FD = BMI->getAnyMember(); - SVal FieldLoc = state->getLValue(FD, thisVal); - SVal InitVal = state->getSVal(BMI->getInit()); - state = state->bindLoc(FieldLoc, InitVal); + SVal FieldLoc = state->getLValue(FD, thisVal); + SVal InitVal = state->getSVal(BMI->getInit(), Pred->getLocationContext()); + state = state->bindLoc(FieldLoc, InitVal); - // Use a custom node building process. - PostInitializer PP(BMI, stackFrame); - // Builder automatically add the generated node to the deferred set, - // which are processed in the builder's dtor. - builder.generateNode(PP, state, Pred); - } - return; - } + // Use a custom node building process. + PostInitializer PP(BMI, stackFrame); + // Builder automatically add the generated node to the deferred set, + // which are processed in the builder's dtor. + Bldr.generateNode(PP, Pred, state); + } else { + assert(BMI->isBaseInitializer()); - assert(BMI->isBaseInitializer()); + // Get the base class declaration. + const CXXConstructExpr *ctorExpr = cast<CXXConstructExpr>(BMI->getInit()); - // Get the base class declaration. - const CXXConstructExpr *ctorExpr = cast<CXXConstructExpr>(BMI->getInit()); + // Create the base object region. + SVal baseVal = + getStoreManager().evalDerivedToBase(thisVal, ctorExpr->getType()); + const MemRegion *baseReg = baseVal.getAsRegion(); + assert(baseReg); + + VisitCXXConstructExpr(ctorExpr, baseReg, Pred, Dst); + } - // Create the base object region. - SVal baseVal = - getStoreManager().evalDerivedToBase(thisVal, ctorExpr->getType()); - const MemRegion *baseReg = baseVal.getAsRegion(); - assert(baseReg); - Builder = &builder; - ExplodedNodeSet dst; - VisitCXXConstructExpr(ctorExpr, baseReg, pred, dst); + // Enqueue the new nodes onto the work list. + Engine.enqueue(Dst, currentBuilderContext->getBlock(), currentStmtIdx); } void ExprEngine::ProcessImplicitDtor(const CFGImplicitDtor D, - StmtNodeBuilder &builder) { - Builder = &builder; - + ExplodedNode *Pred) { + ExplodedNodeSet Dst; switch (D.getKind()) { case CFGElement::AutomaticObjectDtor: - ProcessAutomaticObjDtor(cast<CFGAutomaticObjDtor>(D), builder); + ProcessAutomaticObjDtor(cast<CFGAutomaticObjDtor>(D), Pred, Dst); break; case CFGElement::BaseDtor: - ProcessBaseDtor(cast<CFGBaseDtor>(D), builder); + ProcessBaseDtor(cast<CFGBaseDtor>(D), Pred, Dst); break; case CFGElement::MemberDtor: - ProcessMemberDtor(cast<CFGMemberDtor>(D), builder); + ProcessMemberDtor(cast<CFGMemberDtor>(D), Pred, Dst); break; case CFGElement::TemporaryDtor: - ProcessTemporaryDtor(cast<CFGTemporaryDtor>(D), builder); + ProcessTemporaryDtor(cast<CFGTemporaryDtor>(D), Pred, Dst); break; default: llvm_unreachable("Unexpected dtor kind."); } + + // Enqueue the new nodes onto the work list. + Engine.enqueue(Dst, currentBuilderContext->getBlock(), currentStmtIdx); } -void ExprEngine::ProcessAutomaticObjDtor(const CFGAutomaticObjDtor dtor, - StmtNodeBuilder &builder) { - ExplodedNode *pred = builder.getPredecessor(); - const ProgramState *state = pred->getState(); - const VarDecl *varDecl = dtor.getVarDecl(); +void ExprEngine::ProcessAutomaticObjDtor(const CFGAutomaticObjDtor Dtor, + ExplodedNode *Pred, + ExplodedNodeSet &Dst) { + ProgramStateRef state = Pred->getState(); + const VarDecl *varDecl = Dtor.getVarDecl(); QualType varType = varDecl->getType(); @@ -409,30 +446,29 @@ void ExprEngine::ProcessAutomaticObjDtor(const CFGAutomaticObjDtor dtor, assert(recordDecl && "get CXXRecordDecl fail"); const CXXDestructorDecl *dtorDecl = recordDecl->getDestructor(); - Loc dest = state->getLValue(varDecl, pred->getLocationContext()); + Loc dest = state->getLValue(varDecl, Pred->getLocationContext()); - ExplodedNodeSet dstSet; VisitCXXDestructor(dtorDecl, cast<loc::MemRegionVal>(dest).getRegion(), - dtor.getTriggerStmt(), pred, dstSet); + Dtor.getTriggerStmt(), Pred, Dst); } void ExprEngine::ProcessBaseDtor(const CFGBaseDtor D, - StmtNodeBuilder &builder) { -} + ExplodedNode *Pred, ExplodedNodeSet &Dst) {} void ExprEngine::ProcessMemberDtor(const CFGMemberDtor D, - StmtNodeBuilder &builder) { -} + ExplodedNode *Pred, ExplodedNodeSet &Dst) {} void ExprEngine::ProcessTemporaryDtor(const CFGTemporaryDtor D, - StmtNodeBuilder &builder) { -} + ExplodedNode *Pred, + ExplodedNodeSet &Dst) {} void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, - ExplodedNodeSet &Dst) { + ExplodedNodeSet &DstTop) { PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(), S->getLocStart(), "Error evaluating statement"); + ExplodedNodeSet Dst; + StmtNodeBuilder Bldr(Pred, DstTop, *currentBuilderContext); // Expressions to ignore. if (const Expr *Ex = dyn_cast<Expr>(S)) @@ -442,19 +478,14 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, // this check when we KNOW that there is no block-level subexpression. // The motivation is that this check requires a hashtable lookup. - if (S != currentStmt && Pred->getLocationContext()->getCFG()->isBlkExpr(S)) { - Dst.Add(Pred); + if (S != currentStmt && Pred->getLocationContext()->getCFG()->isBlkExpr(S)) return; - } switch (S->getStmtClass()) { // C++ and ARC stuff we don't support yet. case Expr::ObjCIndirectCopyRestoreExprClass: - case Stmt::CXXBindTemporaryExprClass: - case Stmt::CXXCatchStmtClass: case Stmt::CXXDependentScopeMemberExprClass: case Stmt::CXXPseudoDestructorExprClass: - case Stmt::CXXThrowExprClass: case Stmt::CXXTryStmtClass: case Stmt::CXXTypeidExprClass: case Stmt::CXXUuidofExprClass: @@ -463,6 +494,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, case Stmt::DependentScopeDeclRefExprClass: case Stmt::UnaryTypeTraitExprClass: case Stmt::BinaryTypeTraitExprClass: + case Stmt::TypeTraitExprClass: case Stmt::ArrayTypeTraitExprClass: case Stmt::ExpressionTraitExprClass: case Stmt::UnresolvedLookupExprClass: @@ -472,22 +504,19 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, case Stmt::SubstNonTypeTemplateParmPackExprClass: case Stmt::SEHTryStmtClass: case Stmt::SEHExceptStmtClass: - case Stmt::SEHFinallyStmtClass: - { - SaveAndRestore<bool> OldSink(Builder->BuildSinks); - Builder->BuildSinks = true; - const ExplodedNode *node = MakeNode(Dst, S, Pred, Pred->getState()); - Engine.addAbortedBlock(node, Builder->getBlock()); + case Stmt::LambdaExprClass: + case Stmt::SEHFinallyStmtClass: { + const ExplodedNode *node = Bldr.generateNode(S, Pred, Pred->getState(), + /* sink */ true); + Engine.addAbortedBlock(node, currentBuilderContext->getBlock()); break; } // We don't handle default arguments either yet, but we can fake it // for now by just skipping them. case Stmt::SubstNonTypeTemplateParmExprClass: - case Stmt::CXXDefaultArgExprClass: { - Dst.Add(Pred); + case Stmt::CXXDefaultArgExprClass: break; - } case Stmt::ParenExprClass: llvm_unreachable("ParenExprs already handled."); @@ -511,38 +540,44 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, case Stmt::NullStmtClass: case Stmt::SwitchStmtClass: case Stmt::WhileStmtClass: + case Expr::MSDependentExistsStmtClass: llvm_unreachable("Stmt should not be in analyzer evaluation loop"); - break; case Stmt::GNUNullExprClass: { // GNU __null is a pointer-width integer, not an actual pointer. - const ProgramState *state = Pred->getState(); - state = state->BindExpr(S, svalBuilder.makeIntValWithPtrWidth(0, false)); - MakeNode(Dst, S, Pred, state); + ProgramStateRef state = Pred->getState(); + state = state->BindExpr(S, Pred->getLocationContext(), + svalBuilder.makeIntValWithPtrWidth(0, false)); + Bldr.generateNode(S, Pred, state); break; } case Stmt::ObjCAtSynchronizedStmtClass: + Bldr.takeNodes(Pred); VisitObjCAtSynchronizedStmt(cast<ObjCAtSynchronizedStmt>(S), Pred, Dst); + Bldr.addNodes(Dst); break; + // FIXME. + case Stmt::ObjCSubscriptRefExprClass: + break; + case Stmt::ObjCPropertyRefExprClass: // Implicitly handled by Environment::getSVal(). - Dst.Add(Pred); break; case Stmt::ImplicitValueInitExprClass: { - const ProgramState *state = Pred->getState(); + ProgramStateRef state = Pred->getState(); QualType ty = cast<ImplicitValueInitExpr>(S)->getType(); SVal val = svalBuilder.makeZeroVal(ty); - MakeNode(Dst, S, Pred, state->BindExpr(S, val)); + Bldr.generateNode(S, Pred, state->BindExpr(S, Pred->getLocationContext(), + val)); break; } - case Stmt::ExprWithCleanupsClass: { - Visit(cast<ExprWithCleanups>(S)->getSubExpr(), Pred, Dst); + case Stmt::ExprWithCleanupsClass: + // Handled due to fully linearised CFG. break; - } // Cases not handled yet; but will handle some day. case Stmt::DesignatedInitExprClass: @@ -556,7 +591,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, case Stmt::ObjCIsaExprClass: case Stmt::ObjCProtocolExprClass: case Stmt::ObjCSelectorExprClass: - case Stmt::ObjCStringLiteralClass: + case Expr::ObjCNumericLiteralClass: case Stmt::ParenListExprClass: case Stmt::PredefinedExprClass: case Stmt::ShuffleVectorExprClass: @@ -565,50 +600,101 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, case Stmt::OpaqueValueExprClass: case Stmt::AsTypeExprClass: case Stmt::AtomicExprClass: - // Fall through. + // Fall through. + // Currently all handling of 'throw' just falls to the CFG. We + // can consider doing more if necessary. + case Stmt::CXXThrowExprClass: + // Fall through. + // Cases we intentionally don't evaluate, since they don't need // to be explicitly evaluated. case Stmt::AddrLabelExprClass: case Stmt::IntegerLiteralClass: case Stmt::CharacterLiteralClass: case Stmt::CXXBoolLiteralExprClass: + case Stmt::ObjCBoolLiteralExprClass: case Stmt::FloatingLiteralClass: case Stmt::SizeOfPackExprClass: - case Stmt::CXXNullPtrLiteralExprClass: - Dst.Add(Pred); // No-op. Simply propagate the current state unchanged. + case Stmt::StringLiteralClass: + case Stmt::ObjCStringLiteralClass: + case Stmt::CXXBindTemporaryExprClass: + case Stmt::CXXNullPtrLiteralExprClass: { + Bldr.takeNodes(Pred); + ExplodedNodeSet preVisit; + getCheckerManager().runCheckersForPreStmt(preVisit, Pred, S, *this); + getCheckerManager().runCheckersForPostStmt(Dst, preVisit, S, *this); + Bldr.addNodes(Dst); break; + } + + case Expr::ObjCArrayLiteralClass: + case Expr::ObjCDictionaryLiteralClass: { + Bldr.takeNodes(Pred); + + ExplodedNodeSet preVisit; + getCheckerManager().runCheckersForPreStmt(preVisit, Pred, S, *this); + + // FIXME: explicitly model with a region and the actual contents + // of the container. For now, conjure a symbol. + ExplodedNodeSet Tmp; + StmtNodeBuilder Bldr2(preVisit, Tmp, *currentBuilderContext); + + for (ExplodedNodeSet::iterator it = preVisit.begin(), et = preVisit.end(); + it != et; ++it) { + ExplodedNode *N = *it; + const Expr *Ex = cast<Expr>(S); + QualType resultType = Ex->getType(); + const LocationContext *LCtx = N->getLocationContext(); + SVal result = + svalBuilder.getConjuredSymbolVal(0, Ex, LCtx, resultType, + currentBuilderContext->getCurrentBlockCount()); + ProgramStateRef state = N->getState()->BindExpr(Ex, LCtx, result); + Bldr2.generateNode(S, N, state); + } + + getCheckerManager().runCheckersForPostStmt(Dst, Tmp, S, *this); + Bldr.addNodes(Dst); + break; + } case Stmt::ArraySubscriptExprClass: + Bldr.takeNodes(Pred); VisitLvalArraySubscriptExpr(cast<ArraySubscriptExpr>(S), Pred, Dst); + Bldr.addNodes(Dst); break; case Stmt::AsmStmtClass: + Bldr.takeNodes(Pred); VisitAsmStmt(cast<AsmStmt>(S), Pred, Dst); + Bldr.addNodes(Dst); break; - case Stmt::BlockDeclRefExprClass: { - const BlockDeclRefExpr *BE = cast<BlockDeclRefExpr>(S); - VisitCommonDeclRefExpr(BE, BE->getDecl(), Pred, Dst); - break; - } - case Stmt::BlockExprClass: + Bldr.takeNodes(Pred); VisitBlockExpr(cast<BlockExpr>(S), Pred, Dst); + Bldr.addNodes(Dst); break; case Stmt::BinaryOperatorClass: { const BinaryOperator* B = cast<BinaryOperator>(S); if (B->isLogicalOp()) { + Bldr.takeNodes(Pred); VisitLogicalExpr(B, Pred, Dst); + Bldr.addNodes(Dst); break; } else if (B->getOpcode() == BO_Comma) { - const ProgramState *state = Pred->getState(); - MakeNode(Dst, B, Pred, state->BindExpr(B, state->getSVal(B->getRHS()))); + ProgramStateRef state = Pred->getState(); + Bldr.generateNode(B, Pred, + state->BindExpr(B, Pred->getLocationContext(), + state->getSVal(B->getRHS(), + Pred->getLocationContext()))); break; } + Bldr.takeNodes(Pred); + if (AMgr.shouldEagerlyAssume() && (B->isRelationalOp() || B->isEqualityOp())) { ExplodedNodeSet Tmp; @@ -618,13 +704,24 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, else VisitBinaryOperator(cast<BinaryOperator>(S), Pred, Dst); + Bldr.addNodes(Dst); break; } case Stmt::CallExprClass: case Stmt::CXXOperatorCallExprClass: - case Stmt::CXXMemberCallExprClass: { + case Stmt::CXXMemberCallExprClass: + case Stmt::UserDefinedLiteralClass: { + Bldr.takeNodes(Pred); VisitCallExpr(cast<CallExpr>(S), Pred, Dst); + Bldr.addNodes(Dst); + break; + } + + case Stmt::CXXCatchStmtClass: { + Bldr.takeNodes(Pred); + VisitCXXCatchStmt(cast<CXXCatchStmt>(S), Pred, Dst); + Bldr.addNodes(Dst); break; } @@ -633,58 +730,78 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, const CXXConstructExpr *C = cast<CXXConstructExpr>(S); // For block-level CXXConstructExpr, we don't have a destination region. // Let VisitCXXConstructExpr() create one. + Bldr.takeNodes(Pred); VisitCXXConstructExpr(C, 0, Pred, Dst); + Bldr.addNodes(Dst); break; } case Stmt::CXXNewExprClass: { + Bldr.takeNodes(Pred); const CXXNewExpr *NE = cast<CXXNewExpr>(S); VisitCXXNewExpr(NE, Pred, Dst); + Bldr.addNodes(Dst); break; } case Stmt::CXXDeleteExprClass: { + Bldr.takeNodes(Pred); const CXXDeleteExpr *CDE = cast<CXXDeleteExpr>(S); VisitCXXDeleteExpr(CDE, Pred, Dst); + Bldr.addNodes(Dst); break; } // FIXME: ChooseExpr is really a constant. We need to fix // the CFG do not model them as explicit control-flow. case Stmt::ChooseExprClass: { // __builtin_choose_expr + Bldr.takeNodes(Pred); const ChooseExpr *C = cast<ChooseExpr>(S); VisitGuardedExpr(C, C->getLHS(), C->getRHS(), Pred, Dst); + Bldr.addNodes(Dst); break; } case Stmt::CompoundAssignOperatorClass: + Bldr.takeNodes(Pred); VisitBinaryOperator(cast<BinaryOperator>(S), Pred, Dst); + Bldr.addNodes(Dst); break; case Stmt::CompoundLiteralExprClass: + Bldr.takeNodes(Pred); VisitCompoundLiteralExpr(cast<CompoundLiteralExpr>(S), Pred, Dst); + Bldr.addNodes(Dst); break; case Stmt::BinaryConditionalOperatorClass: case Stmt::ConditionalOperatorClass: { // '?' operator + Bldr.takeNodes(Pred); const AbstractConditionalOperator *C = cast<AbstractConditionalOperator>(S); VisitGuardedExpr(C, C->getTrueExpr(), C->getFalseExpr(), Pred, Dst); + Bldr.addNodes(Dst); break; } case Stmt::CXXThisExprClass: + Bldr.takeNodes(Pred); VisitCXXThisExpr(cast<CXXThisExpr>(S), Pred, Dst); + Bldr.addNodes(Dst); break; case Stmt::DeclRefExprClass: { + Bldr.takeNodes(Pred); const DeclRefExpr *DE = cast<DeclRefExpr>(S); VisitCommonDeclRefExpr(DE, DE->getDecl(), Pred, Dst); + Bldr.addNodes(Dst); break; } case Stmt::DeclStmtClass: + Bldr.takeNodes(Pred); VisitDeclStmt(cast<DeclStmt>(S), Pred, Dst); + Bldr.addNodes(Dst); break; case Stmt::ImplicitCastExprClass: @@ -695,6 +812,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, case Stmt::CXXConstCastExprClass: case Stmt::CXXFunctionalCastExprClass: case Stmt::ObjCBridgedCastExprClass: { + Bldr.takeNodes(Pred); const CastExpr *C = cast<CastExpr>(S); // Handle the previsit checks. ExplodedNodeSet dstPrevisit; @@ -709,58 +827,98 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, // Handle the postvisit checks. getCheckerManager().runCheckersForPostStmt(Dst, dstExpr, C, *this); + Bldr.addNodes(Dst); break; } case Expr::MaterializeTemporaryExprClass: { + Bldr.takeNodes(Pred); const MaterializeTemporaryExpr *Materialize = cast<MaterializeTemporaryExpr>(S); - if (!Materialize->getType()->isRecordType()) - CreateCXXTemporaryObject(Materialize, Pred, Dst); + if (Materialize->getType()->isRecordType()) + Dst.Add(Pred); else - Visit(Materialize->GetTemporaryExpr(), Pred, Dst); + CreateCXXTemporaryObject(Materialize, Pred, Dst); + Bldr.addNodes(Dst); break; } case Stmt::InitListExprClass: + Bldr.takeNodes(Pred); VisitInitListExpr(cast<InitListExpr>(S), Pred, Dst); + Bldr.addNodes(Dst); break; case Stmt::MemberExprClass: + Bldr.takeNodes(Pred); VisitMemberExpr(cast<MemberExpr>(S), Pred, Dst); + Bldr.addNodes(Dst); break; + case Stmt::ObjCIvarRefExprClass: + Bldr.takeNodes(Pred); VisitLvalObjCIvarRefExpr(cast<ObjCIvarRefExpr>(S), Pred, Dst); + Bldr.addNodes(Dst); break; case Stmt::ObjCForCollectionStmtClass: + Bldr.takeNodes(Pred); VisitObjCForCollectionStmt(cast<ObjCForCollectionStmt>(S), Pred, Dst); + Bldr.addNodes(Dst); break; - case Stmt::ObjCMessageExprClass: - VisitObjCMessage(cast<ObjCMessageExpr>(S), Pred, Dst); + case Stmt::ObjCMessageExprClass: { + Bldr.takeNodes(Pred); + // Is this a property access? + const ParentMap &PM = Pred->getLocationContext()->getParentMap(); + const ObjCMessageExpr *ME = cast<ObjCMessageExpr>(S); + bool evaluated = false; + + if (const PseudoObjectExpr *PO = + dyn_cast_or_null<PseudoObjectExpr>(PM.getParent(S))) { + const Expr *syntactic = PO->getSyntacticForm(); + if (const ObjCPropertyRefExpr *PR = + dyn_cast<ObjCPropertyRefExpr>(syntactic)) { + bool isSetter = ME->getNumArgs() > 0; + VisitObjCMessage(ObjCMessage(ME, PR, isSetter), Pred, Dst); + evaluated = true; + } + else if (isa<BinaryOperator>(syntactic)) { + VisitObjCMessage(ObjCMessage(ME, 0, true), Pred, Dst); + } + } + + if (!evaluated) + VisitObjCMessage(ME, Pred, Dst); + + Bldr.addNodes(Dst); break; + } case Stmt::ObjCAtThrowStmtClass: { // FIXME: This is not complete. We basically treat @throw as // an abort. - SaveAndRestore<bool> OldSink(Builder->BuildSinks); - Builder->BuildSinks = true; - MakeNode(Dst, S, Pred, Pred->getState()); + Bldr.generateNode(S, Pred, Pred->getState()); break; } case Stmt::ReturnStmtClass: + Bldr.takeNodes(Pred); VisitReturnStmt(cast<ReturnStmt>(S), Pred, Dst); + Bldr.addNodes(Dst); break; case Stmt::OffsetOfExprClass: + Bldr.takeNodes(Pred); VisitOffsetOfExpr(cast<OffsetOfExpr>(S), Pred, Dst); + Bldr.addNodes(Dst); break; case Stmt::UnaryExprOrTypeTraitExprClass: + Bldr.takeNodes(Pred); VisitUnaryExprOrTypeTraitExpr(cast<UnaryExprOrTypeTraitExpr>(S), Pred, Dst); + Bldr.addNodes(Dst); break; case Stmt::StmtExprClass: { @@ -770,81 +928,154 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, // Empty statement expression. assert(SE->getType() == getContext().VoidTy && "Empty statement expression must have void type."); - Dst.Add(Pred); break; } if (Expr *LastExpr = dyn_cast<Expr>(*SE->getSubStmt()->body_rbegin())) { - const ProgramState *state = Pred->getState(); - MakeNode(Dst, SE, Pred, state->BindExpr(SE, state->getSVal(LastExpr))); + ProgramStateRef state = Pred->getState(); + Bldr.generateNode(SE, Pred, + state->BindExpr(SE, Pred->getLocationContext(), + state->getSVal(LastExpr, + Pred->getLocationContext()))); } - else - Dst.Add(Pred); - break; } - case Stmt::StringLiteralClass: { - const ProgramState *state = Pred->getState(); - SVal V = state->getLValue(cast<StringLiteral>(S)); - MakeNode(Dst, S, Pred, state->BindExpr(S, V)); - return; - } - case Stmt::UnaryOperatorClass: { + Bldr.takeNodes(Pred); const UnaryOperator *U = cast<UnaryOperator>(S); - if (AMgr.shouldEagerlyAssume()&&(U->getOpcode() == UO_LNot)) { + if (AMgr.shouldEagerlyAssume() && (U->getOpcode() == UO_LNot)) { ExplodedNodeSet Tmp; VisitUnaryOperator(U, Pred, Tmp); evalEagerlyAssume(Dst, Tmp, U); } else VisitUnaryOperator(U, Pred, Dst); + Bldr.addNodes(Dst); + break; + } + + case Stmt::PseudoObjectExprClass: { + Bldr.takeNodes(Pred); + ProgramStateRef state = Pred->getState(); + const PseudoObjectExpr *PE = cast<PseudoObjectExpr>(S); + if (const Expr *Result = PE->getResultExpr()) { + SVal V = state->getSVal(Result, Pred->getLocationContext()); + Bldr.generateNode(S, Pred, + state->BindExpr(S, Pred->getLocationContext(), V)); + } + else + Bldr.generateNode(S, Pred, + state->BindExpr(S, Pred->getLocationContext(), + UnknownVal())); + + Bldr.addNodes(Dst); break; } } } -//===----------------------------------------------------------------------===// -// Block entrance. (Update counters). -//===----------------------------------------------------------------------===// +bool ExprEngine::replayWithoutInlining(ExplodedNode *N, + const LocationContext *CalleeLC) { + const StackFrameContext *CalleeSF = CalleeLC->getCurrentStackFrame(); + const StackFrameContext *CallerSF = CalleeSF->getParent()->getCurrentStackFrame(); + assert(CalleeSF && CallerSF); + ExplodedNode *BeforeProcessingCall = 0; + + // Find the first node before we started processing the call expression. + while (N) { + ProgramPoint L = N->getLocation(); + BeforeProcessingCall = N; + N = N->pred_empty() ? NULL : *(N->pred_begin()); + + // Skip the nodes corresponding to the inlined code. + if (L.getLocationContext()->getCurrentStackFrame() != CallerSF) + continue; + // We reached the caller. Find the node right before we started + // processing the CallExpr. + if (isa<PostPurgeDeadSymbols>(L)) + continue; + if (const StmtPoint *SP = dyn_cast<StmtPoint>(&L)) + if (SP->getStmt() == CalleeSF->getCallSite()) + continue; + break; + } + + if (!BeforeProcessingCall) + return false; + + // TODO: Clean up the unneeded nodes. + + // Build an Epsilon node from which we will restart the analyzes. + const Stmt *CE = CalleeSF->getCallSite(); + ProgramPoint NewNodeLoc = + EpsilonPoint(BeforeProcessingCall->getLocationContext(), CE); + // Add the special flag to GDM to signal retrying with no inlining. + // Note, changing the state ensures that we are not going to cache out. + ProgramStateRef NewNodeState = BeforeProcessingCall->getState(); + NewNodeState = NewNodeState->set<ReplayWithoutInlining>((void*)CE); + + // Make the new node a successor of BeforeProcessingCall. + bool IsNew = false; + ExplodedNode *NewNode = G.getNode(NewNodeLoc, NewNodeState, false, &IsNew); + // We cached out at this point. Caching out is common due to us backtracking + // from the inlined function, which might spawn several paths. + if (!IsNew) + return true; + + NewNode->addPredecessor(BeforeProcessingCall, G); -void ExprEngine::processCFGBlockEntrance(ExplodedNodeSet &dstNodes, - GenericNodeBuilder<BlockEntrance> &nodeBuilder){ + // Add the new node to the work list. + Engine.enqueueStmtNode(NewNode, CalleeSF->getCallSiteBlock(), + CalleeSF->getIndex()); + NumTimesRetriedWithoutInlining++; + return true; +} + +/// Block entrance. (Update counters). +void ExprEngine::processCFGBlockEntrance(const BlockEdge &L, + NodeBuilderWithSinks &nodeBuilder) { // FIXME: Refactor this into a checker. - const CFGBlock *block = nodeBuilder.getProgramPoint().getBlock(); - ExplodedNode *pred = nodeBuilder.getPredecessor(); + ExplodedNode *pred = nodeBuilder.getContext().getPred(); - if (nodeBuilder.getBlockCounter().getNumVisited( - pred->getLocationContext()->getCurrentStackFrame(), - block->getBlockID()) >= AMgr.getMaxVisit()) { + if (nodeBuilder.getContext().getCurrentBlockCount() >= AMgr.getMaxVisit()) { static SimpleProgramPointTag tag("ExprEngine : Block count exceeded"); - nodeBuilder.generateNode(pred->getState(), pred, &tag, true); - } -} - -//===----------------------------------------------------------------------===// -// Generic node creation. -//===----------------------------------------------------------------------===// + const ExplodedNode *Sink = + nodeBuilder.generateNode(pred->getState(), pred, &tag, true); + + // Check if we stopped at the top level function or not. + // Root node should have the location context of the top most function. + const LocationContext *CalleeLC = pred->getLocation().getLocationContext(); + const LocationContext *CalleeSF = CalleeLC->getCurrentStackFrame(); + const LocationContext *RootLC = + (*G.roots_begin())->getLocation().getLocationContext(); + if (RootLC->getCurrentStackFrame() != CalleeSF) { + Engine.FunctionSummaries->markReachedMaxBlockCount(CalleeSF->getDecl()); + + // Re-run the call evaluation without inlining it, by storing the + // no-inlining policy in the state and enqueuing the new work item on + // the list. Replay should almost never fail. Use the stats to catch it + // if it does. + if ((!AMgr.NoRetryExhausted && replayWithoutInlining(pred, CalleeLC))) + return; + NumMaxBlockCountReachedInInlined++; + } else + NumMaxBlockCountReached++; -ExplodedNode *ExprEngine::MakeNode(ExplodedNodeSet &Dst, const Stmt *S, - ExplodedNode *Pred, const ProgramState *St, - ProgramPoint::Kind K, - const ProgramPointTag *tag) { - assert (Builder && "StmtNodeBuilder not present."); - SaveAndRestore<const ProgramPointTag*> OldTag(Builder->Tag); - Builder->Tag = tag; - return Builder->MakeNode(Dst, S, Pred, St, K); + // Make sink nodes as exhausted(for stats) only if retry failed. + Engine.blocksExhausted.push_back(std::make_pair(L, Sink)); + } } //===----------------------------------------------------------------------===// // Branch processing. //===----------------------------------------------------------------------===// -const ProgramState *ExprEngine::MarkBranch(const ProgramState *state, - const Stmt *Terminator, - bool branchTaken) { +ProgramStateRef ExprEngine::MarkBranch(ProgramStateRef state, + const Stmt *Terminator, + const LocationContext *LCtx, + bool branchTaken) { switch (Terminator->getStmtClass()) { default: @@ -867,7 +1098,7 @@ const ProgramState *ExprEngine::MarkBranch(const ProgramState *state, (Op == BO_LOr && !branchTaken) ? B->getRHS() : B->getLHS(); - return state->BindExpr(B, UndefinedVal(Ex)); + return state->BindExpr(B, LCtx, UndefinedVal(Ex)); } case Stmt::BinaryConditionalOperatorClass: @@ -885,7 +1116,7 @@ const ProgramState *ExprEngine::MarkBranch(const ProgramState *state, else Ex = C->getFalseExpr(); - return state->BindExpr(C, UndefinedVal(Ex)); + return state->BindExpr(C, LCtx, UndefinedVal(Ex)); } case Stmt::ChooseExprClass: { // ?: @@ -893,7 +1124,7 @@ const ProgramState *ExprEngine::MarkBranch(const ProgramState *state, const ChooseExpr *C = cast<ChooseExpr>(Terminator); const Expr *Ex = branchTaken ? C->getLHS() : C->getRHS(); - return state->BindExpr(C, UndefinedVal(Ex)); + return state->BindExpr(C, LCtx, UndefinedVal(Ex)); } } } @@ -904,8 +1135,9 @@ const ProgramState *ExprEngine::MarkBranch(const ProgramState *state, /// This function returns the SVal bound to Condition->IgnoreCasts if all the // cast(s) did was sign-extend the original value. static SVal RecoverCastedSymbol(ProgramStateManager& StateMgr, - const ProgramState *state, + ProgramStateRef state, const Stmt *Condition, + const LocationContext *LCtx, ASTContext &Ctx) { const Expr *Ex = dyn_cast<Expr>(Condition); @@ -936,15 +1168,22 @@ static SVal RecoverCastedSymbol(ProgramStateManager& StateMgr, if (!bitsInit || !T->isIntegerType() || Ctx.getTypeSize(T) > bits) return UnknownVal(); - return state->getSVal(Ex); + return state->getSVal(Ex, LCtx); } void ExprEngine::processBranch(const Stmt *Condition, const Stmt *Term, - BranchNodeBuilder& builder) { + NodeBuilderContext& BldCtx, + ExplodedNode *Pred, + ExplodedNodeSet &Dst, + const CFGBlock *DstT, + const CFGBlock *DstF) { + currentBuilderContext = &BldCtx; // Check for NULL conditions; e.g. "for(;;)" if (!Condition) { - builder.markInfeasible(false); + BranchNodeBuilder NullCondBldr(Pred, Dst, BldCtx, DstT, DstF); + NullCondBldr.markInfeasible(false); + NullCondBldr.generateNode(Pred->getState(), true, Pred); return; } @@ -952,65 +1191,84 @@ void ExprEngine::processBranch(const Stmt *Condition, const Stmt *Term, Condition->getLocStart(), "Error evaluating branch"); - getCheckerManager().runCheckersForBranchCondition(Condition, builder, *this); - - // If the branch condition is undefined, return; - if (!builder.isFeasible(true) && !builder.isFeasible(false)) + ExplodedNodeSet CheckersOutSet; + getCheckerManager().runCheckersForBranchCondition(Condition, CheckersOutSet, + Pred, *this); + // We generated only sinks. + if (CheckersOutSet.empty()) return; - const ProgramState *PrevState = builder.getState(); - SVal X = PrevState->getSVal(Condition); - - if (X.isUnknownOrUndef()) { - // Give it a chance to recover from unknown. - if (const Expr *Ex = dyn_cast<Expr>(Condition)) { - if (Ex->getType()->isIntegerType()) { - // Try to recover some path-sensitivity. Right now casts of symbolic - // integers that promote their values are currently not tracked well. - // If 'Condition' is such an expression, try and recover the - // underlying value and use that instead. - SVal recovered = RecoverCastedSymbol(getStateManager(), - builder.getState(), Condition, - getContext()); - - if (!recovered.isUnknown()) { - X = recovered; + BranchNodeBuilder builder(CheckersOutSet, Dst, BldCtx, DstT, DstF); + for (NodeBuilder::iterator I = CheckersOutSet.begin(), + E = CheckersOutSet.end(); E != I; ++I) { + ExplodedNode *PredI = *I; + + if (PredI->isSink()) + continue; + + ProgramStateRef PrevState = Pred->getState(); + SVal X = PrevState->getSVal(Condition, Pred->getLocationContext()); + + if (X.isUnknownOrUndef()) { + // Give it a chance to recover from unknown. + if (const Expr *Ex = dyn_cast<Expr>(Condition)) { + if (Ex->getType()->isIntegerType()) { + // Try to recover some path-sensitivity. Right now casts of symbolic + // integers that promote their values are currently not tracked well. + // If 'Condition' is such an expression, try and recover the + // underlying value and use that instead. + SVal recovered = RecoverCastedSymbol(getStateManager(), + PrevState, Condition, + Pred->getLocationContext(), + getContext()); + + if (!recovered.isUnknown()) { + X = recovered; + } } } } + + const LocationContext *LCtx = PredI->getLocationContext(); + // If the condition is still unknown, give up. if (X.isUnknownOrUndef()) { - builder.generateNode(MarkBranch(PrevState, Term, true), true); - builder.generateNode(MarkBranch(PrevState, Term, false), false); - return; + builder.generateNode(MarkBranch(PrevState, Term, LCtx, true), + true, PredI); + builder.generateNode(MarkBranch(PrevState, Term, LCtx, false), + false, PredI); + continue; } - } - DefinedSVal V = cast<DefinedSVal>(X); + DefinedSVal V = cast<DefinedSVal>(X); - // Process the true branch. - if (builder.isFeasible(true)) { - if (const ProgramState *state = PrevState->assume(V, true)) - builder.generateNode(MarkBranch(state, Term, true), true); - else - builder.markInfeasible(true); - } + // Process the true branch. + if (builder.isFeasible(true)) { + if (ProgramStateRef state = PrevState->assume(V, true)) + builder.generateNode(MarkBranch(state, Term, LCtx, true), + true, PredI); + else + builder.markInfeasible(true); + } - // Process the false branch. - if (builder.isFeasible(false)) { - if (const ProgramState *state = PrevState->assume(V, false)) - builder.generateNode(MarkBranch(state, Term, false), false); - else - builder.markInfeasible(false); + // Process the false branch. + if (builder.isFeasible(false)) { + if (ProgramStateRef state = PrevState->assume(V, false)) + builder.generateNode(MarkBranch(state, Term, LCtx, false), + false, PredI); + else + builder.markInfeasible(false); + } } + currentBuilderContext = 0; } /// processIndirectGoto - Called by CoreEngine. Used to generate successor /// nodes by processing the 'effects' of a computed goto jump. void ExprEngine::processIndirectGoto(IndirectGotoNodeBuilder &builder) { - const ProgramState *state = builder.getState(); - SVal V = state->getSVal(builder.getTarget()); + ProgramStateRef state = builder.getState(); + SVal V = state->getSVal(builder.getTarget(), builder.getLocationContext()); // Three possibilities: // @@ -1051,18 +1309,20 @@ void ExprEngine::processIndirectGoto(IndirectGotoNodeBuilder &builder) { /// ProcessEndPath - Called by CoreEngine. Used to generate end-of-path /// nodes when the control reaches the end of a function. -void ExprEngine::processEndOfFunction(EndOfFunctionNodeBuilder& builder) { - StateMgr.EndPath(builder.getState()); - getCheckerManager().runCheckersForEndPath(builder, *this); +void ExprEngine::processEndOfFunction(NodeBuilderContext& BC) { + StateMgr.EndPath(BC.Pred->getState()); + ExplodedNodeSet Dst; + getCheckerManager().runCheckersForEndPath(BC, Dst, *this); + Engine.enqueueEndOfFunction(Dst); } /// ProcessSwitch - Called by CoreEngine. Used to generate successor /// nodes by processing the 'effects' of a switch statement. void ExprEngine::processSwitch(SwitchNodeBuilder& builder) { typedef SwitchNodeBuilder::iterator iterator; - const ProgramState *state = builder.getState(); + ProgramStateRef state = builder.getState(); const Expr *CondE = builder.getCondition(); - SVal CondV_untested = state->getSVal(CondE); + SVal CondV_untested = state->getSVal(CondE, builder.getLocationContext()); if (CondV_untested.isUndef()) { //ExplodedNode* N = builder.generateDefaultCaseNode(state, true); @@ -1073,7 +1333,7 @@ void ExprEngine::processSwitch(SwitchNodeBuilder& builder) { } DefinedOrUnknownSVal CondV = cast<DefinedOrUnknownSVal>(CondV_untested); - const ProgramState *DefaultSt = state; + ProgramStateRef DefaultSt = state; iterator I = builder.begin(), EI = builder.end(); bool defaultIsFeasible = I == EI; @@ -1106,7 +1366,7 @@ void ExprEngine::processSwitch(SwitchNodeBuilder& builder) { CondV, CaseVal); // Now "assume" that the case matches. - if (const ProgramState *stateNew = state->assume(Res, true)) { + if (ProgramStateRef stateNew = state->assume(Res, true)) { builder.generateCaseStmtNode(I, stateNew); // If CondV evaluates to a constant, then we know that this @@ -1119,7 +1379,7 @@ void ExprEngine::processSwitch(SwitchNodeBuilder& builder) { // Now "assume" that the case doesn't match. Add this state // to the default state (if it is feasible). if (DefaultSt) { - if (const ProgramState *stateNew = DefaultSt->assume(Res, false)) { + if (ProgramStateRef stateNew = DefaultSt->assume(Res, false)) { defaultIsFeasible = true; DefaultSt = stateNew; } @@ -1166,7 +1426,10 @@ void ExprEngine::processSwitch(SwitchNodeBuilder& builder) { void ExprEngine::VisitCommonDeclRefExpr(const Expr *Ex, const NamedDecl *D, ExplodedNode *Pred, ExplodedNodeSet &Dst) { - const ProgramState *state = Pred->getState(); + StmtNodeBuilder Bldr(Pred, Dst, *currentBuilderContext); + + ProgramStateRef state = Pred->getState(); + const LocationContext *LCtx = Pred->getLocationContext(); if (const VarDecl *VD = dyn_cast<VarDecl>(D)) { assert(Ex->isLValue()); @@ -1181,22 +1444,29 @@ void ExprEngine::VisitCommonDeclRefExpr(const Expr *Ex, const NamedDecl *D, V = UnknownVal(); } - MakeNode(Dst, Ex, Pred, state->BindExpr(Ex, V), - ProgramPoint::PostLValueKind); + Bldr.generateNode(Ex, Pred, state->BindExpr(Ex, LCtx, V), false, 0, + ProgramPoint::PostLValueKind); return; } if (const EnumConstantDecl *ED = dyn_cast<EnumConstantDecl>(D)) { assert(!Ex->isLValue()); SVal V = svalBuilder.makeIntVal(ED->getInitVal()); - MakeNode(Dst, Ex, Pred, state->BindExpr(Ex, V)); + Bldr.generateNode(Ex, Pred, state->BindExpr(Ex, LCtx, V)); return; } if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) { SVal V = svalBuilder.getFunctionPointer(FD); - MakeNode(Dst, Ex, Pred, state->BindExpr(Ex, V), - ProgramPoint::PostLValueKind); + Bldr.generateNode(Ex, Pred, state->BindExpr(Ex, LCtx, V), false, 0, + ProgramPoint::PostLValueKind); + return; + } + if (isa<FieldDecl>(D)) { + // FIXME: Compute lvalue of fields. + Bldr.generateNode(Ex, Pred, state->BindExpr(Ex, LCtx, UnknownVal()), + false, 0, ProgramPoint::PostLValueKind); return; } + assert (false && "ValueDecl support for this ValueDecl not implemented."); } @@ -1213,24 +1483,33 @@ void ExprEngine::VisitLvalArraySubscriptExpr(const ArraySubscriptExpr *A, ExplodedNodeSet checkerPreStmt; getCheckerManager().runCheckersForPreStmt(checkerPreStmt, Pred, A, *this); + StmtNodeBuilder Bldr(checkerPreStmt, Dst, *currentBuilderContext); + for (ExplodedNodeSet::iterator it = checkerPreStmt.begin(), ei = checkerPreStmt.end(); it != ei; ++it) { - const ProgramState *state = (*it)->getState(); - SVal V = state->getLValue(A->getType(), state->getSVal(Idx), - state->getSVal(Base)); + const LocationContext *LCtx = (*it)->getLocationContext(); + ProgramStateRef state = (*it)->getState(); + SVal V = state->getLValue(A->getType(), + state->getSVal(Idx, LCtx), + state->getSVal(Base, LCtx)); assert(A->isLValue()); - MakeNode(Dst, A, *it, state->BindExpr(A, V), ProgramPoint::PostLValueKind); + Bldr.generateNode(A, *it, state->BindExpr(A, LCtx, V), + false, 0, ProgramPoint::PostLValueKind); } } /// VisitMemberExpr - Transfer function for member expressions. void ExprEngine::VisitMemberExpr(const MemberExpr *M, ExplodedNode *Pred, - ExplodedNodeSet &Dst) { + ExplodedNodeSet &TopDst) { + StmtNodeBuilder Bldr(Pred, TopDst, *currentBuilderContext); + ExplodedNodeSet Dst; Decl *member = M->getMemberDecl(); if (VarDecl *VD = dyn_cast<VarDecl>(member)) { assert(M->isLValue()); + Bldr.takeNodes(Pred); VisitCommonDeclRefExpr(M, VD, Pred, Dst); + Bldr.addNodes(Dst); return; } @@ -1239,15 +1518,16 @@ void ExprEngine::VisitMemberExpr(const MemberExpr *M, ExplodedNode *Pred, return; Expr *baseExpr = M->getBase()->IgnoreParens(); - const ProgramState *state = Pred->getState(); - SVal baseExprVal = state->getSVal(baseExpr); + ProgramStateRef state = Pred->getState(); + const LocationContext *LCtx = Pred->getLocationContext(); + SVal baseExprVal = state->getSVal(baseExpr, Pred->getLocationContext()); if (isa<nonloc::LazyCompoundVal>(baseExprVal) || isa<nonloc::CompoundVal>(baseExprVal) || // FIXME: This can originate by conjuring a symbol for an unknown // temporary struct object, see test/Analysis/fields.c: // (p = getit()).x isa<nonloc::SymbolVal>(baseExprVal)) { - MakeNode(Dst, M, Pred, state->BindExpr(M, UnknownVal())); + Bldr.generateNode(M, Pred, state->BindExpr(M, LCtx, UnknownVal())); return; } @@ -1258,9 +1538,13 @@ void ExprEngine::VisitMemberExpr(const MemberExpr *M, ExplodedNode *Pred, // For all other cases, compute an lvalue. SVal L = state->getLValue(field, baseExprVal); if (M->isLValue()) - MakeNode(Dst, M, Pred, state->BindExpr(M, L), ProgramPoint::PostLValueKind); - else - evalLoad(Dst, M, Pred, state, L); + Bldr.generateNode(M, Pred, state->BindExpr(M, LCtx, L), false, 0, + ProgramPoint::PostLValueKind); + else { + Bldr.takeNodes(Pred); + evalLoad(Dst, M, M, Pred, state, L); + Bldr.addNodes(Dst); + } } /// evalBind - Handle the semantics of binding a value to a specific location. @@ -1272,12 +1556,17 @@ void ExprEngine::evalBind(ExplodedNodeSet &Dst, const Stmt *StoreE, // Do a previsit of the bind. ExplodedNodeSet CheckedSet; getCheckerManager().runCheckersForBind(CheckedSet, Pred, location, Val, - StoreE, *this); + StoreE, *this, + ProgramPoint::PostStmtKind); + ExplodedNodeSet TmpDst; + StmtNodeBuilder Bldr(CheckedSet, TmpDst, *currentBuilderContext); + + const LocationContext *LC = Pred->getLocationContext(); for (ExplodedNodeSet::iterator I = CheckedSet.begin(), E = CheckedSet.end(); I!=E; ++I) { - - const ProgramState *state = (*I)->getState(); + ExplodedNode *PredI = *I; + ProgramStateRef state = PredI->getState(); if (atDeclInit) { const VarRegion *VR = @@ -1288,8 +1577,15 @@ void ExprEngine::evalBind(ExplodedNodeSet &Dst, const Stmt *StoreE, state = state->bindLoc(location, Val); } - MakeNode(Dst, StoreE, *I, state); + const MemRegion *LocReg = 0; + if (loc::MemRegionVal *LocRegVal = dyn_cast<loc::MemRegionVal>(&location)) + LocReg = LocRegVal->getRegion(); + + const ProgramPoint L = PostStore(StoreE, LC, LocReg, 0); + Bldr.generateNode(L, PredI, state, false); } + + Dst.insert(TmpDst); } /// evalStore - Handle the semantics of a store via an assignment. @@ -1303,24 +1599,19 @@ void ExprEngine::evalBind(ExplodedNodeSet &Dst, const Stmt *StoreE, void ExprEngine::evalStore(ExplodedNodeSet &Dst, const Expr *AssignE, const Expr *LocationE, ExplodedNode *Pred, - const ProgramState *state, SVal location, SVal Val, + ProgramStateRef state, SVal location, SVal Val, const ProgramPointTag *tag) { - - assert(Builder && "StmtNodeBuilder must be defined."); - // Proceed with the store. We use AssignE as the anchor for the PostStore // ProgramPoint if it is non-NULL, and LocationE otherwise. const Expr *StoreE = AssignE ? AssignE : LocationE; if (isa<loc::ObjCPropRef>(location)) { - loc::ObjCPropRef prop = cast<loc::ObjCPropRef>(location); - return VisitObjCMessage(ObjCPropertySetter(prop.getPropRefExpr(), - StoreE, Val), Pred, Dst); + assert(false); } // Evaluate the location (checks for bad dereferences). ExplodedNodeSet Tmp; - evalLocation(Tmp, LocationE, Pred, state, location, tag, false); + evalLocation(Tmp, AssignE, LocationE, Pred, state, location, tag, false); if (Tmp.empty()) return; @@ -1328,24 +1619,21 @@ void ExprEngine::evalStore(ExplodedNodeSet &Dst, const Expr *AssignE, if (location.isUndef()) return; - SaveAndRestore<ProgramPoint::Kind> OldSPointKind(Builder->PointKind, - ProgramPoint::PostStoreKind); - for (ExplodedNodeSet::iterator NI=Tmp.begin(), NE=Tmp.end(); NI!=NE; ++NI) - evalBind(Dst, StoreE, *NI, location, Val); + evalBind(Dst, StoreE, *NI, location, Val, false); } -void ExprEngine::evalLoad(ExplodedNodeSet &Dst, const Expr *Ex, - ExplodedNode *Pred, - const ProgramState *state, SVal location, - const ProgramPointTag *tag, QualType LoadTy) { +void ExprEngine::evalLoad(ExplodedNodeSet &Dst, + const Expr *NodeEx, + const Expr *BoundEx, + ExplodedNode *Pred, + ProgramStateRef state, + SVal location, + const ProgramPointTag *tag, + QualType LoadTy) +{ assert(!isa<NonLoc>(location) && "location cannot be a NonLoc."); - - if (isa<loc::ObjCPropRef>(location)) { - loc::ObjCPropRef prop = cast<loc::ObjCPropRef>(location); - return VisitObjCMessage(ObjCPropertyGetter(prop.getPropRefExpr(), Ex), - Pred, Dst); - } + assert(!isa<loc::ObjCPropRef>(location)); // Are we loading from a region? This actually results in two loads; one // to fetch the address of the referenced value and one to fetch the @@ -1358,72 +1646,84 @@ void ExprEngine::evalLoad(ExplodedNodeSet &Dst, const Expr *Ex, static SimpleProgramPointTag loadReferenceTag("ExprEngine : Load Reference"); ExplodedNodeSet Tmp; - evalLoadCommon(Tmp, Ex, Pred, state, location, &loadReferenceTag, + evalLoadCommon(Tmp, NodeEx, BoundEx, Pred, state, + location, &loadReferenceTag, getContext().getPointerType(RT->getPointeeType())); // Perform the load from the referenced value. for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end() ; I!=E; ++I) { state = (*I)->getState(); - location = state->getSVal(Ex); - evalLoadCommon(Dst, Ex, *I, state, location, tag, LoadTy); + location = state->getSVal(BoundEx, (*I)->getLocationContext()); + evalLoadCommon(Dst, NodeEx, BoundEx, *I, state, location, tag, LoadTy); } return; } } - evalLoadCommon(Dst, Ex, Pred, state, location, tag, LoadTy); + evalLoadCommon(Dst, NodeEx, BoundEx, Pred, state, location, tag, LoadTy); } -void ExprEngine::evalLoadCommon(ExplodedNodeSet &Dst, const Expr *Ex, - ExplodedNode *Pred, - const ProgramState *state, SVal location, - const ProgramPointTag *tag, QualType LoadTy) { - +void ExprEngine::evalLoadCommon(ExplodedNodeSet &Dst, + const Expr *NodeEx, + const Expr *BoundEx, + ExplodedNode *Pred, + ProgramStateRef state, + SVal location, + const ProgramPointTag *tag, + QualType LoadTy) { + assert(NodeEx); + assert(BoundEx); // Evaluate the location (checks for bad dereferences). ExplodedNodeSet Tmp; - evalLocation(Tmp, Ex, Pred, state, location, tag, true); - + evalLocation(Tmp, NodeEx, BoundEx, Pred, state, location, tag, true); if (Tmp.empty()) return; + StmtNodeBuilder Bldr(Tmp, Dst, *currentBuilderContext); if (location.isUndef()) return; - SaveAndRestore<ProgramPoint::Kind> OldSPointKind(Builder->PointKind); - // Proceed with the load. for (ExplodedNodeSet::iterator NI=Tmp.begin(), NE=Tmp.end(); NI!=NE; ++NI) { state = (*NI)->getState(); + const LocationContext *LCtx = (*NI)->getLocationContext(); if (location.isUnknown()) { // This is important. We must nuke the old binding. - MakeNode(Dst, Ex, *NI, state->BindExpr(Ex, UnknownVal()), - ProgramPoint::PostLoadKind, tag); + Bldr.generateNode(NodeEx, *NI, + state->BindExpr(BoundEx, LCtx, UnknownVal()), + false, tag, + ProgramPoint::PostLoadKind); } else { if (LoadTy.isNull()) - LoadTy = Ex->getType(); + LoadTy = BoundEx->getType(); SVal V = state->getSVal(cast<Loc>(location), LoadTy); - MakeNode(Dst, Ex, *NI, state->bindExprAndLocation(Ex, location, V), - ProgramPoint::PostLoadKind, tag); + Bldr.generateNode(NodeEx, *NI, + state->bindExprAndLocation(BoundEx, LCtx, location, V), + false, tag, ProgramPoint::PostLoadKind); } } } -void ExprEngine::evalLocation(ExplodedNodeSet &Dst, const Stmt *S, - ExplodedNode *Pred, - const ProgramState *state, SVal location, - const ProgramPointTag *tag, bool isLoad) { +void ExprEngine::evalLocation(ExplodedNodeSet &Dst, + const Stmt *NodeEx, + const Stmt *BoundEx, + ExplodedNode *Pred, + ProgramStateRef state, + SVal location, + const ProgramPointTag *tag, + bool isLoad) { + StmtNodeBuilder BldrTop(Pred, Dst, *currentBuilderContext); // Early checks for performance reason. if (location.isUnknown()) { - Dst.Add(Pred); return; } ExplodedNodeSet Src; - if (Pred->getState() == state) { - Src.Add(Pred); - } else { + BldrTop.takeNodes(Pred); + StmtNodeBuilder Bldr(Pred, Src, *currentBuilderContext); + if (Pred->getState() != state) { // Associate this new state with an ExplodedNode. // FIXME: If I pass null tag, the graph is incorrect, e.g for // int *p; @@ -1435,88 +1735,12 @@ void ExprEngine::evalLocation(ExplodedNodeSet &Dst, const Stmt *S, // FIXME: why is 'tag' not used instead of etag? static SimpleProgramPointTag etag("ExprEngine: Location"); - - ExplodedNode *N = Builder->generateNode(S, state, Pred, &etag); - Src.Add(N ? N : Pred); + Bldr.generateNode(NodeEx, Pred, state, false, &etag); } - getCheckerManager().runCheckersForLocation(Dst, Src, location, isLoad, S, - *this); -} - -bool ExprEngine::InlineCall(ExplodedNodeSet &Dst, const CallExpr *CE, - ExplodedNode *Pred) { - return false; - - // Inlining isn't correct right now because we: - // (a) don't generate CallExit nodes. - // (b) we need a way to postpone doing post-visits of CallExprs until - // the CallExit. This means we need CallExits for the non-inline - // cases as well. - -#if 0 - const ProgramState *state = Pred->getState(); - const Expr *Callee = CE->getCallee(); - SVal L = state->getSVal(Callee); - - const FunctionDecl *FD = L.getAsFunctionDecl(); - if (!FD) - return false; - - // Specially handle CXXMethods. - const CXXMethodDecl *methodDecl = 0; - - switch (CE->getStmtClass()) { - default: break; - case Stmt::CXXOperatorCallExprClass: { - const CXXOperatorCallExpr *opCall = cast<CXXOperatorCallExpr>(CE); - methodDecl = - dyn_cast_or_null<CXXMethodDecl>(opCall->getCalleeDecl()); - break; - } - case Stmt::CXXMemberCallExprClass: { - const CXXMemberCallExpr *memberCall = cast<CXXMemberCallExpr>(CE); - const MemberExpr *memberExpr = - cast<MemberExpr>(memberCall->getCallee()->IgnoreParens()); - methodDecl = cast<CXXMethodDecl>(memberExpr->getMemberDecl()); - break; - } - } - - - - - // Check if the function definition is in the same translation unit. - if (FD->hasBody(FD)) { - const StackFrameContext *stackFrame = - AMgr.getStackFrame(AMgr.getAnalysisContext(FD), - Pred->getLocationContext(), - CE, Builder->getBlock(), Builder->getIndex()); - // Now we have the definition of the callee, create a CallEnter node. - CallEnter Loc(CE, stackFrame, Pred->getLocationContext()); - - ExplodedNode *N = Builder->generateNode(Loc, state, Pred); - Dst.Add(N); - return true; - } - - // Check if we can find the function definition in other translation units. - if (AMgr.hasIndexer()) { - AnalysisContext *C = AMgr.getAnalysisContextInAnotherTU(FD); - if (C == 0) - return false; - const StackFrameContext *stackFrame = - AMgr.getStackFrame(C, Pred->getLocationContext(), - CE, Builder->getBlock(), Builder->getIndex()); - CallEnter Loc(CE, stackFrame, Pred->getLocationContext()); - ExplodedNode *N = Builder->generateNode(Loc, state, Pred); - Dst.Add(N); - return true; - } - - // Generate the CallExit node. - - return false; -#endif + ExplodedNodeSet Tmp; + getCheckerManager().runCheckersForLocation(Tmp, Src, location, isLoad, + NodeEx, BoundEx, *this); + BldrTop.addNodes(Tmp); } std::pair<const ProgramPointTag *, const ProgramPointTag*> @@ -1528,108 +1752,67 @@ ExprEngine::getEagerlyAssumeTags() { } void ExprEngine::evalEagerlyAssume(ExplodedNodeSet &Dst, ExplodedNodeSet &Src, - const Expr *Ex) { - + const Expr *Ex) { + StmtNodeBuilder Bldr(Src, Dst, *currentBuilderContext); for (ExplodedNodeSet::iterator I=Src.begin(), E=Src.end(); I!=E; ++I) { ExplodedNode *Pred = *I; - // Test if the previous node was as the same expression. This can happen // when the expression fails to evaluate to anything meaningful and // (as an optimization) we don't generate a node. ProgramPoint P = Pred->getLocation(); if (!isa<PostStmt>(P) || cast<PostStmt>(P).getStmt() != Ex) { - Dst.Add(Pred); continue; } - const ProgramState *state = Pred->getState(); - SVal V = state->getSVal(Ex); - if (nonloc::SymExprVal *SEV = dyn_cast<nonloc::SymExprVal>(&V)) { + ProgramStateRef state = Pred->getState(); + SVal V = state->getSVal(Ex, Pred->getLocationContext()); + nonloc::SymbolVal *SEV = dyn_cast<nonloc::SymbolVal>(&V); + if (SEV && SEV->isExpression()) { const std::pair<const ProgramPointTag *, const ProgramPointTag*> &tags = getEagerlyAssumeTags(); // First assume that the condition is true. - if (const ProgramState *StateTrue = state->assume(*SEV, true)) { + if (ProgramStateRef StateTrue = state->assume(*SEV, true)) { SVal Val = svalBuilder.makeIntVal(1U, Ex->getType()); - StateTrue = StateTrue->BindExpr(Ex, Val); - Dst.Add(Builder->generateNode(Ex, StateTrue, Pred, tags.first)); + StateTrue = StateTrue->BindExpr(Ex, Pred->getLocationContext(), Val); + Bldr.generateNode(Ex, Pred, StateTrue, false, tags.first); } // Next, assume that the condition is false. - if (const ProgramState *StateFalse = state->assume(*SEV, false)) { + if (ProgramStateRef StateFalse = state->assume(*SEV, false)) { SVal Val = svalBuilder.makeIntVal(0U, Ex->getType()); - StateFalse = StateFalse->BindExpr(Ex, Val); - Dst.Add(Builder->generateNode(Ex, StateFalse, Pred, tags.second)); + StateFalse = StateFalse->BindExpr(Ex, Pred->getLocationContext(), Val); + Bldr.generateNode(Ex, Pred, StateFalse, false, tags.second); } } - else - Dst.Add(Pred); } } void ExprEngine::VisitAsmStmt(const AsmStmt *A, ExplodedNode *Pred, - ExplodedNodeSet &Dst) { - VisitAsmStmtHelperOutputs(A, A->begin_outputs(), A->end_outputs(), Pred, Dst); -} - -void ExprEngine::VisitAsmStmtHelperOutputs(const AsmStmt *A, - AsmStmt::const_outputs_iterator I, - AsmStmt::const_outputs_iterator E, - ExplodedNode *Pred, ExplodedNodeSet &Dst) { - if (I == E) { - VisitAsmStmtHelperInputs(A, A->begin_inputs(), A->end_inputs(), Pred, Dst); - return; - } - - ExplodedNodeSet Tmp; - Visit(*I, Pred, Tmp); - ++I; + ExplodedNodeSet &Dst) { + StmtNodeBuilder Bldr(Pred, Dst, *currentBuilderContext); + // We have processed both the inputs and the outputs. All of the outputs + // should evaluate to Locs. Nuke all of their values. - for (ExplodedNodeSet::iterator NI = Tmp.begin(), NE = Tmp.end();NI != NE;++NI) - VisitAsmStmtHelperOutputs(A, I, E, *NI, Dst); -} - -void ExprEngine::VisitAsmStmtHelperInputs(const AsmStmt *A, - AsmStmt::const_inputs_iterator I, - AsmStmt::const_inputs_iterator E, - ExplodedNode *Pred, - ExplodedNodeSet &Dst) { - if (I == E) { - - // We have processed both the inputs and the outputs. All of the outputs - // should evaluate to Locs. Nuke all of their values. - - // FIXME: Some day in the future it would be nice to allow a "plug-in" - // which interprets the inline asm and stores proper results in the - // outputs. + // FIXME: Some day in the future it would be nice to allow a "plug-in" + // which interprets the inline asm and stores proper results in the + // outputs. - const ProgramState *state = Pred->getState(); + ProgramStateRef state = Pred->getState(); - for (AsmStmt::const_outputs_iterator OI = A->begin_outputs(), - OE = A->end_outputs(); OI != OE; ++OI) { - - SVal X = state->getSVal(*OI); - assert (!isa<NonLoc>(X)); // Should be an Lval, or unknown, undef. - - if (isa<Loc>(X)) - state = state->bindLoc(cast<Loc>(X), UnknownVal()); - } + for (AsmStmt::const_outputs_iterator OI = A->begin_outputs(), + OE = A->end_outputs(); OI != OE; ++OI) { + SVal X = state->getSVal(*OI, Pred->getLocationContext()); + assert (!isa<NonLoc>(X)); // Should be an Lval, or unknown, undef. - MakeNode(Dst, A, Pred, state); - return; + if (isa<Loc>(X)) + state = state->bindLoc(cast<Loc>(X), UnknownVal()); } - ExplodedNodeSet Tmp; - Visit(*I, Pred, Tmp); - - ++I; - - for (ExplodedNodeSet::iterator NI = Tmp.begin(), NE = Tmp.end(); NI!=NE; ++NI) - VisitAsmStmtHelperInputs(A, I, E, *NI, Dst); + Bldr.generateNode(A, Pred, state); } - //===----------------------------------------------------------------------===// // Visualization. //===----------------------------------------------------------------------===// @@ -1694,6 +1877,10 @@ struct DOTGraphTraits<ExplodedNode*> : Out << "CallExit"; break; + case ProgramPoint::EpsilonKind: + Out << "Epsilon Point"; + break; + default: { if (StmtPoint *L = dyn_cast<StmtPoint>(&Loc)) { const Stmt *S = L->getStmt(); @@ -1811,10 +1998,10 @@ struct DOTGraphTraits<ExplodedNode*> : } } - const ProgramState *state = N->getState(); - Out << "\\|StateID: " << (void*) state + ProgramStateRef state = N->getState(); + Out << "\\|StateID: " << (void*) state.getPtr() << " NodeID: " << (void*) N << "\\|"; - state->printDOT(Out, *N->getLocationContext()->getCFG()); + state->printDOT(Out); Out << "\\l"; @@ -1852,9 +2039,7 @@ void ExprEngine::ViewGraph(bool trim) { // Iterate through the reports and get their nodes. for (BugReporter::EQClasses_iterator EI = BR.EQClasses_begin(), EE = BR.EQClasses_end(); EI != EE; ++EI) { - BugReportEquivClass& EQ = *EI; - const BugReport &R = **EQ.begin(); - ExplodedNode *N = const_cast<ExplodedNode*>(R.getErrorNode()); + ExplodedNode *N = const_cast<ExplodedNode*>(EI->begin()->getErrorNode()); if (N) Src.push_back(N); } diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp index 68ccc59..93e598a 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp @@ -13,7 +13,6 @@ #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" -#include "clang/Analysis/Support/SaveAndRestore.h" using namespace clang; using namespace ento; @@ -35,38 +34,40 @@ void ExprEngine::VisitBinaryOperator(const BinaryOperator* B, for (ExplodedNodeSet::iterator it=CheckedSet.begin(), ei=CheckedSet.end(); it != ei; ++it) { - const ProgramState *state = (*it)->getState(); - SVal LeftV = state->getSVal(LHS); - SVal RightV = state->getSVal(RHS); + ProgramStateRef state = (*it)->getState(); + const LocationContext *LCtx = (*it)->getLocationContext(); + SVal LeftV = state->getSVal(LHS, LCtx); + SVal RightV = state->getSVal(RHS, LCtx); BinaryOperator::Opcode Op = B->getOpcode(); if (Op == BO_Assign) { // EXPERIMENTAL: "Conjured" symbols. // FIXME: Handle structs. - if (RightV.isUnknown() || - !getConstraintManager().canReasonAbout(RightV)) { - unsigned Count = Builder->getCurrentBlockCount(); - RightV = svalBuilder.getConjuredSymbolVal(NULL, B->getRHS(), Count); + if (RightV.isUnknown()) { + unsigned Count = currentBuilderContext->getCurrentBlockCount(); + RightV = svalBuilder.getConjuredSymbolVal(NULL, B->getRHS(), LCtx, Count); } // Simulate the effects of a "store": bind the value of the RHS // to the L-Value represented by the LHS. SVal ExprVal = B->isLValue() ? LeftV : RightV; - evalStore(Tmp2, B, LHS, *it, state->BindExpr(B, ExprVal), LeftV, RightV); + evalStore(Tmp2, B, LHS, *it, state->BindExpr(B, LCtx, ExprVal), + LeftV, RightV); continue; } if (!B->isAssignmentOp()) { + StmtNodeBuilder Bldr(*it, Tmp2, *currentBuilderContext); // Process non-assignments except commas or short-circuited // logical expressions (LAnd and LOr). SVal Result = evalBinOp(state, Op, LeftV, RightV, B->getType()); if (Result.isUnknown()) { - MakeNode(Tmp2, B, *it, state); + Bldr.generateNode(B, *it, state); continue; } - state = state->BindExpr(B, Result); - MakeNode(Tmp2, B, *it, state); + state = state->BindExpr(B, LCtx, Result); + Bldr.generateNode(B, *it, state); continue; } @@ -91,13 +92,14 @@ void ExprEngine::VisitBinaryOperator(const BinaryOperator* B, // null dereferences, and so on. ExplodedNodeSet Tmp; SVal location = LeftV; - evalLoad(Tmp, LHS, *it, state, location); + evalLoad(Tmp, B, LHS, *it, state, location); for (ExplodedNodeSet::iterator I = Tmp.begin(), E = Tmp.end(); I != E; ++I) { state = (*I)->getState(); - SVal V = state->getSVal(LHS); + const LocationContext *LCtx = (*I)->getLocationContext(); + SVal V = state->getSVal(LHS, LCtx); // Get the computation type. QualType CTy = @@ -122,16 +124,15 @@ void ExprEngine::VisitBinaryOperator(const BinaryOperator* B, SVal LHSVal; - if (Result.isUnknown() || - !getConstraintManager().canReasonAbout(Result)) { + if (Result.isUnknown()) { - unsigned Count = Builder->getCurrentBlockCount(); + unsigned Count = currentBuilderContext->getCurrentBlockCount(); // The symbolic value is actually for the type of the left-hand side // expression, not the computation type, as this is the value the // LValue on the LHS will bind to. - LHSVal = svalBuilder.getConjuredSymbolVal(NULL, B->getRHS(), LTy, - Count); + LHSVal = svalBuilder.getConjuredSymbolVal(NULL, B->getRHS(), LCtx, + LTy, Count); // However, we need to convert the symbol to the computation type. Result = svalBuilder.evalCast(LHSVal, CTy, LTy); @@ -145,9 +146,9 @@ void ExprEngine::VisitBinaryOperator(const BinaryOperator* B, // In C++, assignment and compound assignment operators return an // lvalue. if (B->isLValue()) - state = state->BindExpr(B, location); + state = state->BindExpr(B, LCtx, location); else - state = state->BindExpr(B, Result); + state = state->BindExpr(B, LCtx, Result); evalStore(Tmp2, B, LHS, *I, state, location, LHSVal); } @@ -165,8 +166,12 @@ void ExprEngine::VisitBlockExpr(const BlockExpr *BE, ExplodedNode *Pred, Pred->getLocationContext()); ExplodedNodeSet Tmp; - MakeNode(Tmp, BE, Pred, Pred->getState()->BindExpr(BE, V), - ProgramPoint::PostLValueKind); + StmtNodeBuilder Bldr(Pred, Tmp, *currentBuilderContext); + Bldr.generateNode(BE, Pred, + Pred->getState()->BindExpr(BE, Pred->getLocationContext(), + V), + false, 0, + ProgramPoint::PostLValueKind); // FIXME: Move all post/pre visits to ::Visit(). getCheckerManager().runCheckersForPostStmt(Dst, Tmp, BE, *this); @@ -178,13 +183,13 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex, ExplodedNodeSet dstPreStmt; getCheckerManager().runCheckersForPreStmt(dstPreStmt, Pred, CastE, *this); - if (CastE->getCastKind() == CK_LValueToRValue || - CastE->getCastKind() == CK_GetObjCProperty) { + if (CastE->getCastKind() == CK_LValueToRValue) { for (ExplodedNodeSet::iterator I = dstPreStmt.begin(), E = dstPreStmt.end(); I!=E; ++I) { ExplodedNode *subExprNode = *I; - const ProgramState *state = subExprNode->getState(); - evalLoad(Dst, CastE, subExprNode, state, state->getSVal(Ex)); + ProgramStateRef state = subExprNode->getState(); + const LocationContext *LCtx = subExprNode->getLocationContext(); + evalLoad(Dst, CastE, CastE, subExprNode, state, state->getSVal(Ex, LCtx)); } return; } @@ -196,6 +201,7 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex, if (const ExplicitCastExpr *ExCast=dyn_cast_or_null<ExplicitCastExpr>(CastE)) T = ExCast->getTypeAsWritten(); + StmtNodeBuilder Bldr(dstPreStmt, Dst, *currentBuilderContext); for (ExplodedNodeSet::iterator I = dstPreStmt.begin(), E = dstPreStmt.end(); I != E; ++I) { @@ -204,10 +210,7 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex, switch (CastE->getCastKind()) { case CK_LValueToRValue: llvm_unreachable("LValueToRValue casts handled earlier."); - case CK_GetObjCProperty: - llvm_unreachable("GetObjCProperty casts handled earlier."); case CK_ToVoid: - Dst.Add(Pred); continue; // The analyzer doesn't do anything special with these casts, // since it understands retain/release semantics already. @@ -215,14 +218,21 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex, case CK_ARCConsumeObject: case CK_ARCReclaimReturnedObject: case CK_ARCExtendBlockObject: // Fall-through. + case CK_CopyAndAutoreleaseBlockObject: + // The analyser can ignore atomic casts for now, although some future + // checkers may want to make certain that you're not modifying the same + // value through atomic and nonatomic pointers. + case CK_AtomicToNonAtomic: + case CK_NonAtomicToAtomic: // True no-ops. case CK_NoOp: case CK_FunctionToPointerDecay: { // Copy the SVal of Ex to CastE. - const ProgramState *state = Pred->getState(); - SVal V = state->getSVal(Ex); - state = state->BindExpr(CastE, V); - MakeNode(Dst, CastE, Pred, state); + ProgramStateRef state = Pred->getState(); + const LocationContext *LCtx = Pred->getLocationContext(); + SVal V = state->getSVal(Ex, LCtx); + state = state->BindExpr(CastE, LCtx, V); + Bldr.generateNode(CastE, Pred, state); continue; } case CK_Dependent: @@ -254,30 +264,76 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex, case CK_AnyPointerToBlockPointerCast: case CK_ObjCObjectLValueCast: { // Delegate to SValBuilder to process. - const ProgramState *state = Pred->getState(); - SVal V = state->getSVal(Ex); + ProgramStateRef state = Pred->getState(); + const LocationContext *LCtx = Pred->getLocationContext(); + SVal V = state->getSVal(Ex, LCtx); V = svalBuilder.evalCast(V, T, ExTy); - state = state->BindExpr(CastE, V); - MakeNode(Dst, CastE, Pred, state); + state = state->BindExpr(CastE, LCtx, V); + Bldr.generateNode(CastE, Pred, state); continue; } case CK_DerivedToBase: case CK_UncheckedDerivedToBase: { // For DerivedToBase cast, delegate to the store manager. - const ProgramState *state = Pred->getState(); - SVal val = state->getSVal(Ex); + ProgramStateRef state = Pred->getState(); + const LocationContext *LCtx = Pred->getLocationContext(); + SVal val = state->getSVal(Ex, LCtx); val = getStoreManager().evalDerivedToBase(val, T); - state = state->BindExpr(CastE, val); - MakeNode(Dst, CastE, Pred, state); + state = state->BindExpr(CastE, LCtx, val); + Bldr.generateNode(CastE, Pred, state); + continue; + } + // Handle C++ dyn_cast. + case CK_Dynamic: { + ProgramStateRef state = Pred->getState(); + const LocationContext *LCtx = Pred->getLocationContext(); + SVal val = state->getSVal(Ex, LCtx); + + // Compute the type of the result. + QualType resultType = CastE->getType(); + if (CastE->isLValue()) + resultType = getContext().getPointerType(resultType); + + bool Failed = false; + + // Check if the value being cast evaluates to 0. + if (val.isZeroConstant()) + Failed = true; + // Else, evaluate the cast. + else + val = getStoreManager().evalDynamicCast(val, T, Failed); + + if (Failed) { + if (T->isReferenceType()) { + // A bad_cast exception is thrown if input value is a reference. + // Currently, we model this, by generating a sink. + Bldr.generateNode(CastE, Pred, state, true); + continue; + } else { + // If the cast fails on a pointer, bind to 0. + state = state->BindExpr(CastE, LCtx, svalBuilder.makeNull()); + } + } else { + // If we don't know if the cast succeeded, conjure a new symbol. + if (val.isUnknown()) { + DefinedOrUnknownSVal NewSym = svalBuilder.getConjuredSymbolVal(NULL, + CastE, LCtx, resultType, + currentBuilderContext->getCurrentBlockCount()); + state = state->BindExpr(CastE, LCtx, NewSym); + } else + // Else, bind to the derived region value. + state = state->BindExpr(CastE, LCtx, val); + } + Bldr.generateNode(CastE, Pred, state); continue; } - // Various C++ casts that are not handled yet. - case CK_Dynamic: + // Various C++ casts that are not handled yet. case CK_ToUnion: case CK_BaseToDerived: case CK_NullToMemberPointer: case CK_BaseToDerivedMemberPointer: case CK_DerivedToBaseMemberPointer: + case CK_ReinterpretMemberPointer: case CK_UserDefinedConversion: case CK_ConstructorConversion: case CK_VectorSplat: @@ -286,13 +342,12 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex, QualType resultType = CastE->getType(); if (CastE->isLValue()) resultType = getContext().getPointerType(resultType); - - SVal result = - svalBuilder.getConjuredSymbolVal(NULL, CastE, resultType, - Builder->getCurrentBlockCount()); - - const ProgramState *state = Pred->getState()->BindExpr(CastE, result); - MakeNode(Dst, CastE, Pred, state); + const LocationContext *LCtx = Pred->getLocationContext(); + SVal result = svalBuilder.getConjuredSymbolVal(NULL, CastE, LCtx, + resultType, currentBuilderContext->getCurrentBlockCount()); + ProgramStateRef state = Pred->getState()->BindExpr(CastE, LCtx, + result); + Bldr.generateNode(CastE, Pred, state); continue; } } @@ -302,18 +357,20 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex, void ExprEngine::VisitCompoundLiteralExpr(const CompoundLiteralExpr *CL, ExplodedNode *Pred, ExplodedNodeSet &Dst) { + StmtNodeBuilder B(Pred, Dst, *currentBuilderContext); + const InitListExpr *ILE = cast<InitListExpr>(CL->getInitializer()->IgnoreParens()); - const ProgramState *state = Pred->getState(); - SVal ILV = state->getSVal(ILE); + ProgramStateRef state = Pred->getState(); + SVal ILV = state->getSVal(ILE, Pred->getLocationContext()); const LocationContext *LC = Pred->getLocationContext(); state = state->bindCompoundLiteral(CL, LC, ILV); if (CL->isLValue()) - MakeNode(Dst, CL, Pred, state->BindExpr(CL, state->getLValue(CL, LC))); + B.generateNode(CL, Pred, state->BindExpr(CL, LC, state->getLValue(CL, LC))); else - MakeNode(Dst, CL, Pred, state->BindExpr(CL, ILV)); + B.generateNode(CL, Pred, state->BindExpr(CL, LC, ILV)); } void ExprEngine::VisitDeclStmt(const DeclStmt *DS, ExplodedNode *Pred, @@ -326,29 +383,32 @@ void ExprEngine::VisitDeclStmt(const DeclStmt *DS, ExplodedNode *Pred, // Assumption: The CFG has one DeclStmt per Decl. const Decl *D = *DS->decl_begin(); - if (!D || !isa<VarDecl>(D)) + if (!D || !isa<VarDecl>(D)) { + //TODO:AZ: remove explicit insertion after refactoring is done. + Dst.insert(Pred); return; + } // FIXME: all pre/post visits should eventually be handled by ::Visit(). ExplodedNodeSet dstPreVisit; getCheckerManager().runCheckersForPreStmt(dstPreVisit, Pred, DS, *this); + StmtNodeBuilder B(dstPreVisit, Dst, *currentBuilderContext); const VarDecl *VD = dyn_cast<VarDecl>(D); - for (ExplodedNodeSet::iterator I = dstPreVisit.begin(), E = dstPreVisit.end(); I!=E; ++I) { ExplodedNode *N = *I; - const ProgramState *state = N->getState(); + ProgramStateRef state = N->getState(); // Decls without InitExpr are not initialized explicitly. const LocationContext *LC = N->getLocationContext(); if (const Expr *InitEx = VD->getInit()) { - SVal InitVal = state->getSVal(InitEx); + SVal InitVal = state->getSVal(InitEx, Pred->getLocationContext()); // We bound the temp obj region to the CXXConstructExpr. Now recover // the lazy compound value when the variable is not a reference. - if (AMgr.getLangOptions().CPlusPlus && VD->getType()->isRecordType() && + if (AMgr.getLangOpts().CPlusPlus && VD->getType()->isRecordType() && !VD->getType()->isReferenceType() && isa<loc::MemRegionVal>(InitVal)){ InitVal = state->getSVal(cast<loc::MemRegionVal>(InitVal).getRegion()); assert(isa<nonloc::LazyCompoundVal>(InitVal)); @@ -356,40 +416,46 @@ void ExprEngine::VisitDeclStmt(const DeclStmt *DS, ExplodedNode *Pred, // Recover some path-sensitivity if a scalar value evaluated to // UnknownVal. - if ((InitVal.isUnknown() || - !getConstraintManager().canReasonAbout(InitVal)) && - !VD->getType()->isReferenceType()) { - InitVal = svalBuilder.getConjuredSymbolVal(NULL, InitEx, - Builder->getCurrentBlockCount()); + if (InitVal.isUnknown()) { + QualType Ty = InitEx->getType(); + if (InitEx->isLValue()) { + Ty = getContext().getPointerType(Ty); + } + + InitVal = svalBuilder.getConjuredSymbolVal(NULL, InitEx, LC, Ty, + currentBuilderContext->getCurrentBlockCount()); } - - evalBind(Dst, DS, N, state->getLValue(VD, LC), InitVal, true); + B.takeNodes(N); + ExplodedNodeSet Dst2; + evalBind(Dst2, DS, N, state->getLValue(VD, LC), InitVal, true); + B.addNodes(Dst2); } else { - MakeNode(Dst, DS, N, state->bindDeclWithNoInit(state->getRegion(VD, LC))); + B.generateNode(DS, N,state->bindDeclWithNoInit(state->getRegion(VD, LC))); } } } void ExprEngine::VisitLogicalExpr(const BinaryOperator* B, ExplodedNode *Pred, ExplodedNodeSet &Dst) { - assert(B->getOpcode() == BO_LAnd || B->getOpcode() == BO_LOr); - - const ProgramState *state = Pred->getState(); - SVal X = state->getSVal(B); + + StmtNodeBuilder Bldr(Pred, Dst, *currentBuilderContext); + ProgramStateRef state = Pred->getState(); + const LocationContext *LCtx = Pred->getLocationContext(); + SVal X = state->getSVal(B, LCtx); assert(X.isUndef()); const Expr *Ex = (const Expr*) cast<UndefinedVal>(X).getData(); assert(Ex); if (Ex == B->getRHS()) { - X = state->getSVal(Ex); + X = state->getSVal(Ex, LCtx); // Handle undefined values. if (X.isUndef()) { - MakeNode(Dst, B, Pred, state->BindExpr(B, X)); + Bldr.generateNode(B, Pred, state->BindExpr(B, LCtx, X)); return; } @@ -401,13 +467,15 @@ void ExprEngine::VisitLogicalExpr(const BinaryOperator* B, ExplodedNode *Pred, // value later when necessary. We don't have the machinery in place for // this right now, and since most logical expressions are used for branches, // the payoff is not likely to be large. Instead, we do eager evaluation. - if (const ProgramState *newState = state->assume(XD, true)) - MakeNode(Dst, B, Pred, - newState->BindExpr(B, svalBuilder.makeIntVal(1U, B->getType()))); + if (ProgramStateRef newState = state->assume(XD, true)) + Bldr.generateNode(B, Pred, + newState->BindExpr(B, LCtx, + svalBuilder.makeIntVal(1U, B->getType()))); - if (const ProgramState *newState = state->assume(XD, false)) - MakeNode(Dst, B, Pred, - newState->BindExpr(B, svalBuilder.makeIntVal(0U, B->getType()))); + if (ProgramStateRef newState = state->assume(XD, false)) + Bldr.generateNode(B, Pred, + newState->BindExpr(B, LCtx, + svalBuilder.makeIntVal(0U, B->getType()))); } else { // We took the LHS expression. Depending on whether we are '&&' or @@ -415,15 +483,17 @@ void ExprEngine::VisitLogicalExpr(const BinaryOperator* B, ExplodedNode *Pred, // the short-circuiting. X = svalBuilder.makeIntVal(B->getOpcode() == BO_LAnd ? 0U : 1U, B->getType()); - MakeNode(Dst, B, Pred, state->BindExpr(B, X)); + Bldr.generateNode(B, Pred, state->BindExpr(B, LCtx, X)); } } void ExprEngine::VisitInitListExpr(const InitListExpr *IE, ExplodedNode *Pred, ExplodedNodeSet &Dst) { + StmtNodeBuilder B(Pred, Dst, *currentBuilderContext); - const ProgramState *state = Pred->getState(); + ProgramStateRef state = Pred->getState(); + const LocationContext *LCtx = Pred->getLocationContext(); QualType T = getContext().getCanonicalType(IE->getType()); unsigned NumInitElements = IE->getNumInits(); @@ -434,24 +504,27 @@ void ExprEngine::VisitInitListExpr(const InitListExpr *IE, // e.g: static int* myArray[] = {}; if (NumInitElements == 0) { SVal V = svalBuilder.makeCompoundVal(T, vals); - MakeNode(Dst, IE, Pred, state->BindExpr(IE, V)); + B.generateNode(IE, Pred, state->BindExpr(IE, LCtx, V)); return; } for (InitListExpr::const_reverse_iterator it = IE->rbegin(), ei = IE->rend(); it != ei; ++it) { - vals = getBasicVals().consVals(state->getSVal(cast<Expr>(*it)), vals); + vals = getBasicVals().consVals(state->getSVal(cast<Expr>(*it), LCtx), + vals); } - MakeNode(Dst, IE, Pred, - state->BindExpr(IE, svalBuilder.makeCompoundVal(T, vals))); + B.generateNode(IE, Pred, + state->BindExpr(IE, LCtx, + svalBuilder.makeCompoundVal(T, vals))); return; } if (Loc::isLocType(T) || T->isIntegerType()) { assert(IE->getNumInits() == 1); const Expr *initEx = IE->getInit(0); - MakeNode(Dst, IE, Pred, state->BindExpr(IE, state->getSVal(initEx))); + B.generateNode(IE, Pred, state->BindExpr(IE, LCtx, + state->getSVal(initEx, LCtx))); return; } @@ -463,33 +536,35 @@ void ExprEngine::VisitGuardedExpr(const Expr *Ex, const Expr *R, ExplodedNode *Pred, ExplodedNodeSet &Dst) { + StmtNodeBuilder B(Pred, Dst, *currentBuilderContext); - const ProgramState *state = Pred->getState(); - SVal X = state->getSVal(Ex); + ProgramStateRef state = Pred->getState(); + const LocationContext *LCtx = Pred->getLocationContext(); + SVal X = state->getSVal(Ex, LCtx); assert (X.isUndef()); const Expr *SE = (Expr*) cast<UndefinedVal>(X).getData(); assert(SE); - X = state->getSVal(SE); + X = state->getSVal(SE, LCtx); // Make sure that we invalidate the previous binding. - MakeNode(Dst, Ex, Pred, state->BindExpr(Ex, X, true)); + B.generateNode(Ex, Pred, state->BindExpr(Ex, LCtx, X, true)); } void ExprEngine:: VisitOffsetOfExpr(const OffsetOfExpr *OOE, ExplodedNode *Pred, ExplodedNodeSet &Dst) { - Expr::EvalResult Res; - if (OOE->Evaluate(Res, getContext()) && Res.Val.isInt()) { - const APSInt &IV = Res.Val.getInt(); + StmtNodeBuilder B(Pred, Dst, *currentBuilderContext); + APSInt IV; + if (OOE->EvaluateAsInt(IV, getContext())) { assert(IV.getBitWidth() == getContext().getTypeSize(OOE->getType())); assert(OOE->getType()->isIntegerType()); assert(IV.isSigned() == OOE->getType()->isSignedIntegerOrEnumerationType()); SVal X = svalBuilder.makeIntVal(IV); - MakeNode(Dst, OOE, Pred, Pred->getState()->BindExpr(OOE, X)); - return; + B.generateNode(OOE, Pred, + Pred->getState()->BindExpr(OOE, Pred->getLocationContext(), + X)); } // FIXME: Handle the case where __builtin_offsetof is not a constant. - Dst.Add(Pred); } @@ -497,6 +572,7 @@ void ExprEngine:: VisitUnaryExprOrTypeTraitExpr(const UnaryExprOrTypeTraitExpr *Ex, ExplodedNode *Pred, ExplodedNodeSet &Dst) { + StmtNodeBuilder Bldr(Pred, Dst, *currentBuilderContext); QualType T = Ex->getTypeOfArgument(); @@ -506,78 +582,69 @@ VisitUnaryExprOrTypeTraitExpr(const UnaryExprOrTypeTraitExpr *Ex, // FIXME: Add support for VLA type arguments and VLA expressions. // When that happens, we should probably refactor VLASizeChecker's code. - Dst.Add(Pred); return; } else if (T->getAs<ObjCObjectType>()) { // Some code tries to take the sizeof an ObjCObjectType, relying that // the compiler has laid out its representation. Just report Unknown // for these. - Dst.Add(Pred); return; } } - Expr::EvalResult Result; - Ex->Evaluate(Result, getContext()); - CharUnits amt = CharUnits::fromQuantity(Result.Val.getInt().getZExtValue()); + APSInt Value = Ex->EvaluateKnownConstInt(getContext()); + CharUnits amt = CharUnits::fromQuantity(Value.getZExtValue()); - const ProgramState *state = Pred->getState(); - state = state->BindExpr(Ex, svalBuilder.makeIntVal(amt.getQuantity(), + ProgramStateRef state = Pred->getState(); + state = state->BindExpr(Ex, Pred->getLocationContext(), + svalBuilder.makeIntVal(amt.getQuantity(), Ex->getType())); - MakeNode(Dst, Ex, Pred, state); + Bldr.generateNode(Ex, Pred, state); } void ExprEngine::VisitUnaryOperator(const UnaryOperator* U, ExplodedNode *Pred, - ExplodedNodeSet &Dst) { + ExplodedNodeSet &Dst) { + StmtNodeBuilder Bldr(Pred, Dst, *currentBuilderContext); switch (U->getOpcode()) { - default: + default: { + Bldr.takeNodes(Pred); + ExplodedNodeSet Tmp; + VisitIncrementDecrementOperator(U, Pred, Tmp); + Bldr.addNodes(Tmp); + } break; case UO_Real: { const Expr *Ex = U->getSubExpr()->IgnoreParens(); - ExplodedNodeSet Tmp; - Visit(Ex, Pred, Tmp); - - for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end(); I!=E; ++I) { - - // FIXME: We don't have complex SValues yet. - if (Ex->getType()->isAnyComplexType()) { - // Just report "Unknown." - Dst.Add(*I); - continue; - } - // For all other types, UO_Real is an identity operation. - assert (U->getType() == Ex->getType()); - const ProgramState *state = (*I)->getState(); - MakeNode(Dst, U, *I, state->BindExpr(U, state->getSVal(Ex))); + // FIXME: We don't have complex SValues yet. + if (Ex->getType()->isAnyComplexType()) { + // Just report "Unknown." + break; } - - return; + + // For all other types, UO_Real is an identity operation. + assert (U->getType() == Ex->getType()); + ProgramStateRef state = Pred->getState(); + const LocationContext *LCtx = Pred->getLocationContext(); + Bldr.generateNode(U, Pred, state->BindExpr(U, LCtx, + state->getSVal(Ex, LCtx))); + break; } - case UO_Imag: { - + case UO_Imag: { const Expr *Ex = U->getSubExpr()->IgnoreParens(); - ExplodedNodeSet Tmp; - Visit(Ex, Pred, Tmp); - - for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end(); I!=E; ++I) { - // FIXME: We don't have complex SValues yet. - if (Ex->getType()->isAnyComplexType()) { - // Just report "Unknown." - Dst.Add(*I); - continue; - } - - // For all other types, UO_Imag returns 0. - const ProgramState *state = (*I)->getState(); - SVal X = svalBuilder.makeZeroVal(Ex->getType()); - MakeNode(Dst, U, *I, state->BindExpr(U, X)); + // FIXME: We don't have complex SValues yet. + if (Ex->getType()->isAnyComplexType()) { + // Just report "Unknown." + break; } - - return; + // For all other types, UO_Imag returns 0. + ProgramStateRef state = Pred->getState(); + const LocationContext *LCtx = Pred->getLocationContext(); + SVal X = svalBuilder.makeZeroVal(Ex->getType()); + Bldr.generateNode(U, Pred, state->BindExpr(U, LCtx, X)); + break; } case UO_Plus: @@ -586,22 +653,19 @@ void ExprEngine::VisitUnaryOperator(const UnaryOperator* U, case UO_Deref: case UO_AddrOf: case UO_Extension: { - + // FIXME: We can probably just have some magic in Environment::getSVal() + // that propagates values, instead of creating a new node here. + // // Unary "+" is a no-op, similar to a parentheses. We still have places // where it may be a block-level expression, so we need to // generate an extra node that just propagates the value of the - // subexpression. - + // subexpression. const Expr *Ex = U->getSubExpr()->IgnoreParens(); - ExplodedNodeSet Tmp; - Visit(Ex, Pred, Tmp); - - for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end(); I!=E; ++I) { - const ProgramState *state = (*I)->getState(); - MakeNode(Dst, U, *I, state->BindExpr(U, state->getSVal(Ex))); - } - - return; + ProgramStateRef state = Pred->getState(); + const LocationContext *LCtx = Pred->getLocationContext(); + Bldr.generateNode(U, Pred, state->BindExpr(U, LCtx, + state->getSVal(Ex, LCtx))); + break; } case UO_LNot: @@ -609,144 +673,139 @@ void ExprEngine::VisitUnaryOperator(const UnaryOperator* U, case UO_Not: { assert (!U->isLValue()); const Expr *Ex = U->getSubExpr()->IgnoreParens(); - ExplodedNodeSet Tmp; - Visit(Ex, Pred, Tmp); - - for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end(); I!=E; ++I) { - const ProgramState *state = (*I)->getState(); - - // Get the value of the subexpression. - SVal V = state->getSVal(Ex); + ProgramStateRef state = Pred->getState(); + const LocationContext *LCtx = Pred->getLocationContext(); - if (V.isUnknownOrUndef()) { - MakeNode(Dst, U, *I, state->BindExpr(U, V)); - continue; - } + // Get the value of the subexpression. + SVal V = state->getSVal(Ex, LCtx); - switch (U->getOpcode()) { - default: - llvm_unreachable("Invalid Opcode."); - - case UO_Not: - // FIXME: Do we need to handle promotions? - state = state->BindExpr(U, evalComplement(cast<NonLoc>(V))); - break; - - case UO_Minus: - // FIXME: Do we need to handle promotions? - state = state->BindExpr(U, evalMinus(cast<NonLoc>(V))); - break; - - case UO_LNot: - - // C99 6.5.3.3: "The expression !E is equivalent to (0==E)." - // - // Note: technically we do "E == 0", but this is the same in the - // transfer functions as "0 == E". - SVal Result; - - if (isa<Loc>(V)) { - Loc X = svalBuilder.makeNull(); - Result = evalBinOp(state, BO_EQ, cast<Loc>(V), X, - U->getType()); - } - else { - nonloc::ConcreteInt X(getBasicVals().getValue(0, Ex->getType())); - Result = evalBinOp(state, BO_EQ, cast<NonLoc>(V), X, - U->getType()); - } - - state = state->BindExpr(U, Result); - - break; - } + if (V.isUnknownOrUndef()) { + Bldr.generateNode(U, Pred, state->BindExpr(U, LCtx, V)); + break; + } - MakeNode(Dst, U, *I, state); + switch (U->getOpcode()) { + default: + llvm_unreachable("Invalid Opcode."); + case UO_Not: + // FIXME: Do we need to handle promotions? + state = state->BindExpr(U, LCtx, evalComplement(cast<NonLoc>(V))); + break; + case UO_Minus: + // FIXME: Do we need to handle promotions? + state = state->BindExpr(U, LCtx, evalMinus(cast<NonLoc>(V))); + break; + case UO_LNot: + // C99 6.5.3.3: "The expression !E is equivalent to (0==E)." + // + // Note: technically we do "E == 0", but this is the same in the + // transfer functions as "0 == E". + SVal Result; + if (isa<Loc>(V)) { + Loc X = svalBuilder.makeNull(); + Result = evalBinOp(state, BO_EQ, cast<Loc>(V), X, + U->getType()); + } + else { + nonloc::ConcreteInt X(getBasicVals().getValue(0, Ex->getType())); + Result = evalBinOp(state, BO_EQ, cast<NonLoc>(V), X, + U->getType()); + } + + state = state->BindExpr(U, LCtx, Result); + break; } - - return; + Bldr.generateNode(U, Pred, state); + break; } } - + +} + +void ExprEngine::VisitIncrementDecrementOperator(const UnaryOperator* U, + ExplodedNode *Pred, + ExplodedNodeSet &Dst) { // Handle ++ and -- (both pre- and post-increment). assert (U->isIncrementDecrementOp()); - ExplodedNodeSet Tmp; const Expr *Ex = U->getSubExpr()->IgnoreParens(); - Visit(Ex, Pred, Tmp); - for (ExplodedNodeSet::iterator I = Tmp.begin(), E = Tmp.end(); I!=E; ++I) { + const LocationContext *LCtx = Pred->getLocationContext(); + ProgramStateRef state = Pred->getState(); + SVal loc = state->getSVal(Ex, LCtx); + + // Perform a load. + ExplodedNodeSet Tmp; + evalLoad(Tmp, U, Ex, Pred, state, loc); + + ExplodedNodeSet Dst2; + StmtNodeBuilder Bldr(Tmp, Dst2, *currentBuilderContext); + for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end();I!=E;++I) { - const ProgramState *state = (*I)->getState(); - SVal loc = state->getSVal(Ex); + state = (*I)->getState(); + assert(LCtx == (*I)->getLocationContext()); + SVal V2_untested = state->getSVal(Ex, LCtx); - // Perform a load. - ExplodedNodeSet Tmp2; - evalLoad(Tmp2, Ex, *I, state, loc); + // Propagate unknown and undefined values. + if (V2_untested.isUnknownOrUndef()) { + Bldr.generateNode(U, *I, state->BindExpr(U, LCtx, V2_untested)); + continue; + } + DefinedSVal V2 = cast<DefinedSVal>(V2_untested); - for (ExplodedNodeSet::iterator I2=Tmp2.begin(), E2=Tmp2.end();I2!=E2;++I2) { - - state = (*I2)->getState(); - SVal V2_untested = state->getSVal(Ex); - - // Propagate unknown and undefined values. - if (V2_untested.isUnknownOrUndef()) { - MakeNode(Dst, U, *I2, state->BindExpr(U, V2_untested)); - continue; - } - DefinedSVal V2 = cast<DefinedSVal>(V2_untested); - - // Handle all other values. - BinaryOperator::Opcode Op = U->isIncrementOp() ? BO_Add - : BO_Sub; - - // If the UnaryOperator has non-location type, use its type to create the - // constant value. If the UnaryOperator has location type, create the - // constant with int type and pointer width. - SVal RHS; - - if (U->getType()->isAnyPointerType()) - RHS = svalBuilder.makeArrayIndex(1); - else - RHS = svalBuilder.makeIntVal(1, U->getType()); - - SVal Result = evalBinOp(state, Op, V2, RHS, U->getType()); - - // Conjure a new symbol if necessary to recover precision. - if (Result.isUnknown() || !getConstraintManager().canReasonAbout(Result)){ - DefinedOrUnknownSVal SymVal = - svalBuilder.getConjuredSymbolVal(NULL, Ex, - Builder->getCurrentBlockCount()); - Result = SymVal; + // Handle all other values. + BinaryOperator::Opcode Op = U->isIncrementOp() ? BO_Add : BO_Sub; + + // If the UnaryOperator has non-location type, use its type to create the + // constant value. If the UnaryOperator has location type, create the + // constant with int type and pointer width. + SVal RHS; + + if (U->getType()->isAnyPointerType()) + RHS = svalBuilder.makeArrayIndex(1); + else + RHS = svalBuilder.makeIntVal(1, U->getType()); + + SVal Result = evalBinOp(state, Op, V2, RHS, U->getType()); + + // Conjure a new symbol if necessary to recover precision. + if (Result.isUnknown()){ + DefinedOrUnknownSVal SymVal = + svalBuilder.getConjuredSymbolVal(NULL, Ex, LCtx, + currentBuilderContext->getCurrentBlockCount()); + Result = SymVal; + + // If the value is a location, ++/-- should always preserve + // non-nullness. Check if the original value was non-null, and if so + // propagate that constraint. + if (Loc::isLocType(U->getType())) { + DefinedOrUnknownSVal Constraint = + svalBuilder.evalEQ(state, V2,svalBuilder.makeZeroVal(U->getType())); - // If the value is a location, ++/-- should always preserve - // non-nullness. Check if the original value was non-null, and if so - // propagate that constraint. - if (Loc::isLocType(U->getType())) { - DefinedOrUnknownSVal Constraint = - svalBuilder.evalEQ(state, V2,svalBuilder.makeZeroVal(U->getType())); + if (!state->assume(Constraint, true)) { + // It isn't feasible for the original value to be null. + // Propagate this constraint. + Constraint = svalBuilder.evalEQ(state, SymVal, + svalBuilder.makeZeroVal(U->getType())); - if (!state->assume(Constraint, true)) { - // It isn't feasible for the original value to be null. - // Propagate this constraint. - Constraint = svalBuilder.evalEQ(state, SymVal, - svalBuilder.makeZeroVal(U->getType())); - - - state = state->assume(Constraint, false); - assert(state); - } + + state = state->assume(Constraint, false); + assert(state); } } - - // Since the lvalue-to-rvalue conversion is explicit in the AST, - // we bind an l-value if the operator is prefix and an lvalue (in C++). - if (U->isLValue()) - state = state->BindExpr(U, loc); - else - state = state->BindExpr(U, U->isPostfix() ? V2 : Result); - - // Perform the store. - evalStore(Dst, NULL, U, *I2, state, loc, Result); } + + // Since the lvalue-to-rvalue conversion is explicit in the AST, + // we bind an l-value if the operator is prefix and an lvalue (in C++). + if (U->isLValue()) + state = state->BindExpr(U, LCtx, loc); + else + state = state->BindExpr(U, LCtx, U->isPostfix() ? V2 : Result); + + // Perform the store. + Bldr.takeNodes(*I); + ExplodedNodeSet Dst3; + evalStore(Dst3, U, U, *I, state, loc, Result); + Bldr.addNodes(Dst3); } + Dst.insert(Dst2); } diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp index acb0074..a14a491 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp @@ -16,81 +16,11 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h" #include "clang/AST/DeclCXX.h" +#include "clang/AST/StmtCXX.h" using namespace clang; using namespace ento; -namespace { -class CallExprWLItem { -public: - CallExpr::const_arg_iterator I; - ExplodedNode *N; - - CallExprWLItem(const CallExpr::const_arg_iterator &i, ExplodedNode *n) - : I(i), N(n) {} -}; -} - -void ExprEngine::evalArguments(ConstExprIterator AI, ConstExprIterator AE, - const FunctionProtoType *FnType, - ExplodedNode *Pred, ExplodedNodeSet &Dst, - bool FstArgAsLValue) { - - - SmallVector<CallExprWLItem, 20> WorkList; - WorkList.reserve(AE - AI); - WorkList.push_back(CallExprWLItem(AI, Pred)); - - while (!WorkList.empty()) { - CallExprWLItem Item = WorkList.back(); - WorkList.pop_back(); - - if (Item.I == AE) { - Dst.insert(Item.N); - continue; - } - - // Evaluate the argument. - ExplodedNodeSet Tmp; - if (FstArgAsLValue) { - FstArgAsLValue = false; - } - - Visit(*Item.I, Item.N, Tmp); - ++(Item.I); - for (ExplodedNodeSet::iterator NI=Tmp.begin(), NE=Tmp.end(); NI != NE; ++NI) - WorkList.push_back(CallExprWLItem(Item.I, *NI)); - } -} - -void ExprEngine::evalCallee(const CallExpr *callExpr, - const ExplodedNodeSet &src, - ExplodedNodeSet &dest) { - - const Expr *callee = 0; - - switch (callExpr->getStmtClass()) { - case Stmt::CXXMemberCallExprClass: { - // Evaluate the implicit object argument that is the recipient of the - // call. - callee = cast<CXXMemberCallExpr>(callExpr)->getImplicitObjectArgument(); - - // FIXME: handle member pointers. - if (!callee) - return; - - break; - } - default: { - callee = callExpr->getCallee()->IgnoreParens(); - break; - } - } - - for (ExplodedNodeSet::iterator i = src.begin(), e = src.end(); i != e; ++i) - Visit(callee, *i, dest); -} - const CXXThisRegion *ExprEngine::getCXXThisRegion(const CXXRecordDecl *D, const StackFrameContext *SFC) { const Type *T = D->getTypeForDecl(); @@ -107,19 +37,26 @@ const CXXThisRegion *ExprEngine::getCXXThisRegion(const CXXMethodDecl *decl, void ExprEngine::CreateCXXTemporaryObject(const MaterializeTemporaryExpr *ME, ExplodedNode *Pred, ExplodedNodeSet &Dst) { + StmtNodeBuilder Bldr(Pred, Dst, *currentBuilderContext); const Expr *tempExpr = ME->GetTemporaryExpr()->IgnoreParens(); - const ProgramState *state = Pred->getState(); + ProgramStateRef state = Pred->getState(); + const LocationContext *LCtx = Pred->getLocationContext(); // Bind the temporary object to the value of the expression. Then bind // the expression to the location of the object. - SVal V = state->getSVal(tempExpr); + SVal V = state->getSVal(tempExpr, Pred->getLocationContext()); const MemRegion *R = - svalBuilder.getRegionManager().getCXXTempObjectRegion(ME, - Pred->getLocationContext()); + svalBuilder.getRegionManager().getCXXTempObjectRegion(ME, LCtx); state = state->bindLoc(loc::MemRegionVal(R), V); - MakeNode(Dst, ME, Pred, state->BindExpr(ME, loc::MemRegionVal(R))); + Bldr.generateNode(ME, Pred, state->BindExpr(ME, LCtx, loc::MemRegionVal(R))); +} + +void ExprEngine::VisitCXXTemporaryObjectExpr(const CXXTemporaryObjectExpr *expr, + ExplodedNode *Pred, + ExplodedNodeSet &Dst) { + VisitCXXConstructExpr(expr, 0, Pred, Dst); } void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *E, @@ -127,8 +64,10 @@ void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *E, ExplodedNode *Pred, ExplodedNodeSet &destNodes) { +#if 0 const CXXConstructorDecl *CD = E->getConstructor(); assert(CD); +#endif #if 0 if (!(CD->doesThisDeclarationHaveABody() && AMgr.shouldInlineCall())) @@ -136,26 +75,19 @@ void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *E, return; #endif - // Evaluate other arguments. - ExplodedNodeSet argsEvaluated; - const FunctionProtoType *FnType = CD->getType()->getAs<FunctionProtoType>(); - evalArguments(E->arg_begin(), E->arg_end(), FnType, Pred, argsEvaluated); - #if 0 // Is the constructor elidable? if (E->isElidable()) { - VisitAggExpr(E->getArg(0), destNodes, Pred, Dst); - // FIXME: this is here to force propagation if VisitAggExpr doesn't - if (destNodes.empty()) - destNodes.Add(Pred); + destNodes.Add(Pred); return; } #endif // Perform the previsit of the constructor. - ExplodedNodeSet destPreVisit; - getCheckerManager().runCheckersForPreStmt(destPreVisit, argsEvaluated, E, - *this); + ExplodedNodeSet SrcNodes; + SrcNodes.Add(Pred); + ExplodedNodeSet TmpNodes; + getCheckerManager().runCheckersForPreStmt(TmpNodes, SrcNodes, E, *this); // Evaluate the constructor. Currently we don't now allow checker-specific // implementations of specific constructors (as we do with ordinary @@ -174,7 +106,8 @@ void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *E, // parameter region. const StackFrameContext *SFC = AMgr.getStackFrame(CD, Pred->getLocationContext(), - E, Builder->getBlock(), Builder->getIndex()); + E, currentBuilderContext->getBlock(), + currentStmtIdx); // Create the 'this' region. const CXXThisRegion *ThisR = @@ -182,34 +115,33 @@ void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *E, CallEnter Loc(E, SFC, Pred->getLocationContext()); - - for (ExplodedNodeSet::iterator NI = argsEvaluated.begin(), - NE = argsEvaluated.end(); NI != NE; ++NI) { - const ProgramState *state = (*NI)->getState(); + StmtNodeBuilder Bldr(SrcNodes, TmpNodes, *currentBuilderContext); + for (ExplodedNodeSet::iterator NI = SrcNodes.begin(), + NE = SrcNodes.end(); NI != NE; ++NI) { + ProgramStateRef state = (*NI)->getState(); // Setup 'this' region, so that the ctor is evaluated on the object pointed // by 'Dest'. state = state->bindLoc(loc::MemRegionVal(ThisR), loc::MemRegionVal(Dest)); - if (ExplodedNode *N = Builder->generateNode(Loc, state, *NI)) - destNodes.Add(N); + Bldr.generateNode(Loc, *NI, state); } } #endif // Default semantics: invalidate all regions passed as arguments. ExplodedNodeSet destCall; - - for (ExplodedNodeSet::iterator - i = destPreVisit.begin(), e = destPreVisit.end(); - i != e; ++i) { - ExplodedNode *Pred = *i; - const LocationContext *LC = Pred->getLocationContext(); - const ProgramState *state = Pred->getState(); + StmtNodeBuilder Bldr(TmpNodes, destCall, *currentBuilderContext); + for (ExplodedNodeSet::iterator i = TmpNodes.begin(), e = TmpNodes.end(); + i != e; ++i) + { + ExplodedNode *Pred = *i; + const LocationContext *LC = Pred->getLocationContext(); + ProgramStateRef state = Pred->getState(); - state = invalidateArguments(state, CallOrObjCMessage(E, state), LC); - Builder->MakeNode(destCall, E, Pred, state); + state = invalidateArguments(state, CallOrObjCMessage(E, state, LC), LC); + Bldr.generateNode(E, Pred, state); + } } - // Do the post visit. getCheckerManager().runCheckersForPostStmt(destNodes, destCall, E, *this); } @@ -219,31 +151,33 @@ void ExprEngine::VisitCXXDestructor(const CXXDestructorDecl *DD, const Stmt *S, ExplodedNode *Pred, ExplodedNodeSet &Dst) { + StmtNodeBuilder Bldr(Pred, Dst, *currentBuilderContext); if (!(DD->doesThisDeclarationHaveABody() && AMgr.shouldInlineCall())) return; + // Create the context for 'this' region. - const StackFrameContext *SFC = AMgr.getStackFrame(DD, - Pred->getLocationContext(), - S, Builder->getBlock(), - Builder->getIndex()); + const StackFrameContext *SFC = + AnalysisDeclContexts.getContext(DD)-> + getStackFrame(Pred->getLocationContext(), S, + currentBuilderContext->getBlock(), currentStmtIdx); const CXXThisRegion *ThisR = getCXXThisRegion(DD->getParent(), SFC); CallEnter PP(S, SFC, Pred->getLocationContext()); - const ProgramState *state = Pred->getState(); + ProgramStateRef state = Pred->getState(); state = state->bindLoc(loc::MemRegionVal(ThisR), loc::MemRegionVal(Dest)); - ExplodedNode *N = Builder->generateNode(PP, state, Pred); - if (N) - Dst.Add(N); + Bldr.generateNode(PP, Pred, state); } void ExprEngine::VisitCXXNewExpr(const CXXNewExpr *CNE, ExplodedNode *Pred, ExplodedNodeSet &Dst) { + StmtNodeBuilder Bldr(Pred, Dst, *currentBuilderContext); - unsigned blockCount = Builder->getCurrentBlockCount(); + unsigned blockCount = currentBuilderContext->getCurrentBlockCount(); + const LocationContext *LCtx = Pred->getLocationContext(); DefinedOrUnknownSVal symVal = - svalBuilder.getConjuredSymbolVal(NULL, CNE, CNE->getType(), blockCount); + svalBuilder.getConjuredSymbolVal(NULL, CNE, LCtx, CNE->getType(), blockCount); const MemRegion *NewReg = cast<loc::MemRegionVal>(symVal).getRegion(); QualType ObjTy = CNE->getType()->getAs<PointerType>()->getPointeeType(); const ElementRegion *EleReg = @@ -252,26 +186,31 @@ void ExprEngine::VisitCXXNewExpr(const CXXNewExpr *CNE, ExplodedNode *Pred, if (CNE->isArray()) { // FIXME: allocating an array requires simulating the constructors. // For now, just return a symbolicated region. - const ProgramState *state = Pred->getState(); - state = state->BindExpr(CNE, loc::MemRegionVal(EleReg)); - MakeNode(Dst, CNE, Pred, state); + ProgramStateRef state = Pred->getState(); + state = state->BindExpr(CNE, Pred->getLocationContext(), + loc::MemRegionVal(EleReg)); + Bldr.generateNode(CNE, Pred, state); return; } + // FIXME: Update for AST changes. +#if 0 // Evaluate constructor arguments. const FunctionProtoType *FnType = NULL; const CXXConstructorDecl *CD = CNE->getConstructor(); if (CD) FnType = CD->getType()->getAs<FunctionProtoType>(); ExplodedNodeSet argsEvaluated; + Bldr.takeNodes(Pred); evalArguments(CNE->constructor_arg_begin(), CNE->constructor_arg_end(), FnType, Pred, argsEvaluated); + Bldr.addNodes(argsEvaluated); // Initialize the object region and bind the 'new' expression. for (ExplodedNodeSet::iterator I = argsEvaluated.begin(), E = argsEvaluated.end(); I != E; ++I) { - const ProgramState *state = (*I)->getState(); + ProgramStateRef state = (*I)->getState(); // Accumulate list of regions that are invalidated. // FIXME: Eventually we should unify the logic for constructor @@ -281,7 +220,7 @@ void ExprEngine::VisitCXXNewExpr(const CXXNewExpr *CNE, ExplodedNode *Pred, ai = CNE->constructor_arg_begin(), ae = CNE->constructor_arg_end(); ai != ae; ++ai) { - SVal val = state->getSVal(*ai); + SVal val = state->getSVal(*ai, (*I)->getLocationContext()); if (const MemRegion *region = val.getAsRegion()) regionsToInvalidate.push_back(region); } @@ -289,18 +228,21 @@ void ExprEngine::VisitCXXNewExpr(const CXXNewExpr *CNE, ExplodedNode *Pred, if (ObjTy->isRecordType()) { regionsToInvalidate.push_back(EleReg); // Invalidate the regions. + // TODO: Pass the call to new information as the last argument, to limit + // the globals which will get invalidated. state = state->invalidateRegions(regionsToInvalidate, - CNE, blockCount, 0, - /* invalidateGlobals = */ true); + CNE, blockCount, 0, 0); } else { // Invalidate the regions. + // TODO: Pass the call to new information as the last argument, to limit + // the globals which will get invalidated. state = state->invalidateRegions(regionsToInvalidate, - CNE, blockCount, 0, - /* invalidateGlobals = */ true); + CNE, blockCount, 0, 0); if (CNE->hasInitializer()) { - SVal V = state->getSVal(*CNE->constructor_arg_begin()); + SVal V = state->getSVal(*CNE->constructor_arg_begin(), + (*I)->getLocationContext()); state = state->bindLoc(loc::MemRegionVal(EleReg), V); } else { // Explicitly set to undefined, because currently we retrieve symbolic @@ -308,32 +250,51 @@ void ExprEngine::VisitCXXNewExpr(const CXXNewExpr *CNE, ExplodedNode *Pred, state = state->bindLoc(loc::MemRegionVal(EleReg), UndefinedVal()); } } - state = state->BindExpr(CNE, loc::MemRegionVal(EleReg)); - MakeNode(Dst, CNE, *I, state); + state = state->BindExpr(CNE, (*I)->getLocationContext(), + loc::MemRegionVal(EleReg)); + Bldr.generateNode(CNE, *I, state); } +#endif } void ExprEngine::VisitCXXDeleteExpr(const CXXDeleteExpr *CDE, - ExplodedNode *Pred,ExplodedNodeSet &Dst) { - // Should do more checking. - ExplodedNodeSet Argevaluated; - Visit(CDE->getArgument(), Pred, Argevaluated); - for (ExplodedNodeSet::iterator I = Argevaluated.begin(), - E = Argevaluated.end(); I != E; ++I) { - const ProgramState *state = (*I)->getState(); - MakeNode(Dst, CDE, *I, state); + ExplodedNode *Pred, ExplodedNodeSet &Dst) { + StmtNodeBuilder Bldr(Pred, Dst, *currentBuilderContext); + ProgramStateRef state = Pred->getState(); + Bldr.generateNode(CDE, Pred, state); +} + +void ExprEngine::VisitCXXCatchStmt(const CXXCatchStmt *CS, + ExplodedNode *Pred, + ExplodedNodeSet &Dst) { + const VarDecl *VD = CS->getExceptionDecl(); + if (!VD) { + Dst.Add(Pred); + return; } + + const LocationContext *LCtx = Pred->getLocationContext(); + SVal V = svalBuilder.getConjuredSymbolVal(CS, LCtx, VD->getType(), + currentBuilderContext->getCurrentBlockCount()); + ProgramStateRef state = Pred->getState(); + state = state->bindLoc(state->getLValue(VD, LCtx), V); + + StmtNodeBuilder Bldr(Pred, Dst, *currentBuilderContext); + Bldr.generateNode(CS, Pred, state); } void ExprEngine::VisitCXXThisExpr(const CXXThisExpr *TE, ExplodedNode *Pred, ExplodedNodeSet &Dst) { + StmtNodeBuilder Bldr(Pred, Dst, *currentBuilderContext); + // Get the this object region from StoreManager. + const LocationContext *LCtx = Pred->getLocationContext(); const MemRegion *R = svalBuilder.getRegionManager().getCXXThisRegion( getContext().getCanonicalType(TE->getType()), - Pred->getLocationContext()); + LCtx); - const ProgramState *state = Pred->getState(); + ProgramStateRef state = Pred->getState(); SVal V = state->getSVal(loc::MemRegionVal(R)); - MakeNode(Dst, TE, Pred, state->BindExpr(TE, V)); + Bldr.generateNode(TE, Pred, state->BindExpr(TE, LCtx, V)); } diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp index 6d377b9..b99bd54 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp @@ -15,40 +15,72 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h" #include "clang/AST/DeclCXX.h" -#include "clang/Analysis/Support/SaveAndRestore.h" +#include "llvm/ADT/SmallSet.h" +#include "llvm/Support/SaveAndRestore.h" using namespace clang; using namespace ento; -namespace { - // Trait class for recording returned expression in the state. - struct ReturnExpr { - static int TagInt; - typedef const Stmt *data_type; - }; - int ReturnExpr::TagInt; +void ExprEngine::processCallEnter(CallEnter CE, ExplodedNode *Pred) { + // Get the entry block in the CFG of the callee. + const StackFrameContext *calleeCtx = CE.getCalleeContext(); + const CFG *CalleeCFG = calleeCtx->getCFG(); + const CFGBlock *Entry = &(CalleeCFG->getEntry()); + + // Validate the CFG. + assert(Entry->empty()); + assert(Entry->succ_size() == 1); + + // Get the solitary sucessor. + const CFGBlock *Succ = *(Entry->succ_begin()); + + // Construct an edge representing the starting location in the callee. + BlockEdge Loc(Entry, Succ, calleeCtx); + + // Construct a new state which contains the mapping from actual to + // formal arguments. + const LocationContext *callerCtx = Pred->getLocationContext(); + ProgramStateRef state = Pred->getState()->enterStackFrame(callerCtx, + calleeCtx); + + // Construct a new node and add it to the worklist. + bool isNew; + ExplodedNode *Node = G.getNode(Loc, state, false, &isNew); + Node->addPredecessor(Pred, G); + if (isNew) + Engine.getWorkList()->enqueue(Node); } -void ExprEngine::processCallEnter(CallEnterNodeBuilder &B) { - const ProgramState *state = - B.getState()->enterStackFrame(B.getCalleeContext()); - B.generateNode(state); +static const ReturnStmt *getReturnStmt(const ExplodedNode *Node) { + while (Node) { + const ProgramPoint &PP = Node->getLocation(); + // Skip any BlockEdges. + if (isa<BlockEdge>(PP) || isa<CallExit>(PP)) { + assert(Node->pred_size() == 1); + Node = *Node->pred_begin(); + continue; + } + if (const StmtPoint *SP = dyn_cast<StmtPoint>(&PP)) { + const Stmt *S = SP->getStmt(); + return dyn_cast<ReturnStmt>(S); + } + break; + } + return 0; } -void ExprEngine::processCallExit(CallExitNodeBuilder &B) { - const ProgramState *state = B.getState(); - const ExplodedNode *Pred = B.getPredecessor(); +void ExprEngine::processCallExit(ExplodedNode *Pred) { + ProgramStateRef state = Pred->getState(); const StackFrameContext *calleeCtx = - cast<StackFrameContext>(Pred->getLocationContext()); + Pred->getLocationContext()->getCurrentStackFrame(); + const LocationContext *callerCtx = calleeCtx->getParent(); const Stmt *CE = calleeCtx->getCallSite(); // If the callee returns an expression, bind its value to CallExpr. - const Stmt *ReturnedExpr = state->get<ReturnExpr>(); - if (ReturnedExpr) { - SVal RetVal = state->getSVal(ReturnedExpr); - state = state->BindExpr(CE, RetVal); - // Clear the return expr GDM. - state = state->remove<ReturnExpr>(); + if (const ReturnStmt *RS = getReturnStmt(Pred)) { + const LocationContext *LCtx = Pred->getLocationContext(); + SVal V = state->getSVal(RS, LCtx); + state = state->BindExpr(CE, callerCtx, V); } // Bind the constructed object value to CXXConstructExpr. @@ -58,14 +90,197 @@ void ExprEngine::processCallExit(CallExitNodeBuilder &B) { SVal ThisV = state->getSVal(ThisR); // Always bind the region to the CXXConstructExpr. - state = state->BindExpr(CCE, ThisV); + state = state->BindExpr(CCE, Pred->getLocationContext(), ThisV); } - B.generateNode(state); + static SimpleProgramPointTag returnTag("ExprEngine : Call Return"); + PostStmt Loc(CE, callerCtx, &returnTag); + bool isNew; + ExplodedNode *N = G.getNode(Loc, state, false, &isNew); + N->addPredecessor(Pred, G); + if (!isNew) + return; + + // Perform the post-condition check of the CallExpr. + ExplodedNodeSet Dst; + NodeBuilderContext Ctx(Engine, calleeCtx->getCallSiteBlock(), N); + SaveAndRestore<const NodeBuilderContext*> NBCSave(currentBuilderContext, + &Ctx); + SaveAndRestore<unsigned> CBISave(currentStmtIdx, calleeCtx->getIndex()); + + getCheckerManager().runCheckersForPostStmt(Dst, N, CE, *this, + /* wasInlined */ true); + + // Enqueue the next element in the block. + for (ExplodedNodeSet::iterator I = Dst.begin(), E = Dst.end(); I != E; ++I) { + Engine.getWorkList()->enqueue(*I, + calleeCtx->getCallSiteBlock(), + calleeCtx->getIndex()+1); + } } -const ProgramState * -ExprEngine::invalidateArguments(const ProgramState *State, +static unsigned getNumberStackFrames(const LocationContext *LCtx) { + unsigned count = 0; + while (LCtx) { + if (isa<StackFrameContext>(LCtx)) + ++count; + LCtx = LCtx->getParent(); + } + return count; +} + +// Determine if we should inline the call. +bool ExprEngine::shouldInlineDecl(const FunctionDecl *FD, ExplodedNode *Pred) { + AnalysisDeclContext *CalleeADC = AMgr.getAnalysisDeclContext(FD); + const CFG *CalleeCFG = CalleeADC->getCFG(); + + if (getNumberStackFrames(Pred->getLocationContext()) + == AMgr.InlineMaxStackDepth) + return false; + + if (Engine.FunctionSummaries->hasReachedMaxBlockCount(FD)) + return false; + + if (CalleeCFG->getNumBlockIDs() > AMgr.InlineMaxFunctionSize) + return false; + + return true; +} + +// For now, skip inlining variadic functions. +// We also don't inline blocks. +static bool shouldInlineCallExpr(const CallExpr *CE, ExprEngine *E) { + if (!E->getAnalysisManager().shouldInlineCall()) + return false; + QualType callee = CE->getCallee()->getType(); + const FunctionProtoType *FT = 0; + if (const PointerType *PT = callee->getAs<PointerType>()) + FT = dyn_cast<FunctionProtoType>(PT->getPointeeType()); + else if (const BlockPointerType *BT = callee->getAs<BlockPointerType>()) { + // FIXME: inline blocks. + // FT = dyn_cast<FunctionProtoType>(BT->getPointeeType()); + (void) BT; + return false; + } + // If we have no prototype, assume the function is okay. + if (!FT) + return true; + + // Skip inlining of variadic functions. + return !FT->isVariadic(); +} + +bool ExprEngine::InlineCall(ExplodedNodeSet &Dst, + const CallExpr *CE, + ExplodedNode *Pred) { + if (!shouldInlineCallExpr(CE, this)) + return false; + + ProgramStateRef state = Pred->getState(); + const Expr *Callee = CE->getCallee(); + const FunctionDecl *FD = + state->getSVal(Callee, Pred->getLocationContext()).getAsFunctionDecl(); + if (!FD || !FD->hasBody(FD)) + return false; + + switch (CE->getStmtClass()) { + default: + // FIXME: Handle C++. + break; + case Stmt::CallExprClass: { + if (!shouldInlineDecl(FD, Pred)) + return false; + + // Construct a new stack frame for the callee. + AnalysisDeclContext *CalleeADC = AMgr.getAnalysisDeclContext(FD); + const StackFrameContext *CallerSFC = + Pred->getLocationContext()->getCurrentStackFrame(); + const StackFrameContext *CalleeSFC = + CalleeADC->getStackFrame(CallerSFC, CE, + currentBuilderContext->getBlock(), + currentStmtIdx); + + CallEnter Loc(CE, CalleeSFC, Pred->getLocationContext()); + bool isNew; + if (ExplodedNode *N = G.getNode(Loc, state, false, &isNew)) { + N->addPredecessor(Pred, G); + if (isNew) + Engine.getWorkList()->enqueue(N); + } + return true; + } + } + return false; +} + +static bool isPointerToConst(const ParmVarDecl *ParamDecl) { + QualType PointeeTy = ParamDecl->getOriginalType()->getPointeeType(); + if (PointeeTy != QualType() && PointeeTy.isConstQualified() && + !PointeeTy->isAnyPointerType() && !PointeeTy->isReferenceType()) { + return true; + } + return false; +} + +// Try to retrieve the function declaration and find the function parameter +// types which are pointers/references to a non-pointer const. +// We do not invalidate the corresponding argument regions. +static void findPtrToConstParams(llvm::SmallSet<unsigned, 1> &PreserveArgs, + const CallOrObjCMessage &Call) { + const Decl *CallDecl = Call.getDecl(); + if (!CallDecl) + return; + + if (const FunctionDecl *FDecl = dyn_cast<FunctionDecl>(CallDecl)) { + const IdentifierInfo *II = FDecl->getIdentifier(); + + // List the cases, where the region should be invalidated even if the + // argument is const. + if (II) { + StringRef FName = II->getName(); + // - 'int pthread_setspecific(ptheread_key k, const void *)' stores a + // value into thread local storage. The value can later be retrieved with + // 'void *ptheread_getspecific(pthread_key)'. So even thought the + // parameter is 'const void *', the region escapes through the call. + // - funopen - sets a buffer for future IO calls. + // - ObjC functions that end with "NoCopy" can free memory, of the passed + // in buffer. + // - Many CF containers allow objects to escape through custom + // allocators/deallocators upon container construction. + // - NSXXInsertXX, for example NSMapInsertIfAbsent, since they can + // be deallocated by NSMapRemove. + if (FName == "pthread_setspecific" || + FName == "funopen" || + FName.endswith("NoCopy") || + (FName.startswith("NS") && + (FName.find("Insert") != StringRef::npos)) || + Call.isCFCGAllowingEscape(FName)) + return; + } + + for (unsigned Idx = 0, E = Call.getNumArgs(); Idx != E; ++Idx) { + if (FDecl && Idx < FDecl->getNumParams()) { + if (isPointerToConst(FDecl->getParamDecl(Idx))) + PreserveArgs.insert(Idx); + } + } + return; + } + + if (const ObjCMethodDecl *MDecl = dyn_cast<ObjCMethodDecl>(CallDecl)) { + assert(MDecl->param_size() <= Call.getNumArgs()); + unsigned Idx = 0; + for (clang::ObjCMethodDecl::param_const_iterator + I = MDecl->param_begin(), E = MDecl->param_end(); I != E; ++I, ++Idx) { + if (isPointerToConst(*I)) + PreserveArgs.insert(Idx); + } + return; + } +} + +ProgramStateRef +ExprEngine::invalidateArguments(ProgramStateRef State, const CallOrObjCMessage &Call, const LocationContext *LC) { SmallVector<const MemRegion *, 8> RegionsToInvalidate; @@ -85,13 +300,21 @@ ExprEngine::invalidateArguments(const ProgramState *State, } else if (Call.isFunctionCall()) { // Block calls invalidate all captured-by-reference values. - if (const MemRegion *Callee = Call.getFunctionCallee().getAsRegion()) { + SVal CalleeVal = Call.getFunctionCallee(); + if (const MemRegion *Callee = CalleeVal.getAsRegion()) { if (isa<BlockDataRegion>(Callee)) RegionsToInvalidate.push_back(Callee); } } + // Indexes of arguments whose values will be preserved by the call. + llvm::SmallSet<unsigned, 1> PreserveArgs; + findPtrToConstParams(PreserveArgs, Call); + for (unsigned idx = 0, e = Call.getNumArgs(); idx != e; ++idx) { + if (PreserveArgs.count(idx)) + continue; + SVal V = Call.getArgSVal(idx); // If we are passing a location wrapped as an integer, unwrap it and @@ -105,7 +328,7 @@ ExprEngine::invalidateArguments(const ProgramState *State, // Invalidate the value of the variable passed by reference. // Are we dealing with an ElementRegion? If the element type is - // a basic integer type (e.g., char, int) and the underying region + // a basic integer type (e.g., char, int) and the underlying 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 @@ -116,7 +339,7 @@ ExprEngine::invalidateArguments(const ProgramState *State, // 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 + // appropriately 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()) { @@ -146,18 +369,29 @@ ExprEngine::invalidateArguments(const ProgramState *State, // to identify conjured symbols by an expression pair: the enclosing // expression (the context) and the expression itself. This should // disambiguate conjured symbols. - assert(Builder && "Invalidating arguments outside of a statement context"); - unsigned Count = Builder->getCurrentBlockCount(); + unsigned Count = currentBuilderContext->getCurrentBlockCount(); StoreManager::InvalidatedSymbols IS; // NOTE: Even if RegionsToInvalidate is empty, we may still invalidate // global variables. return State->invalidateRegions(RegionsToInvalidate, - Call.getOriginExpr(), Count, - &IS, doesInvalidateGlobals(Call)); + Call.getOriginExpr(), Count, LC, + &IS, &Call); } +static ProgramStateRef getReplayWithoutInliningState(ExplodedNode *&N, + const CallExpr *CE) { + void *ReplayState = N->getState()->get<ReplayWithoutInlining>(); + if (!ReplayState) + return 0; + const CallExpr *ReplayCE = reinterpret_cast<const CallExpr*>(ReplayState); + if (CE == ReplayCE) { + return N->getState()->remove<ReplayWithoutInlining>(); + } + return 0; +} + void ExprEngine::VisitCallExpr(const CallExpr *CE, ExplodedNode *Pred, ExplodedNodeSet &dst) { // Perform the previsit of the CallExpr. @@ -173,20 +407,21 @@ void ExprEngine::VisitCallExpr(const CallExpr *CE, ExplodedNode *Pred, DefaultEval(ExprEngine &eng, const CallExpr *ce) : Eng(eng), CE(ce) {} virtual void expandGraph(ExplodedNodeSet &Dst, ExplodedNode *Pred) { - // Should we inline the call? - if (Eng.getAnalysisManager().shouldInlineCall() && - Eng.InlineCall(Dst, CE, Pred)) { + + ProgramStateRef state = getReplayWithoutInliningState(Pred, CE); + + // First, try to inline the call. + if (state == 0 && Eng.InlineCall(Dst, CE, Pred)) return; - } // First handle the return value. - StmtNodeBuilder &Builder = Eng.getBuilder(); - assert(&Builder && "StmtNodeBuilder must be defined."); + StmtNodeBuilder Bldr(Pred, Dst, *Eng.currentBuilderContext); // Get the callee. const Expr *Callee = CE->getCallee()->IgnoreParens(); - const ProgramState *state = Pred->getState(); - SVal L = state->getSVal(Callee); + if (state == 0) + state = Pred->getState(); + SVal L = state->getSVal(Callee, Pred->getLocationContext()); // Figure out the result type. We do this dance to handle references. QualType ResultTy; @@ -200,18 +435,19 @@ void ExprEngine::VisitCallExpr(const CallExpr *CE, ExplodedNode *Pred, // Conjure a symbol value to use as the result. SValBuilder &SVB = Eng.getSValBuilder(); - unsigned Count = Builder.getCurrentBlockCount(); - SVal RetVal = SVB.getConjuredSymbolVal(0, CE, ResultTy, Count); + unsigned Count = Eng.currentBuilderContext->getCurrentBlockCount(); + const LocationContext *LCtx = Pred->getLocationContext(); + SVal RetVal = SVB.getConjuredSymbolVal(0, CE, LCtx, ResultTy, Count); // Generate a new state with the return value set. - state = state->BindExpr(CE, RetVal); + state = state->BindExpr(CE, LCtx, RetVal); // Invalidate the arguments. - const LocationContext *LC = Pred->getLocationContext(); - state = Eng.invalidateArguments(state, CallOrObjCMessage(CE, state), LC); + state = Eng.invalidateArguments(state, CallOrObjCMessage(CE, state, LCtx), + LCtx); // And make the result node. - Eng.MakeNode(Dst, CE, Pred, state); + Bldr.generateNode(CE, Pred, state); } }; @@ -231,23 +467,16 @@ void ExprEngine::VisitCallExpr(const CallExpr *CE, ExplodedNode *Pred, void ExprEngine::VisitReturnStmt(const ReturnStmt *RS, ExplodedNode *Pred, ExplodedNodeSet &Dst) { - ExplodedNodeSet Src; - if (const Expr *RetE = RS->getRetValue()) { - // Record the returned expression in the state. It will be used in - // processCallExit to bind the return value to the call expr. - { - static SimpleProgramPointTag tag("ExprEngine: ReturnStmt"); - const ProgramState *state = Pred->getState(); - state = state->set<ReturnExpr>(RetE); - Pred = Builder->generateNode(RetE, state, Pred, &tag); + + ExplodedNodeSet dstPreVisit; + getCheckerManager().runCheckersForPreStmt(dstPreVisit, Pred, RS, *this); + + StmtNodeBuilder B(dstPreVisit, Dst, *currentBuilderContext); + + if (RS->getRetValue()) { + for (ExplodedNodeSet::iterator it = dstPreVisit.begin(), + ei = dstPreVisit.end(); it != ei; ++it) { + B.generateNode(RS, *it, (*it)->getState()); } - // We may get a NULL Pred because we generated a cached node. - if (Pred) - Visit(RetE, Pred, Src); } - else { - Src.Add(Pred); - } - - getCheckerManager().runCheckersForPreStmt(Dst, Src, RS, *this); } diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp index e0560fd..c8ad70a 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp @@ -11,10 +11,10 @@ // //===----------------------------------------------------------------------===// +#include "clang/AST/StmtObjC.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h" -#include "clang/Analysis/Support/SaveAndRestore.h" using namespace clang; using namespace ento; @@ -22,13 +22,14 @@ using namespace ento; void ExprEngine::VisitLvalObjCIvarRefExpr(const ObjCIvarRefExpr *Ex, ExplodedNode *Pred, ExplodedNodeSet &Dst) { - - const ProgramState *state = Pred->getState(); - SVal baseVal = state->getSVal(Ex->getBase()); + ProgramStateRef state = Pred->getState(); + const LocationContext *LCtx = Pred->getLocationContext(); + SVal baseVal = state->getSVal(Ex->getBase(), LCtx); SVal location = state->getLValue(Ex->getDecl(), baseVal); ExplodedNodeSet dstIvar; - MakeNode(dstIvar, Ex, Pred, state->BindExpr(Ex, location)); + StmtNodeBuilder Bldr(Pred, dstIvar, *currentBuilderContext); + Bldr.generateNode(Ex, Pred, state->BindExpr(Ex, LCtx, location)); // Perform the post-condition check of the ObjCIvarRefExpr and store // the created nodes in 'Dst'. @@ -69,10 +70,11 @@ void ExprEngine::VisitObjCForCollectionStmt(const ObjCForCollectionStmt *S, // For now: simulate (1) by assigning either a symbol or nil if the // container is empty. Thus this transfer function will by default // result in state splitting. - + const Stmt *elem = S->getElement(); - const ProgramState *state = Pred->getState(); + ProgramStateRef state = Pred->getState(); SVal elementV; + StmtNodeBuilder Bldr(Pred, Dst, *currentBuilderContext); if (const DeclStmt *DS = dyn_cast<DeclStmt>(elem)) { const VarDecl *elemD = cast<VarDecl>(DS->getSingleDecl()); @@ -80,27 +82,27 @@ void ExprEngine::VisitObjCForCollectionStmt(const ObjCForCollectionStmt *S, elementV = state->getLValue(elemD, Pred->getLocationContext()); } else { - elementV = state->getSVal(elem); + elementV = state->getSVal(elem, Pred->getLocationContext()); } ExplodedNodeSet dstLocation; - evalLocation(dstLocation, elem, Pred, state, elementV, NULL, false); - - if (dstLocation.empty()) - return; + Bldr.takeNodes(Pred); + evalLocation(dstLocation, S, elem, Pred, state, elementV, NULL, false); + Bldr.addNodes(dstLocation); for (ExplodedNodeSet::iterator NI = dstLocation.begin(), NE = dstLocation.end(); NI!=NE; ++NI) { Pred = *NI; - const ProgramState *state = Pred->getState(); + ProgramStateRef state = Pred->getState(); + const LocationContext *LCtx = Pred->getLocationContext(); // Handle the case where the container still has elements. SVal TrueV = svalBuilder.makeTruthVal(1); - const ProgramState *hasElems = state->BindExpr(S, TrueV); + ProgramStateRef hasElems = state->BindExpr(S, LCtx, TrueV); // Handle the case where the container has no elements. SVal FalseV = svalBuilder.makeTruthVal(0); - const ProgramState *noElems = state->BindExpr(S, FalseV); + ProgramStateRef noElems = state->BindExpr(S, LCtx, FalseV); if (loc::MemRegionVal *MV = dyn_cast<loc::MemRegionVal>(&elementV)) if (const TypedValueRegion *R = @@ -110,8 +112,8 @@ void ExprEngine::VisitObjCForCollectionStmt(const ObjCForCollectionStmt *S, // For now, just 'conjure' up a symbolic value. QualType T = R->getValueType(); assert(Loc::isLocType(T)); - unsigned Count = Builder->getCurrentBlockCount(); - SymbolRef Sym = SymMgr.getConjuredSymbol(elem, T, Count); + unsigned Count = currentBuilderContext->getCurrentBlockCount(); + SymbolRef Sym = SymMgr.getConjuredSymbol(elem, LCtx, T, Count); SVal V = svalBuilder.makeLoc(Sym); hasElems = hasElems->bindLoc(elementV, V); @@ -121,8 +123,8 @@ void ExprEngine::VisitObjCForCollectionStmt(const ObjCForCollectionStmt *S, } // Create the new nodes. - MakeNode(Dst, S, Pred, hasElems); - MakeNode(Dst, S, Pred, noElems); + Bldr.generateNode(S, Pred, hasElems); + Bldr.generateNode(S, Pred, noElems); } } @@ -137,29 +139,27 @@ void ExprEngine::VisitObjCMessage(const ObjCMessage &msg, // Proceed with evaluate the message expression. ExplodedNodeSet dstEval; - + StmtNodeBuilder Bldr(dstPrevisit, dstEval, *currentBuilderContext); + for (ExplodedNodeSet::iterator DI = dstPrevisit.begin(), DE = dstPrevisit.end(); DI != DE; ++DI) { ExplodedNode *Pred = *DI; bool RaisesException = false; - SaveAndRestore<bool> OldSink(Builder->BuildSinks); - SaveOr OldHasGen(Builder->hasGeneratedNode); if (const Expr *Receiver = msg.getInstanceReceiver()) { - const ProgramState *state = Pred->getState(); - SVal recVal = state->getSVal(Receiver); + ProgramStateRef state = Pred->getState(); + SVal recVal = state->getSVal(Receiver, Pred->getLocationContext()); if (!recVal.isUndef()) { // Bifurcate the state into nil and non-nil ones. DefinedOrUnknownSVal receiverVal = cast<DefinedOrUnknownSVal>(recVal); - const ProgramState *notNilState, *nilState; + ProgramStateRef notNilState, nilState; llvm::tie(notNilState, nilState) = state->assume(receiverVal); // There are three cases: can be nil or non-nil, must be nil, must be // non-nil. We ignore must be nil, and merge the rest two into non-nil. if (nilState && !notNilState) { - dstEval.insert(Pred); continue; } @@ -168,13 +168,10 @@ void ExprEngine::VisitObjCMessage(const ObjCMessage &msg, if (msg.getSelector() == RaiseSel) RaisesException = true; - // Check if we raise an exception. For now treat these as sinks. + // If we raise an exception, for now treat it as a sink. // Eventually we will want to handle exceptions properly. - if (RaisesException) - Builder->BuildSinks = true; - // Dispatch to plug-in transfer function. - evalObjCMessage(dstEval, msg, Pred, notNilState); + evalObjCMessage(Bldr, msg, Pred, notNilState, RaisesException); } } else if (const ObjCInterfaceDecl *Iface = msg.getReceiverInterface()) { @@ -217,16 +214,11 @@ void ExprEngine::VisitObjCMessage(const ObjCMessage &msg, } } - // Check if we raise an exception. For now treat these as sinks. + // If we raise an exception, for now treat it as a sink. // Eventually we will want to handle exceptions properly. - if (RaisesException) - Builder->BuildSinks = true; - // Dispatch to plug-in transfer function. - evalObjCMessage(dstEval, msg, Pred, Pred->getState()); + evalObjCMessage(Bldr, msg, Pred, Pred->getState(), RaisesException); } - - assert(Builder->BuildSinks || Builder->hasGeneratedNode); } // Finally, perform the post-condition check of the ObjCMessageExpr and store @@ -234,11 +226,11 @@ void ExprEngine::VisitObjCMessage(const ObjCMessage &msg, getCheckerManager().runCheckersForPostObjCMessage(Dst, dstEval, msg, *this); } -void ExprEngine::evalObjCMessage(ExplodedNodeSet &Dst, const ObjCMessage &msg, +void ExprEngine::evalObjCMessage(StmtNodeBuilder &Bldr, + const ObjCMessage &msg, ExplodedNode *Pred, - const ProgramState *state) { - assert (Builder && "StmtNodeBuilder must be defined."); - + ProgramStateRef state, + bool GenSink) { // First handle the return value. SVal ReturnValue = UnknownVal(); @@ -252,7 +244,7 @@ void ExprEngine::evalObjCMessage(ExplodedNodeSet &Dst, const ObjCMessage &msg, // These methods return their receivers. const Expr *ReceiverE = msg.getInstanceReceiver(); if (ReceiverE) - ReturnValue = state->getSVal(ReceiverE); + ReturnValue = state->getSVal(ReceiverE, Pred->getLocationContext()); break; } } @@ -261,19 +253,21 @@ void ExprEngine::evalObjCMessage(ExplodedNodeSet &Dst, const ObjCMessage &msg, if (ReturnValue.isUnknown()) { SValBuilder &SVB = getSValBuilder(); QualType ResultTy = msg.getResultType(getContext()); - unsigned Count = Builder->getCurrentBlockCount(); + unsigned Count = currentBuilderContext->getCurrentBlockCount(); const Expr *CurrentE = cast<Expr>(currentStmt); - ReturnValue = SVB.getConjuredSymbolVal(NULL, CurrentE, ResultTy, Count); + const LocationContext *LCtx = Pred->getLocationContext(); + ReturnValue = SVB.getConjuredSymbolVal(NULL, CurrentE, LCtx, ResultTy, Count); } // Bind the return value. - state = state->BindExpr(currentStmt, ReturnValue); + const LocationContext *LCtx = Pred->getLocationContext(); + state = state->BindExpr(currentStmt, LCtx, ReturnValue); // Invalidate the arguments (and the receiver) - const LocationContext *LC = Pred->getLocationContext(); - state = invalidateArguments(state, CallOrObjCMessage(msg, state), LC); + state = invalidateArguments(state, CallOrObjCMessage(msg, state, LCtx), LCtx); // And create the new node. - MakeNode(Dst, msg.getOriginExpr(), Pred, state); + Bldr.generateNode(msg.getMessageExpr(), Pred, state, GenSink); + assert(Bldr.hasGeneratedNodes()); } diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/FunctionSummary.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/FunctionSummary.cpp new file mode 100644 index 0000000..c227aac --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/FunctionSummary.cpp @@ -0,0 +1,38 @@ +//== FunctionSummary.h - Stores summaries of functions. ------------*- 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 a summary of a function gathered/used by static analyzes. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Core/PathSensitive/FunctionSummary.h" +using namespace clang; +using namespace ento; + +FunctionSummariesTy::~FunctionSummariesTy() { + for (MapTy::iterator I = Map.begin(), E = Map.end(); I != E; ++I) { + delete(I->second); + } +} + +unsigned FunctionSummariesTy::getTotalNumBasicBlocks() { + unsigned Total = 0; + for (MapTy::iterator I = Map.begin(), E = Map.end(); I != E; ++I) { + Total += I->second->TotalBasicBlocks; + } + return Total; +} + +unsigned FunctionSummariesTy::getTotalNumVisitedBasicBlocks() { + unsigned Total = 0; + for (MapTy::iterator I = Map.begin(), E = Map.end(); I != E; ++I) { + Total += I->second->VisitedBasicBlocks.count(); + } + return Total; +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp index 0c4e427..629f1ea 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp @@ -39,15 +39,13 @@ class HTMLDiagnostics : public PathDiagnosticConsumer { llvm::sys::Path Directory, FilePrefix; bool createdDir, noDir; const Preprocessor &PP; - std::vector<const PathDiagnostic*> BatchedDiags; public: HTMLDiagnostics(const std::string& prefix, const Preprocessor &pp); virtual ~HTMLDiagnostics() { FlushDiagnostics(NULL); } - virtual void FlushDiagnostics(SmallVectorImpl<std::string> *FilesMade); - - virtual void HandlePathDiagnosticImpl(const PathDiagnostic* D); + virtual void FlushDiagnosticsImpl(std::vector<const PathDiagnostic *> &Diags, + SmallVectorImpl<std::string> *FilesMade); virtual StringRef getName() const { return "HTMLDiagnostics"; @@ -88,34 +86,49 @@ ento::createHTMLDiagnosticConsumer(const std::string& prefix, // Report processing. //===----------------------------------------------------------------------===// -void HTMLDiagnostics::HandlePathDiagnosticImpl(const PathDiagnostic* D) { - if (!D) - return; - - if (D->empty()) { - delete D; - return; +void HTMLDiagnostics::FlushDiagnosticsImpl( + std::vector<const PathDiagnostic *> &Diags, + SmallVectorImpl<std::string> *FilesMade) { + for (std::vector<const PathDiagnostic *>::iterator it = Diags.begin(), + et = Diags.end(); it != et; ++it) { + ReportDiag(**it, FilesMade); } - - const_cast<PathDiagnostic*>(D)->flattenLocations(); - BatchedDiags.push_back(D); } -void -HTMLDiagnostics::FlushDiagnostics(SmallVectorImpl<std::string> *FilesMade) -{ - while (!BatchedDiags.empty()) { - const PathDiagnostic* D = BatchedDiags.back(); - BatchedDiags.pop_back(); - ReportDiag(*D, FilesMade); - delete D; +static void flattenPath(PathPieces &primaryPath, PathPieces ¤tPath, + const PathPieces &oldPath) { + for (PathPieces::const_iterator it = oldPath.begin(), et = oldPath.end(); + it != et; ++it ) { + PathDiagnosticPiece *piece = it->getPtr(); + if (const PathDiagnosticCallPiece *call = + dyn_cast<PathDiagnosticCallPiece>(piece)) { + IntrusiveRefCntPtr<PathDiagnosticEventPiece> callEnter = + call->getCallEnterEvent(); + if (callEnter) + currentPath.push_back(callEnter); + flattenPath(primaryPath, primaryPath, call->path); + IntrusiveRefCntPtr<PathDiagnosticEventPiece> callExit = + call->getCallExitEvent(); + if (callExit) + currentPath.push_back(callExit); + continue; + } + if (PathDiagnosticMacroPiece *macro = + dyn_cast<PathDiagnosticMacroPiece>(piece)) { + currentPath.push_back(piece); + PathPieces newPath; + flattenPath(primaryPath, newPath, macro->subPieces); + macro->subPieces = newPath; + continue; + } + + currentPath.push_back(piece); } - - BatchedDiags.clear(); } void HTMLDiagnostics::ReportDiag(const PathDiagnostic& D, - SmallVectorImpl<std::string> *FilesMade){ + SmallVectorImpl<std::string> *FilesMade) { + // Create the HTML directory if it is missing. if (!createdDir) { createdDir = true; @@ -138,47 +151,29 @@ void HTMLDiagnostics::ReportDiag(const PathDiagnostic& D, if (noDir) return; - const SourceManager &SMgr = D.begin()->getLocation().getManager(); - FileID FID; - - // Verify that the entire path is from the same FileID. - for (PathDiagnostic::const_iterator I = D.begin(), E = D.end(); I != E; ++I) { - FullSourceLoc L = I->getLocation().asLocation().getExpansionLoc(); - - if (FID.isInvalid()) { - FID = SMgr.getFileID(L); - } else if (SMgr.getFileID(L) != FID) - return; // FIXME: Emit a warning? - - // Check the source ranges. - for (PathDiagnosticPiece::range_iterator RI=I->ranges_begin(), - RE=I->ranges_end(); RI!=RE; ++RI) { - - SourceLocation L = SMgr.getExpansionLoc(RI->getBegin()); - - if (!L.isFileID() || SMgr.getFileID(L) != FID) - return; // FIXME: Emit a warning? - - L = SMgr.getExpansionLoc(RI->getEnd()); - - if (!L.isFileID() || SMgr.getFileID(L) != FID) - return; // FIXME: Emit a warning? - } - } + // First flatten out the entire path to make it easier to use. + PathPieces path; + flattenPath(path, path, D.path); - if (FID.isInvalid()) - return; // FIXME: Emit a warning? + // The path as already been prechecked that all parts of the path are + // from the same file and that it is non-empty. + const SourceManager &SMgr = (*path.begin())->getLocation().getManager(); + assert(!path.empty()); + FileID FID = + (*path.begin())->getLocation().asLocation().getExpansionLoc().getFileID(); + assert(!FID.isInvalid()); // Create a new rewriter to generate HTML. - Rewriter R(const_cast<SourceManager&>(SMgr), PP.getLangOptions()); + Rewriter R(const_cast<SourceManager&>(SMgr), PP.getLangOpts()); // Process the path. - unsigned n = D.size(); + unsigned n = path.size(); unsigned max = n; - for (PathDiagnostic::const_reverse_iterator I=D.rbegin(), E=D.rend(); - I!=E; ++I, --n) - HandlePiece(R, FID, *I, n, max); + for (PathPieces::const_reverse_iterator I = path.rbegin(), + E = path.rend(); + I != E; ++I, --n) + HandlePiece(R, FID, **I, n, max); // Add line numbers, header, footer, etc. @@ -221,9 +216,9 @@ void HTMLDiagnostics::ReportDiag(const PathDiagnostic& D, << html::EscapeText(Entry->getName()) << "</td></tr>\n<tr><td class=\"rowname\">Location:</td><td>" "<a href=\"#EndPath\">line " - << (*D.rbegin()).getLocation().asLocation().getExpansionLineNumber() + << (*path.rbegin())->getLocation().asLocation().getExpansionLineNumber() << ", column " - << (*D.rbegin()).getLocation().asLocation().getExpansionColumnNumber() + << (*path.rbegin())->getLocation().asLocation().getExpansionColumnNumber() << "</a></td></tr>\n" "<tr><td class=\"rowname\">Description:</td><td>" << D.getDescription() << "</td></tr>\n"; @@ -261,10 +256,10 @@ void HTMLDiagnostics::ReportDiag(const PathDiagnostic& D, os << "\n<!-- BUGFILE " << DirName << Entry->getName() << " -->\n"; os << "\n<!-- BUGLINE " - << D.back()->getLocation().asLocation().getExpansionLineNumber() + << path.back()->getLocation().asLocation().getExpansionLineNumber() << " -->\n"; - os << "\n<!-- BUGPATHLENGTH " << D.size() << " -->\n"; + os << "\n<!-- BUGPATHLENGTH " << path.size() << " -->\n"; // Mark the end of the tags. os << "\n<!-- BUGMETAEND -->\n"; @@ -353,6 +348,8 @@ void HTMLDiagnostics::HandlePiece(Rewriter& R, FileID BugFileID, const char *Kind = 0; switch (P.getKind()) { + case PathDiagnosticPiece::Call: + llvm_unreachable("Calls should already be handled"); case PathDiagnosticPiece::Event: Kind = "Event"; break; case PathDiagnosticPiece::ControlFlow: Kind = "Control"; break; // Setting Kind to "Control" is intentional. @@ -445,7 +442,7 @@ void HTMLDiagnostics::HandlePiece(Rewriter& R, FileID BugFileID, assert(L.isFileID()); StringRef BufferInfo = L.getBufferData(); const char* MacroName = L.getDecomposedLoc().second + BufferInfo.data(); - Lexer rawLexer(L, PP.getLangOptions(), BufferInfo.begin(), + Lexer rawLexer(L, PP.getLangOpts(), BufferInfo.begin(), MacroName, BufferInfo.end()); Token TheTok; @@ -518,7 +515,7 @@ unsigned HTMLDiagnostics::ProcessMacroPiece(raw_ostream &os, const PathDiagnosticMacroPiece& P, unsigned num) { - for (PathDiagnosticMacroPiece::const_iterator I=P.begin(), E=P.end(); + for (PathPieces::const_iterator I = P.subPieces.begin(), E=P.subPieces.end(); I!=E; ++I) { if (const PathDiagnosticMacroPiece *MP = diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/MemRegion.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/MemRegion.cpp index 6f92da8..ed94c79 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/MemRegion.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/MemRegion.cpp @@ -18,7 +18,9 @@ #include "clang/Analysis/AnalysisContext.h" #include "clang/Analysis/Support/BumpVector.h" #include "clang/AST/CharUnits.h" +#include "clang/AST/DeclObjC.h" #include "clang/AST/RecordLayout.h" +#include "clang/Basic/SourceManager.h" #include "llvm/Support/raw_ostream.h" using namespace clang; @@ -219,6 +221,17 @@ DefinedOrUnknownSVal StringRegion::getExtent(SValBuilder &svalBuilder) const { svalBuilder.getArrayIndexType()); } +ObjCIvarRegion::ObjCIvarRegion(const ObjCIvarDecl *ivd, const MemRegion* sReg) + : DeclRegion(ivd, sReg, ObjCIvarRegionKind) {} + +const ObjCIvarDecl *ObjCIvarRegion::getDecl() const { + return cast<ObjCIvarDecl>(D); +} + +QualType ObjCIvarRegion::getValueType() const { + return getDecl()->getType(); +} + QualType CXXBaseObjectRegion::getValueType() const { return QualType(decl->getTypeForDecl(), 0); } @@ -249,6 +262,14 @@ void StringRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, ID.AddPointer(superRegion); } +void ObjCStringRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, + const ObjCStringLiteral* Str, + const MemRegion* superRegion) { + ID.AddInteger((unsigned) ObjCStringRegionKind); + ID.AddPointer(Str); + ID.AddPointer(superRegion); +} + void AllocaRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, const Expr *Ex, unsigned cnt, const MemRegion *) { @@ -285,6 +306,12 @@ void CXXThisRegion::Profile(llvm::FoldingSetNodeID &ID) const { CXXThisRegion::ProfileRegion(ID, ThisPointerTy, superRegion); } +void ObjCIvarRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, + const ObjCIvarDecl *ivd, + const MemRegion* superRegion) { + DeclRegion::ProfileRegion(ID, ivd, superRegion, ObjCIvarRegionKind); +} + void DeclRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, const Decl *D, const MemRegion* superRegion, Kind k) { ID.AddInteger((unsigned) k); @@ -337,7 +364,7 @@ void FunctionTextRegion::Profile(llvm::FoldingSetNodeID& ID) const { void BlockTextRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, const BlockDecl *BD, CanQualType, - const AnalysisContext *AC, + const AnalysisDeclContext *AC, const MemRegion*) { ID.AddInteger(MemRegion::BlockTextRegionKind); ID.AddPointer(BD); @@ -384,6 +411,20 @@ void CXXBaseObjectRegion::Profile(llvm::FoldingSetNodeID &ID) const { } //===----------------------------------------------------------------------===// +// Region anchors. +//===----------------------------------------------------------------------===// + +void GlobalsSpaceRegion::anchor() { } +void HeapSpaceRegion::anchor() { } +void UnknownSpaceRegion::anchor() { } +void StackLocalsSpaceRegion::anchor() { } +void StackArgumentsSpaceRegion::anchor() { } +void TypedRegion::anchor() { } +void TypedValueRegion::anchor() { } +void CodeTextRegion::anchor() { } +void SubRegion::anchor() { } + +//===----------------------------------------------------------------------===// // Region pretty-printing. //===----------------------------------------------------------------------===// @@ -445,16 +486,16 @@ void FieldRegion::dumpToStream(raw_ostream &os) const { os << superRegion << "->" << *getDecl(); } -void NonStaticGlobalSpaceRegion::dumpToStream(raw_ostream &os) const { - os << "NonStaticGlobalSpaceRegion"; -} - void ObjCIvarRegion::dumpToStream(raw_ostream &os) const { os << "ivar{" << superRegion << ',' << *getDecl() << '}'; } void StringRegion::dumpToStream(raw_ostream &os) const { - Str->printPretty(os, 0, PrintingPolicy(getContext().getLangOptions())); + Str->printPretty(os, 0, PrintingPolicy(getContext().getLangOpts())); +} + +void ObjCStringRegion::dumpToStream(raw_ostream &os) const { + Str->printPretty(os, 0, PrintingPolicy(getContext().getLangOpts())); } void SymbolicRegion::dumpToStream(raw_ostream &os) const { @@ -477,6 +518,35 @@ void StaticGlobalSpaceRegion::dumpToStream(raw_ostream &os) const { os << "StaticGlobalsMemSpace{" << CR << '}'; } +void NonStaticGlobalSpaceRegion::dumpToStream(raw_ostream &os) const { + os << "NonStaticGlobalSpaceRegion"; +} + +void GlobalInternalSpaceRegion::dumpToStream(raw_ostream &os) const { + os << "GlobalInternalSpaceRegion"; +} + +void GlobalSystemSpaceRegion::dumpToStream(raw_ostream &os) const { + os << "GlobalSystemSpaceRegion"; +} + +void GlobalImmutableSpaceRegion::dumpToStream(raw_ostream &os) const { + os << "GlobalImmutableSpaceRegion"; +} + +void MemRegion::dumpPretty(raw_ostream &os) const { + return; +} + +void VarRegion::dumpPretty(raw_ostream &os) const { + os << getDecl()->getName(); +} + +void FieldRegion::dumpPretty(raw_ostream &os) const { + superRegion->dumpPretty(os); + os << "->" << getDecl(); +} + //===----------------------------------------------------------------------===// // MemRegionManager methods. //===----------------------------------------------------------------------===// @@ -528,10 +598,18 @@ MemRegionManager::getStackArgumentsRegion(const StackFrameContext *STC) { } const GlobalsSpaceRegion -*MemRegionManager::getGlobalsRegion(const CodeTextRegion *CR) { - if (!CR) - return LazyAllocate(globals); +*MemRegionManager::getGlobalsRegion(MemRegion::Kind K, + const CodeTextRegion *CR) { + if (!CR) { + if (K == MemRegion::GlobalSystemSpaceRegionKind) + return LazyAllocate(SystemGlobals); + if (K == MemRegion::GlobalImmutableSpaceRegionKind) + return LazyAllocate(ImmutableGlobals); + assert(K == MemRegion::GlobalInternalSpaceRegionKind); + return LazyAllocate(InternalGlobals); + } + assert(K == MemRegion::StaticGlobalSpaceRegionKind); StaticGlobalSpaceRegion *&R = StaticsGlobalSpaceRegions[CR]; if (R) return R; @@ -556,18 +634,44 @@ const MemSpaceRegion *MemRegionManager::getCodeRegion() { //===----------------------------------------------------------------------===// // Constructing regions. //===----------------------------------------------------------------------===// - const StringRegion* MemRegionManager::getStringRegion(const StringLiteral* Str){ return getSubRegion<StringRegion>(Str, getGlobalsRegion()); } +const ObjCStringRegion * +MemRegionManager::getObjCStringRegion(const ObjCStringLiteral* Str){ + return getSubRegion<ObjCStringRegion>(Str, getGlobalsRegion()); +} + const VarRegion* MemRegionManager::getVarRegion(const VarDecl *D, const LocationContext *LC) { const MemRegion *sReg = 0; - if (D->hasGlobalStorage() && !D->isStaticLocal()) - sReg = getGlobalsRegion(); - else { + if (D->hasGlobalStorage() && !D->isStaticLocal()) { + + // First handle the globals defined in system headers. + if (C.getSourceManager().isInSystemHeader(D->getLocation())) { + // Whitelist the system globals which often DO GET modified, assume the + // rest are immutable. + if (D->getName().find("errno") != StringRef::npos) + sReg = getGlobalsRegion(MemRegion::GlobalSystemSpaceRegionKind); + else + sReg = getGlobalsRegion(MemRegion::GlobalImmutableSpaceRegionKind); + + // Treat other globals as GlobalInternal unless they are constants. + } else { + QualType GQT = D->getType(); + const Type *GT = GQT.getTypePtrOrNull(); + // TODO: We could walk the complex types here and see if everything is + // constified. + if (GT && GQT.isConstQualified() && GT->isArithmeticType()) + sReg = getGlobalsRegion(MemRegion::GlobalImmutableSpaceRegionKind); + else + sReg = getGlobalsRegion(); + } + + // Finally handle static locals. + } else { // FIXME: Once we implement scope handling, we will need to properly lookup // 'D' to the proper LocationContext. const DeclContext *DC = D->getDeclContext(); @@ -585,13 +689,15 @@ const VarRegion* MemRegionManager::getVarRegion(const VarDecl *D, assert(D->isStaticLocal()); const Decl *D = STC->getDecl(); if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) - sReg = getGlobalsRegion(getFunctionTextRegion(FD)); + sReg = getGlobalsRegion(MemRegion::StaticGlobalSpaceRegionKind, + getFunctionTextRegion(FD)); else if (const BlockDecl *BD = dyn_cast<BlockDecl>(D)) { const BlockTextRegion *BTR = getBlockTextRegion(BD, C.getCanonicalType(BD->getSignatureAsWritten()->getType()), - STC->getAnalysisContext()); - sReg = getGlobalsRegion(BTR); + STC->getAnalysisDeclContext()); + sReg = getGlobalsRegion(MemRegion::StaticGlobalSpaceRegionKind, + BTR); } else { // FIXME: For ObjC-methods, we need a new CodeTextRegion. For now @@ -614,18 +720,24 @@ const BlockDataRegion * MemRegionManager::getBlockDataRegion(const BlockTextRegion *BC, const LocationContext *LC) { const MemRegion *sReg = 0; - - if (LC) { - // FIXME: Once we implement scope handling, we want the parent region - // to be the scope. - const StackFrameContext *STC = LC->getCurrentStackFrame(); - assert(STC); - sReg = getStackLocalsRegion(STC); + const BlockDecl *BD = BC->getDecl(); + if (!BD->hasCaptures()) { + // This handles 'static' blocks. + sReg = getGlobalsRegion(MemRegion::GlobalImmutableSpaceRegionKind); } else { - // We allow 'LC' to be NULL for cases where want BlockDataRegions - // without context-sensitivity. - sReg = getUnknownRegion(); + if (LC) { + // FIXME: Once we implement scope handling, we want the parent region + // to be the scope. + const StackFrameContext *STC = LC->getCurrentStackFrame(); + assert(STC); + sReg = getStackLocalsRegion(STC); + } + else { + // We allow 'LC' to be NULL for cases where want BlockDataRegions + // without context-sensitivity. + sReg = getUnknownRegion(); + } } return getSubRegion<BlockDataRegion>(BC, LC, sReg); @@ -678,7 +790,7 @@ MemRegionManager::getFunctionTextRegion(const FunctionDecl *FD) { const BlockTextRegion * MemRegionManager::getBlockTextRegion(const BlockDecl *BD, CanQualType locTy, - AnalysisContext *AC) { + AnalysisDeclContext *AC) { return getSubRegion<BlockTextRegion>(BD, locTy, AC, getCodeRegion()); } @@ -928,8 +1040,8 @@ void BlockDataRegion::LazyInitializeReferencedVars() { if (ReferencedVars) return; - AnalysisContext *AC = getCodeRegion()->getAnalysisContext(); - AnalysisContext::referenced_decls_iterator I, E; + AnalysisDeclContext *AC = getCodeRegion()->getAnalysisDeclContext(); + AnalysisDeclContext::referenced_decls_iterator I, E; llvm::tie(I, E) = AC->getReferencedBlockVars(BC->getDecl()); if (I == E) { diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ObjCMessage.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ObjCMessage.cpp index 0974fe8..65cdcd9 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ObjCMessage.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ObjCMessage.cpp @@ -13,108 +13,16 @@ //===----------------------------------------------------------------------===// #include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h" +#include "clang/AST/DeclCXX.h" using namespace clang; using namespace ento; -QualType ObjCMessage::getType(ASTContext &ctx) const { - assert(isValid() && "This ObjCMessage is uninitialized!"); - if (const ObjCMessageExpr *msgE = dyn_cast<ObjCMessageExpr>(MsgOrPropE)) - return msgE->getType(); - const ObjCPropertyRefExpr *propE = cast<ObjCPropertyRefExpr>(MsgOrPropE); - if (isPropertySetter()) - return ctx.VoidTy; - return propE->getType(); -} - -Selector ObjCMessage::getSelector() const { - assert(isValid() && "This ObjCMessage is uninitialized!"); - if (const ObjCMessageExpr *msgE = dyn_cast<ObjCMessageExpr>(MsgOrPropE)) - return msgE->getSelector(); - const ObjCPropertyRefExpr *propE = cast<ObjCPropertyRefExpr>(MsgOrPropE); - if (isPropertySetter()) - return propE->getSetterSelector(); - return propE->getGetterSelector(); -} - -ObjCMethodFamily ObjCMessage::getMethodFamily() const { - assert(isValid() && "This ObjCMessage is uninitialized!"); - // Case 1. Explicit message send. - if (const ObjCMessageExpr *msgE = dyn_cast<ObjCMessageExpr>(MsgOrPropE)) - return msgE->getMethodFamily(); - - const ObjCPropertyRefExpr *propE = cast<ObjCPropertyRefExpr>(MsgOrPropE); - - // Case 2. Reference to implicit property. - if (propE->isImplicitProperty()) { - if (isPropertySetter()) - return propE->getImplicitPropertySetter()->getMethodFamily(); - else - return propE->getImplicitPropertyGetter()->getMethodFamily(); - } - - // Case 3. Reference to explicit property. - const ObjCPropertyDecl *prop = propE->getExplicitProperty(); - if (isPropertySetter()) { - if (prop->getSetterMethodDecl()) - return prop->getSetterMethodDecl()->getMethodFamily(); - return prop->getSetterName().getMethodFamily(); - } else { - if (prop->getGetterMethodDecl()) - return prop->getGetterMethodDecl()->getMethodFamily(); - return prop->getGetterName().getMethodFamily(); - } -} - -const ObjCMethodDecl *ObjCMessage::getMethodDecl() const { - assert(isValid() && "This ObjCMessage is uninitialized!"); - if (const ObjCMessageExpr *msgE = dyn_cast<ObjCMessageExpr>(MsgOrPropE)) - return msgE->getMethodDecl(); - const ObjCPropertyRefExpr *propE = cast<ObjCPropertyRefExpr>(MsgOrPropE); - if (propE->isImplicitProperty()) - return isPropertySetter() ? propE->getImplicitPropertySetter() - : propE->getImplicitPropertyGetter(); - return 0; -} - -const ObjCInterfaceDecl *ObjCMessage::getReceiverInterface() const { - assert(isValid() && "This ObjCMessage is uninitialized!"); - if (const ObjCMessageExpr *msgE = dyn_cast<ObjCMessageExpr>(MsgOrPropE)) - return msgE->getReceiverInterface(); - const ObjCPropertyRefExpr *propE = cast<ObjCPropertyRefExpr>(MsgOrPropE); - if (propE->isClassReceiver()) - return propE->getClassReceiver(); - QualType recT; - if (const Expr *recE = getInstanceReceiver()) - recT = recE->getType(); - else { - assert(propE->isSuperReceiver()); - recT = propE->getSuperReceiverType(); - } - if (const ObjCObjectPointerType *Ptr = recT->getAs<ObjCObjectPointerType>()) - return Ptr->getInterfaceDecl(); - return 0; -} - -const Expr *ObjCMessage::getArgExpr(unsigned i) const { - assert(isValid() && "This ObjCMessage is uninitialized!"); - assert(i < getNumArgs() && "Invalid index for argument"); - if (const ObjCMessageExpr *msgE = dyn_cast<ObjCMessageExpr>(MsgOrPropE)) - return msgE->getArg(i); - assert(isPropertySetter()); - if (const BinaryOperator *bop = dyn_cast<BinaryOperator>(OriginE)) - if (bop->isAssignmentOp()) - return bop->getRHS(); - return 0; -} - QualType CallOrObjCMessage::getResultType(ASTContext &ctx) const { QualType resultTy; bool isLVal = false; if (isObjCMessage()) { - isLVal = isa<ObjCMessageExpr>(Msg.getOriginExpr()) && - Msg.getOriginExpr()->isLValue(); resultTy = Msg.getResultType(ctx); } else if (const CXXConstructExpr *Ctor = CallE.dyn_cast<const CXXConstructExpr *>()) { @@ -124,7 +32,7 @@ QualType CallOrObjCMessage::getResultType(ASTContext &ctx) const { isLVal = FunctionCall->isLValue(); const Expr *Callee = FunctionCall->getCallee(); - if (const FunctionDecl *FD = State->getSVal(Callee).getAsFunctionDecl()) + if (const FunctionDecl *FD = State->getSVal(Callee, LCtx).getAsFunctionDecl()) resultTy = FD->getResultType(); else resultTy = FunctionCall->getType(); @@ -140,7 +48,7 @@ SVal CallOrObjCMessage::getFunctionCallee() const { assert(isFunctionCall()); assert(!isCXXCall()); const Expr *Fun = CallE.get<const CallExpr *>()->getCallee()->IgnoreParens(); - return State->getSVal(Fun); + return State->getSVal(Fun, LCtx); } SVal CallOrObjCMessage::getCXXCallee() const { @@ -154,7 +62,7 @@ SVal CallOrObjCMessage::getCXXCallee() const { if (!callee) return UnknownVal(); - return State->getSVal(callee); + return State->getSVal(callee, LCtx); } SVal @@ -162,3 +70,21 @@ CallOrObjCMessage::getInstanceMessageReceiver(const LocationContext *LC) const { assert(isObjCMessage()); return Msg.getInstanceReceiverSVal(State, LC); } + +const Decl *CallOrObjCMessage::getDecl() const { + if (isCXXCall()) { + const CXXMemberCallExpr *CE = + cast<CXXMemberCallExpr>(CallE.dyn_cast<const CallExpr *>()); + assert(CE); + return CE->getMethodDecl(); + } else if (isObjCMessage()) { + return Msg.getMethodDecl(); + } else if (isFunctionCall()) { + // In case of a C style call, use the path sensitive information to find + // the function declaration. + SVal CalleeVal = getFunctionCallee(); + return CalleeVal.getAsFunctionDecl(); + } + return 0; +} + diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/PathDiagnostic.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/PathDiagnostic.cpp index 3a87903..01dd965 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/PathDiagnostic.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/PathDiagnostic.cpp @@ -13,6 +13,7 @@ #include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h" +#include "clang/Basic/SourceManager.h" #include "clang/AST/Expr.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclObjC.h" @@ -24,15 +25,14 @@ using namespace clang; using namespace ento; bool PathDiagnosticMacroPiece::containsEvent() const { - for (const_iterator I = begin(), E = end(); I!=E; ++I) { + for (PathPieces::const_iterator I = subPieces.begin(), E = subPieces.end(); + I!=E; ++I) { if (isa<PathDiagnosticEventPiece>(*I)) return true; - if (PathDiagnosticMacroPiece *MP = dyn_cast<PathDiagnosticMacroPiece>(*I)) if (MP->containsEvent()) return true; } - return false; } @@ -52,41 +52,189 @@ PathDiagnosticPiece::PathDiagnosticPiece(Kind k, DisplayHint hint) PathDiagnosticPiece::~PathDiagnosticPiece() {} PathDiagnosticEventPiece::~PathDiagnosticEventPiece() {} +PathDiagnosticCallPiece::~PathDiagnosticCallPiece() {} PathDiagnosticControlFlowPiece::~PathDiagnosticControlFlowPiece() {} +PathDiagnosticMacroPiece::~PathDiagnosticMacroPiece() {} -PathDiagnosticMacroPiece::~PathDiagnosticMacroPiece() { - for (iterator I = begin(), E = end(); I != E; ++I) delete *I; -} -PathDiagnostic::PathDiagnostic() : Size(0) {} +PathPieces::~PathPieces() {} +PathDiagnostic::~PathDiagnostic() {} + +PathDiagnostic::PathDiagnostic(const Decl *declWithIssue, + StringRef bugtype, StringRef desc, + StringRef category) + : DeclWithIssue(declWithIssue), + BugType(StripTrailingDots(bugtype)), + Desc(StripTrailingDots(desc)), + Category(StripTrailingDots(category)), + path(pathImpl) {} + +void PathDiagnosticConsumer::anchor() { } -PathDiagnostic::~PathDiagnostic() { - for (iterator I = begin(), E = end(); I != E; ++I) delete &*I; +PathDiagnosticConsumer::~PathDiagnosticConsumer() { + // Delete the contents of the FoldingSet if it isn't empty already. + for (llvm::FoldingSet<PathDiagnostic>::iterator it = + Diags.begin(), et = Diags.end() ; it != et ; ++it) { + delete &*it; + } } -void PathDiagnostic::resetPath(bool deletePieces) { - Size = 0; +void PathDiagnosticConsumer::HandlePathDiagnostic(PathDiagnostic *D) { + llvm::OwningPtr<PathDiagnostic> OwningD(D); + + if (!D || D->path.empty()) + return; + + // We need to flatten the locations (convert Stmt* to locations) because + // the referenced statements may be freed by the time the diagnostics + // are emitted. + D->flattenLocations(); + + // If the PathDiagnosticConsumer does not support diagnostics that + // cross file boundaries, prune out such diagnostics now. + if (!supportsCrossFileDiagnostics()) { + // Verify that the entire path is from the same FileID. + FileID FID; + const SourceManager &SMgr = (*D->path.begin())->getLocation().getManager(); + llvm::SmallVector<const PathPieces *, 5> WorkList; + WorkList.push_back(&D->path); + + while (!WorkList.empty()) { + const PathPieces &path = *WorkList.back(); + WorkList.pop_back(); + + for (PathPieces::const_iterator I = path.begin(), E = path.end(); + I != E; ++I) { + const PathDiagnosticPiece *piece = I->getPtr(); + FullSourceLoc L = piece->getLocation().asLocation().getExpansionLoc(); + + if (FID.isInvalid()) { + FID = SMgr.getFileID(L); + } else if (SMgr.getFileID(L) != FID) + return; // FIXME: Emit a warning? + + // Check the source ranges. + for (PathDiagnosticPiece::range_iterator RI = piece->ranges_begin(), + RE = piece->ranges_end(); + RI != RE; ++RI) { + SourceLocation L = SMgr.getExpansionLoc(RI->getBegin()); + if (!L.isFileID() || SMgr.getFileID(L) != FID) + return; // FIXME: Emit a warning? + L = SMgr.getExpansionLoc(RI->getEnd()); + if (!L.isFileID() || SMgr.getFileID(L) != FID) + return; // FIXME: Emit a warning? + } + + if (const PathDiagnosticCallPiece *call = + dyn_cast<PathDiagnosticCallPiece>(piece)) { + WorkList.push_back(&call->path); + } + else if (const PathDiagnosticMacroPiece *macro = + dyn_cast<PathDiagnosticMacroPiece>(piece)) { + WorkList.push_back(¯o->subPieces); + } + } + } + + if (FID.isInvalid()) + return; // FIXME: Emit a warning? + } - if (deletePieces) - for (iterator I=begin(), E=end(); I!=E; ++I) - delete &*I; + // Profile the node to see if we already have something matching it + llvm::FoldingSetNodeID profile; + D->Profile(profile); + void *InsertPos = 0; + + if (PathDiagnostic *orig = Diags.FindNodeOrInsertPos(profile, InsertPos)) { + // Keep the PathDiagnostic with the shorter path. + const unsigned orig_size = orig->full_size(); + const unsigned new_size = D->full_size(); + + if (orig_size <= new_size) { + bool shouldKeepOriginal = true; + if (orig_size == new_size) { + // Here we break ties in a fairly arbitrary, but deterministic, way. + llvm::FoldingSetNodeID fullProfile, fullProfileOrig; + D->FullProfile(fullProfile); + orig->FullProfile(fullProfileOrig); + if (fullProfile.ComputeHash() < fullProfileOrig.ComputeHash()) + shouldKeepOriginal = false; + } - path.clear(); + if (shouldKeepOriginal) + return; + } + Diags.RemoveNode(orig); + delete orig; + } + + Diags.InsertNode(OwningD.take()); } -PathDiagnostic::PathDiagnostic(StringRef bugtype, StringRef desc, - StringRef category) - : Size(0), - BugType(StripTrailingDots(bugtype)), - Desc(StripTrailingDots(desc)), - Category(StripTrailingDots(category)) {} +namespace { +struct CompareDiagnostics { + // Compare if 'X' is "<" than 'Y'. + bool operator()(const PathDiagnostic *X, const PathDiagnostic *Y) const { + // First compare by location + const FullSourceLoc &XLoc = X->getLocation().asLocation(); + const FullSourceLoc &YLoc = Y->getLocation().asLocation(); + if (XLoc < YLoc) + return true; + if (XLoc != YLoc) + return false; + + // Next, compare by bug type. + StringRef XBugType = X->getBugType(); + StringRef YBugType = Y->getBugType(); + if (XBugType < YBugType) + return true; + if (XBugType != YBugType) + return false; + + // Next, compare by bug description. + StringRef XDesc = X->getDescription(); + StringRef YDesc = Y->getDescription(); + if (XDesc < YDesc) + return true; + if (XDesc != YDesc) + return false; + + // FIXME: Further refine by comparing PathDiagnosticPieces? + return false; + } +}; +} -void PathDiagnosticConsumer::HandlePathDiagnostic(const PathDiagnostic *D) { - // For now this simply forwards to HandlePathDiagnosticImpl. In the future - // we can use this indirection to control for multi-threaded access to - // the PathDiagnosticConsumer from multiple bug reporters. - HandlePathDiagnosticImpl(D); +void +PathDiagnosticConsumer::FlushDiagnostics(SmallVectorImpl<std::string> *Files) { + if (flushed) + return; + + flushed = true; + + std::vector<const PathDiagnostic *> BatchDiags; + for (llvm::FoldingSet<PathDiagnostic>::iterator it = Diags.begin(), + et = Diags.end(); it != et; ++it) { + BatchDiags.push_back(&*it); + } + + // Clear out the FoldingSet. + Diags.clear(); + + // Sort the diagnostics so that they are always emitted in a deterministic + // order. + if (!BatchDiags.empty()) + std::sort(BatchDiags.begin(), BatchDiags.end(), CompareDiagnostics()); + + FlushDiagnosticsImpl(BatchDiags, Files); + + // Delete the flushed diagnostics. + for (std::vector<const PathDiagnostic *>::iterator it = BatchDiags.begin(), + et = BatchDiags.end(); it != et; ++it) { + const PathDiagnostic *D = *it; + delete D; + } } //===----------------------------------------------------------------------===// @@ -94,9 +242,9 @@ void PathDiagnosticConsumer::HandlePathDiagnostic(const PathDiagnostic *D) { //===----------------------------------------------------------------------===// static SourceLocation getValidSourceLocation(const Stmt* S, - LocationOrAnalysisContext LAC) { + LocationOrAnalysisDeclContext LAC) { SourceLocation L = S->getLocStart(); - assert(!LAC.isNull() && "A valid LocationContext or AnalysisContext should " + assert(!LAC.isNull() && "A valid LocationContext or AnalysisDeclContext should " "be passed to PathDiagnosticLocation upon creation."); // S might be a temporary statement that does not have a location in the @@ -107,7 +255,7 @@ static SourceLocation getValidSourceLocation(const Stmt* S, if (LAC.is<const LocationContext*>()) PM = &LAC.get<const LocationContext*>()->getParentMap(); else - PM = &LAC.get<AnalysisContext*>()->getParentMap(); + PM = &LAC.get<AnalysisDeclContext*>()->getParentMap(); while (!L.isValid()) { S = PM->getParent(S); @@ -127,7 +275,7 @@ PathDiagnosticLocation PathDiagnosticLocation PathDiagnosticLocation::createBegin(const Stmt *S, const SourceManager &SM, - LocationOrAnalysisContext LAC) { + LocationOrAnalysisDeclContext LAC) { return PathDiagnosticLocation(getValidSourceLocation(S, LAC), SM, SingleLocK); } @@ -193,9 +341,6 @@ PathDiagnosticLocation } return PathDiagnosticLocation(S, SMng, P.getLocationContext()); - - if (!S) - return PathDiagnosticLocation(); } PathDiagnosticLocation @@ -212,8 +357,9 @@ PathDiagnosticLocation return PathDiagnosticLocation(PS->getStmt(), SM, LC); else if (const BlockEdge *BE = dyn_cast<BlockEdge>(&P)) { const Stmt *Term = BE->getSrc()->getTerminator(); - assert(Term); - return PathDiagnosticLocation(Term, SM, LC); + if (Term) { + return PathDiagnosticLocation(Term, SM, LC); + } } NI = NI->succ_empty() ? 0 : *(NI->succ_begin()); } @@ -229,7 +375,7 @@ PathDiagnosticLocation PathDiagnosticLocation::createSingleLocation( FullSourceLoc PathDiagnosticLocation::genLocation(SourceLocation L, - LocationOrAnalysisContext LAC) const { + LocationOrAnalysisDeclContext LAC) const { assert(isValid()); // Note that we want a 'switch' here so that the compiler can warn us in // case we add more cases. @@ -238,9 +384,15 @@ FullSourceLoc case RangeK: break; case StmtK: + // Defensive checking. + if (!S) + break; return FullSourceLoc(getValidSourceLocation(S, LAC), const_cast<SourceManager&>(*SM)); case DeclK: + // Defensive checking. + if (!D) + break; return FullSourceLoc(D->getLocation(), const_cast<SourceManager&>(*SM)); } @@ -248,7 +400,7 @@ FullSourceLoc } PathDiagnosticRange - PathDiagnosticLocation::genRange(LocationOrAnalysisContext LAC) const { + PathDiagnosticLocation::genRange(LocationOrAnalysisDeclContext LAC) const { assert(isValid()); // Note that we want a 'switch' here so that the compiler can warn us in // case we add more cases. @@ -321,6 +473,132 @@ void PathDiagnosticLocation::flatten() { } } +PathDiagnosticLocation PathDiagnostic::getLocation() const { + assert(path.size() > 0 && + "getLocation() requires a non-empty PathDiagnostic."); + + PathDiagnosticPiece *p = path.rbegin()->getPtr(); + + while (true) { + if (PathDiagnosticCallPiece *cp = dyn_cast<PathDiagnosticCallPiece>(p)) { + assert(!cp->path.empty()); + p = cp->path.rbegin()->getPtr(); + continue; + } + break; + } + + return p->getLocation(); +} + +//===----------------------------------------------------------------------===// +// Manipulation of PathDiagnosticCallPieces. +//===----------------------------------------------------------------------===// + +static PathDiagnosticLocation getLastStmtLoc(const ExplodedNode *N, + const SourceManager &SM) { + while (N) { + ProgramPoint PP = N->getLocation(); + if (const StmtPoint *SP = dyn_cast<StmtPoint>(&PP)) + return PathDiagnosticLocation(SP->getStmt(), SM, PP.getLocationContext()); + if (N->pred_empty()) + break; + N = *N->pred_begin(); + } + return PathDiagnosticLocation(); +} + +PathDiagnosticCallPiece * +PathDiagnosticCallPiece::construct(const ExplodedNode *N, + const CallExit &CE, + const SourceManager &SM) { + const Decl *caller = CE.getLocationContext()->getParent()->getDecl(); + PathDiagnosticLocation pos = getLastStmtLoc(N, SM); + return new PathDiagnosticCallPiece(caller, pos); +} + +PathDiagnosticCallPiece * +PathDiagnosticCallPiece::construct(PathPieces &path, + const Decl *caller) { + PathDiagnosticCallPiece *C = new PathDiagnosticCallPiece(path, caller); + path.clear(); + path.push_front(C); + return C; +} + +void PathDiagnosticCallPiece::setCallee(const CallEnter &CE, + const SourceManager &SM) { + const Decl *D = CE.getCalleeContext()->getDecl(); + Callee = D; + callEnter = PathDiagnosticLocation(CE.getCallExpr(), SM, + CE.getLocationContext()); + callEnterWithin = PathDiagnosticLocation::createBegin(D, SM); +} + +IntrusiveRefCntPtr<PathDiagnosticEventPiece> +PathDiagnosticCallPiece::getCallEnterEvent() const { + if (!Callee) + return 0; + SmallString<256> buf; + llvm::raw_svector_ostream Out(buf); + if (isa<BlockDecl>(Callee)) + Out << "Calling anonymous block"; + else if (const NamedDecl *ND = dyn_cast<NamedDecl>(Callee)) + Out << "Calling '" << *ND << "'"; + StringRef msg = Out.str(); + if (msg.empty()) + return 0; + return new PathDiagnosticEventPiece(callEnter, msg); +} + +IntrusiveRefCntPtr<PathDiagnosticEventPiece> +PathDiagnosticCallPiece::getCallEnterWithinCallerEvent() const { + SmallString<256> buf; + llvm::raw_svector_ostream Out(buf); + if (const NamedDecl *ND = dyn_cast_or_null<NamedDecl>(Caller)) + Out << "Entered call from '" << *ND << "'"; + else + Out << "Entered call"; + StringRef msg = Out.str(); + if (msg.empty()) + return 0; + return new PathDiagnosticEventPiece(callEnterWithin, msg); +} + +IntrusiveRefCntPtr<PathDiagnosticEventPiece> +PathDiagnosticCallPiece::getCallExitEvent() const { + if (NoExit) + return 0; + SmallString<256> buf; + llvm::raw_svector_ostream Out(buf); + if (!CallStackMessage.empty()) + Out << CallStackMessage; + else if (const NamedDecl *ND = dyn_cast_or_null<NamedDecl>(Callee)) + Out << "Returning from '" << *ND << "'"; + else + Out << "Returning to caller"; + return new PathDiagnosticEventPiece(callReturn, Out.str()); +} + +static void compute_path_size(const PathPieces &pieces, unsigned &size) { + for (PathPieces::const_iterator it = pieces.begin(), + et = pieces.end(); it != et; ++it) { + const PathDiagnosticPiece *piece = it->getPtr(); + if (const PathDiagnosticCallPiece *cp = + dyn_cast<PathDiagnosticCallPiece>(piece)) { + compute_path_size(cp->path, size); + } + else + ++size; + } +} + +unsigned PathDiagnostic::full_size() { + unsigned size = 0; + compute_path_size(path, size); + return size; +} + //===----------------------------------------------------------------------===// // FoldingSet profiling methods. //===----------------------------------------------------------------------===// @@ -343,6 +621,14 @@ void PathDiagnosticPiece::Profile(llvm::FoldingSetNodeID &ID) const { } } +void PathDiagnosticCallPiece::Profile(llvm::FoldingSetNodeID &ID) const { + PathDiagnosticPiece::Profile(ID); + for (PathPieces::const_iterator it = path.begin(), + et = path.end(); it != et; ++it) { + ID.Add(**it); + } +} + void PathDiagnosticSpotPiece::Profile(llvm::FoldingSetNodeID &ID) const { PathDiagnosticPiece::Profile(ID); ID.Add(Pos); @@ -356,18 +642,114 @@ void PathDiagnosticControlFlowPiece::Profile(llvm::FoldingSetNodeID &ID) const { void PathDiagnosticMacroPiece::Profile(llvm::FoldingSetNodeID &ID) const { PathDiagnosticSpotPiece::Profile(ID); - for (const_iterator I = begin(), E = end(); I != E; ++I) + for (PathPieces::const_iterator I = subPieces.begin(), E = subPieces.end(); + I != E; ++I) ID.Add(**I); } void PathDiagnostic::Profile(llvm::FoldingSetNodeID &ID) const { - ID.AddInteger(Size); + if (!path.empty()) + getLocation().Profile(ID); ID.AddString(BugType); ID.AddString(Desc); ID.AddString(Category); - for (const_iterator I = begin(), E = end(); I != E; ++I) - ID.Add(*I); - +} + +void PathDiagnostic::FullProfile(llvm::FoldingSetNodeID &ID) const { + Profile(ID); + for (PathPieces::const_iterator I = path.begin(), E = path.end(); I != E; ++I) + ID.Add(**I); for (meta_iterator I = meta_begin(), E = meta_end(); I != E; ++I) ID.AddString(*I); } + +StackHintGenerator::~StackHintGenerator() {} + +std::string StackHintGeneratorForSymbol::getMessage(const ExplodedNode *N){ + ProgramPoint P = N->getLocation(); + const CallExit *CExit = dyn_cast<CallExit>(&P); + assert(CExit && "Stack Hints should be constructed at CallExit points."); + + const CallExpr *CE = dyn_cast_or_null<CallExpr>(CExit->getStmt()); + if (!CE) + return ""; + + // Get the successor node to make sure the return statement is evaluated and + // CE is set to the result value. + N = *N->succ_begin(); + if (!N) + return getMessageForSymbolNotFound(); + + // Check if one of the parameters are set to the interesting symbol. + ProgramStateRef State = N->getState(); + const LocationContext *LCtx = N->getLocationContext(); + unsigned ArgIndex = 0; + for (CallExpr::const_arg_iterator I = CE->arg_begin(), + E = CE->arg_end(); I != E; ++I, ++ArgIndex){ + SVal SV = State->getSVal(*I, LCtx); + + // Check if the variable corresponding to the symbol is passed by value. + SymbolRef AS = SV.getAsLocSymbol(); + if (AS == Sym) { + return getMessageForArg(*I, ArgIndex); + } + + // Check if the parameter is a pointer to the symbol. + if (const loc::MemRegionVal *Reg = dyn_cast<loc::MemRegionVal>(&SV)) { + SVal PSV = State->getSVal(Reg->getRegion()); + SymbolRef AS = PSV.getAsLocSymbol(); + if (AS == Sym) { + return getMessageForArg(*I, ArgIndex); + } + } + } + + // Check if we are returning the interesting symbol. + SVal SV = State->getSVal(CE, LCtx); + SymbolRef RetSym = SV.getAsLocSymbol(); + if (RetSym == Sym) { + return getMessageForReturn(CE); + } + + return getMessageForSymbolNotFound(); +} + +/// TODO: This is copied from clang diagnostics. Maybe we could just move it to +/// some common place. (Same as HandleOrdinalModifier.) +void StackHintGeneratorForSymbol::printOrdinal(unsigned ValNo, + llvm::raw_svector_ostream &Out) { + assert(ValNo != 0 && "ValNo must be strictly positive!"); + + // We could use text forms for the first N ordinals, but the numeric + // forms are actually nicer in diagnostics because they stand out. + Out << ValNo; + + // It is critically important that we do this perfectly for + // user-written sequences with over 100 elements. + switch (ValNo % 100) { + case 11: + case 12: + case 13: + Out << "th"; return; + default: + switch (ValNo % 10) { + case 1: Out << "st"; return; + case 2: Out << "nd"; return; + case 3: Out << "rd"; return; + default: Out << "th"; return; + } + } +} + +std::string StackHintGeneratorForSymbol::getMessageForArg(const Expr *ArgE, + unsigned ArgIndex) { + SmallString<200> buf; + llvm::raw_svector_ostream os(buf); + + os << Msg << " via "; + // Printed parameters start at 1, not 0. + printOrdinal(++ArgIndex, os); + os << " parameter"; + + return os.str(); +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp index 5ae95c6..323cede 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp @@ -25,56 +25,23 @@ using namespace ento; typedef llvm::DenseMap<FileID, unsigned> FIDMap; -namespace { -struct CompareDiagnostics { - // Compare if 'X' is "<" than 'Y'. - bool operator()(const PathDiagnostic *X, const PathDiagnostic *Y) const { - // First compare by location - const FullSourceLoc &XLoc = X->getLocation().asLocation(); - const FullSourceLoc &YLoc = Y->getLocation().asLocation(); - if (XLoc < YLoc) - return true; - if (XLoc != YLoc) - return false; - - // Next, compare by bug type. - StringRef XBugType = X->getBugType(); - StringRef YBugType = Y->getBugType(); - if (XBugType < YBugType) - return true; - if (XBugType != YBugType) - return false; - - // Next, compare by bug description. - StringRef XDesc = X->getDescription(); - StringRef YDesc = Y->getDescription(); - if (XDesc < YDesc) - return true; - if (XDesc != YDesc) - return false; - - // FIXME: Further refine by comparing PathDiagnosticPieces? - return false; - } -}; -} namespace { class PlistDiagnostics : public PathDiagnosticConsumer { - std::vector<const PathDiagnostic*> BatchedDiags; const std::string OutputFile; const LangOptions &LangOpts; - llvm::OwningPtr<PathDiagnosticConsumer> SubPD; + OwningPtr<PathDiagnosticConsumer> SubPD; bool flushed; + const bool SupportsCrossFileDiagnostics; public: PlistDiagnostics(const std::string& prefix, const LangOptions &LangOpts, + bool supportsMultipleFiles, PathDiagnosticConsumer *subPD); - ~PlistDiagnostics() { FlushDiagnostics(NULL); } + virtual ~PlistDiagnostics() {} - void FlushDiagnostics(SmallVectorImpl<std::string> *FilesMade); - - void HandlePathDiagnosticImpl(const PathDiagnostic* D); + void FlushDiagnosticsImpl(std::vector<const PathDiagnostic *> &Diags, + SmallVectorImpl<std::string> *FilesMade); virtual StringRef getName() const { return "PlistDiagnostics"; @@ -84,18 +51,29 @@ namespace { bool supportsLogicalOpControlFlow() const { return true; } bool supportsAllBlockEdges() const { return true; } virtual bool useVerboseDescription() const { return false; } + virtual bool supportsCrossFileDiagnostics() const { + return SupportsCrossFileDiagnostics; + } }; } // end anonymous namespace PlistDiagnostics::PlistDiagnostics(const std::string& output, const LangOptions &LO, + bool supportsMultipleFiles, PathDiagnosticConsumer *subPD) - : OutputFile(output), LangOpts(LO), SubPD(subPD), flushed(false) {} + : OutputFile(output), LangOpts(LO), SubPD(subPD), flushed(false), + SupportsCrossFileDiagnostics(supportsMultipleFiles) {} PathDiagnosticConsumer* ento::createPlistDiagnosticConsumer(const std::string& s, const Preprocessor &PP, PathDiagnosticConsumer *subPD) { - return new PlistDiagnostics(s, PP.getLangOptions(), subPD); + return new PlistDiagnostics(s, PP.getLangOpts(), false, subPD); +} + +PathDiagnosticConsumer* +ento::createPlistMultiFileDiagnosticConsumer(const std::string &s, + const Preprocessor &PP) { + return new PlistDiagnostics(s, PP.getLangOpts(), true, 0); } PathDiagnosticConsumer::PathGenerationScheme @@ -167,10 +145,9 @@ static void EmitRange(raw_ostream &o, const SourceManager &SM, Indent(o, indent) << "</array>\n"; } -static raw_ostream &EmitString(raw_ostream &o, - const std::string& s) { +static raw_ostream &EmitString(raw_ostream &o, StringRef s) { o << "<string>"; - for (std::string::const_iterator I=s.begin(), E=s.end(); I!=E; ++I) { + for (StringRef::const_iterator I = s.begin(), E = s.end(); I != E; ++I) { char c = *I; switch (c) { default: o << c; break; @@ -232,7 +209,8 @@ static void ReportEvent(raw_ostream &o, const PathDiagnosticPiece& P, const FIDMap& FM, const SourceManager &SM, const LangOptions &LangOpts, - unsigned indent) { + unsigned indent, + unsigned depth) { Indent(o, indent) << "<dict>\n"; ++indent; @@ -258,6 +236,10 @@ static void ReportEvent(raw_ostream &o, const PathDiagnosticPiece& P, --indent; Indent(o, indent) << "</array>\n"; } + + // Output the call depth. + Indent(o, indent) << "<key>depth</key>" + << "<integer>" << depth << "</integer>\n"; // Output the text. assert(!P.getString().empty()); @@ -269,108 +251,143 @@ static void ReportEvent(raw_ostream &o, const PathDiagnosticPiece& P, // FIXME: Really use a short string. Indent(o, indent) << "<key>message</key>\n"; EmitString(o, P.getString()) << '\n'; - + // Finish up. --indent; Indent(o, indent); o << "</dict>\n"; } +static void ReportPiece(raw_ostream &o, + const PathDiagnosticPiece &P, + const FIDMap& FM, const SourceManager &SM, + const LangOptions &LangOpts, + unsigned indent, + unsigned depth, + bool includeControlFlow); + +static void ReportCall(raw_ostream &o, + const PathDiagnosticCallPiece &P, + const FIDMap& FM, const SourceManager &SM, + const LangOptions &LangOpts, + unsigned indent, + unsigned depth) { + + IntrusiveRefCntPtr<PathDiagnosticEventPiece> callEnter = + P.getCallEnterEvent(); + + if (callEnter) + ReportPiece(o, *callEnter, FM, SM, LangOpts, indent, depth, true); + + IntrusiveRefCntPtr<PathDiagnosticEventPiece> callEnterWithinCaller = + P.getCallEnterWithinCallerEvent(); + + ++depth; + + if (callEnterWithinCaller) + ReportPiece(o, *callEnterWithinCaller, FM, SM, LangOpts, + indent, depth, true); + + for (PathPieces::const_iterator I = P.path.begin(), E = P.path.end();I!=E;++I) + ReportPiece(o, **I, FM, SM, LangOpts, indent, depth, true); + + IntrusiveRefCntPtr<PathDiagnosticEventPiece> callExit = + P.getCallExitEvent(); + + if (callExit) + ReportPiece(o, *callExit, FM, SM, LangOpts, indent, depth, true); +} + static void ReportMacro(raw_ostream &o, const PathDiagnosticMacroPiece& P, const FIDMap& FM, const SourceManager &SM, const LangOptions &LangOpts, - unsigned indent) { + unsigned indent, + unsigned depth) { - for (PathDiagnosticMacroPiece::const_iterator I=P.begin(), E=P.end(); + for (PathPieces::const_iterator I = P.subPieces.begin(), E=P.subPieces.end(); I!=E; ++I) { - - switch ((*I)->getKind()) { - default: - break; - case PathDiagnosticPiece::Event: - ReportEvent(o, cast<PathDiagnosticEventPiece>(**I), FM, SM, LangOpts, - indent); - break; - case PathDiagnosticPiece::Macro: - ReportMacro(o, cast<PathDiagnosticMacroPiece>(**I), FM, SM, LangOpts, - indent); - break; - } + ReportPiece(o, **I, FM, SM, LangOpts, indent, depth, false); } } static void ReportDiag(raw_ostream &o, const PathDiagnosticPiece& P, const FIDMap& FM, const SourceManager &SM, const LangOptions &LangOpts) { - - unsigned indent = 4; - - switch (P.getKind()) { - case PathDiagnosticPiece::ControlFlow: - ReportControlFlow(o, cast<PathDiagnosticControlFlowPiece>(P), FM, SM, - LangOpts, indent); - break; - case PathDiagnosticPiece::Event: - ReportEvent(o, cast<PathDiagnosticEventPiece>(P), FM, SM, LangOpts, - indent); - break; - case PathDiagnosticPiece::Macro: - ReportMacro(o, cast<PathDiagnosticMacroPiece>(P), FM, SM, LangOpts, - indent); - break; - } + ReportPiece(o, P, FM, SM, LangOpts, 4, 0, true); } -void PlistDiagnostics::HandlePathDiagnosticImpl(const PathDiagnostic* D) { - if (!D) - return; - - if (D->empty()) { - delete D; - return; +static void ReportPiece(raw_ostream &o, + const PathDiagnosticPiece &P, + const FIDMap& FM, const SourceManager &SM, + const LangOptions &LangOpts, + unsigned indent, + unsigned depth, + bool includeControlFlow) { + switch (P.getKind()) { + case PathDiagnosticPiece::ControlFlow: + if (includeControlFlow) + ReportControlFlow(o, cast<PathDiagnosticControlFlowPiece>(P), FM, SM, + LangOpts, indent); + break; + case PathDiagnosticPiece::Call: + ReportCall(o, cast<PathDiagnosticCallPiece>(P), FM, SM, LangOpts, + indent, depth); + break; + case PathDiagnosticPiece::Event: + ReportEvent(o, cast<PathDiagnosticSpotPiece>(P), FM, SM, LangOpts, + indent, depth); + break; + case PathDiagnosticPiece::Macro: + ReportMacro(o, cast<PathDiagnosticMacroPiece>(P), FM, SM, LangOpts, + indent, depth); + break; } - - // We need to flatten the locations (convert Stmt* to locations) because - // the referenced statements may be freed by the time the diagnostics - // are emitted. - const_cast<PathDiagnostic*>(D)->flattenLocations(); - BatchedDiags.push_back(D); } -void PlistDiagnostics::FlushDiagnostics(SmallVectorImpl<std::string> - *FilesMade) { - - if (flushed) - return; - - flushed = true; - - // Sort the diagnostics so that they are always emitted in a deterministic - // order. - if (!BatchedDiags.empty()) - std::sort(BatchedDiags.begin(), BatchedDiags.end(), CompareDiagnostics()); - +void PlistDiagnostics::FlushDiagnosticsImpl( + std::vector<const PathDiagnostic *> &Diags, + SmallVectorImpl<std::string> *FilesMade) { // Build up a set of FIDs that we use by scanning the locations and // ranges of the diagnostics. FIDMap FM; SmallVector<FileID, 10> Fids; const SourceManager* SM = 0; - if (!BatchedDiags.empty()) - SM = &(*BatchedDiags.begin())->begin()->getLocation().getManager(); + if (!Diags.empty()) + SM = &(*(*Diags.begin())->path.begin())->getLocation().getManager(); - for (std::vector<const PathDiagnostic*>::iterator DI = BatchedDiags.begin(), - DE = BatchedDiags.end(); DI != DE; ++DI) { + + for (std::vector<const PathDiagnostic*>::iterator DI = Diags.begin(), + DE = Diags.end(); DI != DE; ++DI) { const PathDiagnostic *D = *DI; - for (PathDiagnostic::const_iterator I=D->begin(), E=D->end(); I!=E; ++I) { - AddFID(FM, Fids, SM, I->getLocation().asLocation()); + llvm::SmallVector<const PathPieces *, 5> WorkList; + WorkList.push_back(&D->path); - for (PathDiagnosticPiece::range_iterator RI=I->ranges_begin(), - RE=I->ranges_end(); RI!=RE; ++RI) { - AddFID(FM, Fids, SM, RI->getBegin()); - AddFID(FM, Fids, SM, RI->getEnd()); + while (!WorkList.empty()) { + const PathPieces &path = *WorkList.back(); + WorkList.pop_back(); + + for (PathPieces::const_iterator I = path.begin(), E = path.end(); + I!=E; ++I) { + const PathDiagnosticPiece *piece = I->getPtr(); + AddFID(FM, Fids, SM, piece->getLocation().asLocation()); + + for (PathDiagnosticPiece::range_iterator RI = piece->ranges_begin(), + RE= piece->ranges_end(); RI != RE; ++RI) { + AddFID(FM, Fids, SM, RI->getBegin()); + AddFID(FM, Fids, SM, RI->getEnd()); + } + + if (const PathDiagnosticCallPiece *call = + dyn_cast<PathDiagnosticCallPiece>(piece)) { + WorkList.push_back(&call->path); + } + else if (const PathDiagnosticMacroPiece *macro = + dyn_cast<PathDiagnosticMacroPiece>(piece)) { + WorkList.push_back(¯o->subPieces); + } } } } @@ -379,7 +396,7 @@ void PlistDiagnostics::FlushDiagnostics(SmallVectorImpl<std::string> std::string ErrMsg; llvm::raw_fd_ostream o(OutputFile.c_str(), ErrMsg); if (!ErrMsg.empty()) { - llvm::errs() << "warning: could not creat file: " << OutputFile << '\n'; + llvm::errs() << "warning: could not create file: " << OutputFile << '\n'; return; } @@ -406,21 +423,19 @@ void PlistDiagnostics::FlushDiagnostics(SmallVectorImpl<std::string> " <key>diagnostics</key>\n" " <array>\n"; - for (std::vector<const PathDiagnostic*>::iterator DI=BatchedDiags.begin(), - DE = BatchedDiags.end(); DI!=DE; ++DI) { + for (std::vector<const PathDiagnostic*>::iterator DI=Diags.begin(), + DE = Diags.end(); DI!=DE; ++DI) { o << " <dict>\n" " <key>path</key>\n"; const PathDiagnostic *D = *DI; - // Create an owning smart pointer for 'D' just so that we auto-free it - // when we exit this method. - llvm::OwningPtr<PathDiagnostic> OwnedD(const_cast<PathDiagnostic*>(D)); o << " <array>\n"; - for (PathDiagnostic::const_iterator I=D->begin(), E=D->end(); I != E; ++I) - ReportDiag(o, *I, FM, *SM, LangOpts); + for (PathPieces::const_iterator I = D->path.begin(), E = D->path.end(); + I != E; ++I) + ReportDiag(o, **I, FM, *SM, LangOpts); o << " </array>\n"; @@ -431,6 +446,38 @@ void PlistDiagnostics::FlushDiagnostics(SmallVectorImpl<std::string> EmitString(o, D->getCategory()) << '\n'; o << " <key>type</key>"; EmitString(o, D->getBugType()) << '\n'; + + // Output information about the semantic context where + // the issue occurred. + if (const Decl *DeclWithIssue = D->getDeclWithIssue()) { + // FIXME: handle blocks, which have no name. + if (const NamedDecl *ND = dyn_cast<NamedDecl>(DeclWithIssue)) { + StringRef declKind; + switch (ND->getKind()) { + case Decl::CXXRecord: + declKind = "C++ class"; + break; + case Decl::CXXMethod: + declKind = "C++ method"; + break; + case Decl::ObjCMethod: + declKind = "Objective-C method"; + break; + case Decl::Function: + declKind = "function"; + break; + default: + break; + } + if (!declKind.empty()) { + const std::string &declName = ND->getDeclName().getAsString(); + o << " <key>issue_context_kind</key>"; + EmitString(o, declKind) << '\n'; + o << " <key>issue_context</key>"; + EmitString(o, declName) << '\n'; + } + } + } // Output the location of the bug. o << " <key>location</key>\n"; @@ -438,9 +485,10 @@ void PlistDiagnostics::FlushDiagnostics(SmallVectorImpl<std::string> // Output the diagnostic to the sub-diagnostic client, if any. if (SubPD) { - SubPD->HandlePathDiagnostic(OwnedD.take()); + std::vector<const PathDiagnostic *> SubDiags; + SubDiags.push_back(D); SmallVector<std::string, 1> SubFilesMade; - SubPD->FlushDiagnostics(SubFilesMade); + SubPD->FlushDiagnosticsImpl(SubDiags, &SubFilesMade); if (!SubFilesMade.empty()) { o << " <key>" << SubPD->getName() << "_files</key>\n"; @@ -462,6 +510,4 @@ void PlistDiagnostics::FlushDiagnostics(SmallVectorImpl<std::string> if (FilesMade) FilesMade->push_back(OutputFile); - - BatchedDiags.clear(); } diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ProgramState.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ProgramState.cpp index 73788cc..b9cfa27 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ProgramState.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ProgramState.cpp @@ -15,6 +15,7 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/TaintManager.h" #include "llvm/Support/raw_ostream.h" using namespace clang; @@ -24,6 +25,26 @@ using namespace ento; // FIXME: Move this elsewhere. ConstraintManager::~ConstraintManager() {} +namespace clang { namespace ento { +/// Increments the number of times this state is referenced. + +void ProgramStateRetain(const ProgramState *state) { + ++const_cast<ProgramState*>(state)->refCount; +} + +/// Decrement the number of times this state is referenced. +void ProgramStateRelease(const ProgramState *state) { + assert(state->refCount > 0); + ProgramState *s = const_cast<ProgramState*>(state); + if (--s->refCount == 0) { + ProgramStateManager &Mgr = s->getStateManager(); + Mgr.StateSet.RemoveNode(s); + s->~ProgramState(); + Mgr.freeStates.push_back(s); + } +} +}} + ProgramState::ProgramState(ProgramStateManager *mgr, const Environment& env, StoreRef st, GenericDataMap gdm) : stateMgr(mgr), @@ -55,8 +76,8 @@ ProgramStateManager::~ProgramStateManager() { I->second.second(I->second.first); } -const ProgramState* -ProgramStateManager::removeDeadBindings(const ProgramState *state, +ProgramStateRef +ProgramStateManager::removeDeadBindings(ProgramStateRef state, const StackFrameContext *LCtx, SymbolReaper& SymReaper) { @@ -79,7 +100,7 @@ ProgramStateManager::removeDeadBindings(const ProgramState *state, return getPersistentState(NewState); } -const ProgramState *ProgramStateManager::MarshalState(const ProgramState *state, +ProgramStateRef ProgramStateManager::MarshalState(ProgramStateRef state, const StackFrameContext *InitLoc) { // make up an empty state for now. ProgramState State(this, @@ -90,7 +111,7 @@ const ProgramState *ProgramStateManager::MarshalState(const ProgramState *state, return getPersistentState(State); } -const ProgramState *ProgramState::bindCompoundLiteral(const CompoundLiteralExpr *CL, +ProgramStateRef ProgramState::bindCompoundLiteral(const CompoundLiteralExpr *CL, const LocationContext *LC, SVal V) const { const StoreRef &newStore = @@ -98,21 +119,21 @@ const ProgramState *ProgramState::bindCompoundLiteral(const CompoundLiteralExpr return makeWithStore(newStore); } -const ProgramState *ProgramState::bindDecl(const VarRegion* VR, SVal IVal) const { +ProgramStateRef ProgramState::bindDecl(const VarRegion* VR, SVal IVal) const { const StoreRef &newStore = getStateManager().StoreMgr->BindDecl(getStore(), VR, IVal); return makeWithStore(newStore); } -const ProgramState *ProgramState::bindDeclWithNoInit(const VarRegion* VR) const { +ProgramStateRef ProgramState::bindDeclWithNoInit(const VarRegion* VR) const { const StoreRef &newStore = getStateManager().StoreMgr->BindDeclWithNoInit(getStore(), VR); return makeWithStore(newStore); } -const ProgramState *ProgramState::bindLoc(Loc LV, SVal V) const { +ProgramStateRef ProgramState::bindLoc(Loc LV, SVal V) const { ProgramStateManager &Mgr = getStateManager(); - const ProgramState *newState = makeWithStore(Mgr.StoreMgr->Bind(getStore(), + ProgramStateRef newState = makeWithStore(Mgr.StoreMgr->Bind(getStore(), LV, V)); const MemRegion *MR = LV.getAsRegion(); if (MR && Mgr.getOwningEngine()) @@ -121,53 +142,55 @@ const ProgramState *ProgramState::bindLoc(Loc LV, SVal V) const { return newState; } -const ProgramState *ProgramState::bindDefault(SVal loc, SVal V) const { +ProgramStateRef ProgramState::bindDefault(SVal loc, SVal V) const { ProgramStateManager &Mgr = getStateManager(); const MemRegion *R = cast<loc::MemRegionVal>(loc).getRegion(); const StoreRef &newStore = Mgr.StoreMgr->BindDefault(getStore(), R, V); - const ProgramState *new_state = makeWithStore(newStore); + ProgramStateRef new_state = makeWithStore(newStore); return Mgr.getOwningEngine() ? Mgr.getOwningEngine()->processRegionChange(new_state, R) : new_state; } -const ProgramState * +ProgramStateRef ProgramState::invalidateRegions(ArrayRef<const MemRegion *> Regions, const Expr *E, unsigned Count, + const LocationContext *LCtx, StoreManager::InvalidatedSymbols *IS, - bool invalidateGlobals) const { + const CallOrObjCMessage *Call) const { if (!IS) { StoreManager::InvalidatedSymbols invalidated; - return invalidateRegionsImpl(Regions, E, Count, - invalidated, invalidateGlobals); + return invalidateRegionsImpl(Regions, E, Count, LCtx, + invalidated, Call); } - return invalidateRegionsImpl(Regions, E, Count, *IS, invalidateGlobals); + return invalidateRegionsImpl(Regions, E, Count, LCtx, *IS, Call); } -const ProgramState * +ProgramStateRef ProgramState::invalidateRegionsImpl(ArrayRef<const MemRegion *> Regions, const Expr *E, unsigned Count, + const LocationContext *LCtx, StoreManager::InvalidatedSymbols &IS, - bool invalidateGlobals) const { + const CallOrObjCMessage *Call) const { ProgramStateManager &Mgr = getStateManager(); SubEngine* Eng = Mgr.getOwningEngine(); if (Eng && Eng->wantsRegionChangeUpdate(this)) { StoreManager::InvalidatedRegions Invalidated; const StoreRef &newStore - = Mgr.StoreMgr->invalidateRegions(getStore(), Regions, E, Count, IS, - invalidateGlobals, &Invalidated); - const ProgramState *newState = makeWithStore(newStore); - return Eng->processRegionChanges(newState, &IS, Regions, Invalidated); + = Mgr.StoreMgr->invalidateRegions(getStore(), Regions, E, Count, LCtx, IS, + Call, &Invalidated); + ProgramStateRef newState = makeWithStore(newStore); + return Eng->processRegionChanges(newState, &IS, Regions, Invalidated, Call); } const StoreRef &newStore = - Mgr.StoreMgr->invalidateRegions(getStore(), Regions, E, Count, IS, - invalidateGlobals, NULL); + Mgr.StoreMgr->invalidateRegions(getStore(), Regions, E, Count, LCtx, IS, + Call, NULL); return makeWithStore(newStore); } -const ProgramState *ProgramState::unbindLoc(Loc LV) const { +ProgramStateRef ProgramState::unbindLoc(Loc LV) const { assert(!isa<loc::MemRegionVal>(LV) && "Use invalidateRegion instead."); Store OldStore = getStore(); @@ -179,9 +202,11 @@ const ProgramState *ProgramState::unbindLoc(Loc LV) const { return makeWithStore(newStore); } -const ProgramState *ProgramState::enterStackFrame(const StackFrameContext *frame) const { +ProgramStateRef +ProgramState::enterStackFrame(const LocationContext *callerCtx, + const StackFrameContext *calleeCtx) const { const StoreRef &new_store = - getStateManager().StoreMgr->enterStackFrame(this, frame); + getStateManager().StoreMgr->enterStackFrame(this, callerCtx, calleeCtx); return makeWithStore(new_store); } @@ -238,9 +263,12 @@ SVal ProgramState::getSVal(Loc location, QualType T) const { return V; } -const ProgramState *ProgramState::BindExpr(const Stmt *S, SVal V, bool Invalidate) const{ - Environment NewEnv = getStateManager().EnvMgr.bindExpr(Env, S, V, - Invalidate); +ProgramStateRef ProgramState::BindExpr(const Stmt *S, + const LocationContext *LCtx, + SVal V, bool Invalidate) const{ + Environment NewEnv = + getStateManager().EnvMgr.bindExpr(Env, EnvironmentEntry(S, LCtx), V, + Invalidate); if (NewEnv == Env) return this; @@ -249,10 +277,14 @@ const ProgramState *ProgramState::BindExpr(const Stmt *S, SVal V, bool Invalidat return getStateManager().getPersistentState(NewSt); } -const ProgramState *ProgramState::bindExprAndLocation(const Stmt *S, SVal location, - SVal V) const { +ProgramStateRef +ProgramState::bindExprAndLocation(const Stmt *S, const LocationContext *LCtx, + SVal location, + SVal V) const { Environment NewEnv = - getStateManager().EnvMgr.bindExprAndLocation(Env, S, location, V); + getStateManager().EnvMgr.bindExprAndLocation(Env, + EnvironmentEntry(S, LCtx), + location, V); if (NewEnv == Env) return this; @@ -262,9 +294,10 @@ const ProgramState *ProgramState::bindExprAndLocation(const Stmt *S, SVal locati return getStateManager().getPersistentState(NewSt); } -const ProgramState *ProgramState::assumeInBound(DefinedOrUnknownSVal Idx, +ProgramStateRef ProgramState::assumeInBound(DefinedOrUnknownSVal Idx, DefinedOrUnknownSVal UpperBound, - bool Assumption) const { + bool Assumption, + QualType indexTy) const { if (Idx.isUnknown() || UpperBound.isUnknown()) return this; @@ -278,7 +311,8 @@ const ProgramState *ProgramState::assumeInBound(DefinedOrUnknownSVal Idx, // Get the offset: the minimum value of the array index type. BasicValueFactory &BVF = svalBuilder.getBasicValueFactory(); // FIXME: This should be using ValueManager::ArrayindexTy...somehow. - QualType indexTy = Ctx.IntTy; + if (indexTy.isNull()) + indexTy = Ctx.IntTy; nonloc::ConcreteInt Min(BVF.getMinValue(indexTy)); // Adjust the index. @@ -307,7 +341,7 @@ const ProgramState *ProgramState::assumeInBound(DefinedOrUnknownSVal Idx, return CM.assume(this, cast<DefinedSVal>(inBound), Assumption); } -const ProgramState *ProgramStateManager::getInitialState(const LocationContext *InitLoc) { +ProgramStateRef ProgramStateManager::getInitialState(const LocationContext *InitLoc) { ProgramState State(this, EnvMgr.getInitialEnvironment(), StoreMgr->getInitialStore(InitLoc), @@ -316,28 +350,15 @@ const ProgramState *ProgramStateManager::getInitialState(const LocationContext * return getPersistentState(State); } -void ProgramStateManager::recycleUnusedStates() { - for (std::vector<ProgramState*>::iterator i = recentlyAllocatedStates.begin(), - e = recentlyAllocatedStates.end(); i != e; ++i) { - ProgramState *state = *i; - if (state->referencedByExplodedNode()) - continue; - StateSet.RemoveNode(state); - freeStates.push_back(state); - state->~ProgramState(); - } - recentlyAllocatedStates.clear(); -} - -const ProgramState *ProgramStateManager::getPersistentStateWithGDM( - const ProgramState *FromState, - const ProgramState *GDMState) { - ProgramState NewState = *FromState; +ProgramStateRef ProgramStateManager::getPersistentStateWithGDM( + ProgramStateRef FromState, + ProgramStateRef GDMState) { + ProgramState NewState(*FromState); NewState.GDM = GDMState->GDM; return getPersistentState(NewState); } -const ProgramState *ProgramStateManager::getPersistentState(ProgramState &State) { +ProgramStateRef ProgramStateManager::getPersistentState(ProgramState &State) { llvm::FoldingSetNodeID ID; State.Profile(ID); @@ -356,12 +377,11 @@ const ProgramState *ProgramStateManager::getPersistentState(ProgramState &State) } new (newState) ProgramState(State); StateSet.InsertNode(newState, InsertPos); - recentlyAllocatedStates.push_back(newState); return newState; } -const ProgramState *ProgramState::makeWithStore(const StoreRef &store) const { - ProgramState NewSt = *this; +ProgramStateRef ProgramState::makeWithStore(const StoreRef &store) const { + ProgramState NewSt(*this); NewSt.setStore(store); return getStateManager().getPersistentState(NewSt); } @@ -379,92 +399,44 @@ void ProgramState::setStore(const StoreRef &newStore) { // State pretty-printing. //===----------------------------------------------------------------------===// -static bool IsEnvLoc(const Stmt *S) { - // FIXME: This is a layering violation. Should be in environment. - return (bool) (((uintptr_t) S) & 0x1); -} - -void ProgramState::print(raw_ostream &Out, CFG &C, +void ProgramState::print(raw_ostream &Out, const char *NL, const char *Sep) const { // Print the store. ProgramStateManager &Mgr = getStateManager(); Mgr.getStoreManager().print(getStore(), Out, NL, Sep); - // Print Subexpression bindings. - bool isFirst = true; + // Print out the environment. + Env.print(Out, NL, Sep); - // FIXME: All environment printing should be moved inside Environment. - for (Environment::iterator I = Env.begin(), E = Env.end(); I != E; ++I) { - if (C.isBlkExpr(I.getKey()) || IsEnvLoc(I.getKey())) - continue; - - if (isFirst) { - Out << NL << NL << "Sub-Expressions:" << NL; - isFirst = false; - } else { - Out << NL; - } + // Print out the constraints. + Mgr.getConstraintManager().print(this, Out, NL, Sep); - Out << " (" << (void*) I.getKey() << ") "; - LangOptions LO; // FIXME. - I.getKey()->printPretty(Out, 0, PrintingPolicy(LO)); - Out << " : " << I.getData(); - } + // Print checker-specific data. + Mgr.getOwningEngine()->printState(Out, this, NL, Sep); +} - // Print block-expression bindings. - isFirst = true; +void ProgramState::printDOT(raw_ostream &Out) const { + print(Out, "\\l", "\\|"); +} - for (Environment::iterator I = Env.begin(), E = Env.end(); I != E; ++I) { - if (!C.isBlkExpr(I.getKey())) - continue; +void ProgramState::dump() const { + print(llvm::errs()); +} - if (isFirst) { - Out << NL << NL << "Block-level Expressions:" << NL; - isFirst = false; - } else { - Out << NL; - } +void ProgramState::printTaint(raw_ostream &Out, + const char *NL, const char *Sep) const { + TaintMapImpl TM = get<TaintMap>(); - Out << " (" << (void*) I.getKey() << ") "; - LangOptions LO; // FIXME. - I.getKey()->printPretty(Out, 0, PrintingPolicy(LO)); - Out << " : " << I.getData(); - } - - // Print locations. - isFirst = true; - - for (Environment::iterator I = Env.begin(), E = Env.end(); I != E; ++I) { - if (!IsEnvLoc(I.getKey())) - continue; - - if (isFirst) { - Out << NL << NL << "Load/store locations:" << NL; - isFirst = false; - } else { - Out << NL; - } + if (!TM.isEmpty()) + Out <<"Tainted Symbols:" << NL; - const Stmt *S = (Stmt*) (((uintptr_t) I.getKey()) & ((uintptr_t) ~0x1)); - - Out << " (" << (void*) S << ") "; - LangOptions LO; // FIXME. - S->printPretty(Out, 0, PrintingPolicy(LO)); - Out << " : " << I.getData(); + for (TaintMapImpl::iterator I = TM.begin(), E = TM.end(); I != E; ++I) { + Out << I->first << " : " << I->second << NL; } - - Mgr.getConstraintManager().print(this, Out, NL, Sep); - - // Print checker-specific data. - Mgr.getOwningEngine()->printState(Out, this, NL, Sep); -} - -void ProgramState::printDOT(raw_ostream &Out, CFG &C) const { - print(Out, C, "\\l", "\\|"); } -void ProgramState::printStdErr(CFG &C) const { - print(llvm::errs(), C); +void ProgramState::dumpTaint() const { + printTaint(llvm::errs()); } //===----------------------------------------------------------------------===// @@ -489,7 +461,7 @@ ProgramStateManager::FindGDMContext(void *K, return p.first; } -const ProgramState *ProgramStateManager::addGDM(const ProgramState *St, void *Key, void *Data){ +ProgramStateRef ProgramStateManager::addGDM(ProgramStateRef St, void *Key, void *Data){ ProgramState::GenericDataMap M1 = St->getGDM(); ProgramState::GenericDataMap M2 = GDMFactory.add(M1, Key, Data); @@ -501,7 +473,7 @@ const ProgramState *ProgramStateManager::addGDM(const ProgramState *St, void *Ke return getPersistentState(NewSt); } -const ProgramState *ProgramStateManager::removeGDM(const ProgramState *state, void *Key) { +ProgramStateRef ProgramStateManager::removeGDM(ProgramStateRef state, void *Key) { ProgramState::GenericDataMap OldM = state->getGDM(); ProgramState::GenericDataMap NewM = GDMFactory.remove(OldM, Key); @@ -513,6 +485,8 @@ const ProgramState *ProgramStateManager::removeGDM(const ProgramState *state, vo return getPersistentState(NewState); } +void ScanReachableSymbols::anchor() { } + bool ScanReachableSymbols::scan(nonloc::CompoundVal val) { for (nonloc::CompoundVal::iterator I=val.begin(), E=val.end(); I!=E; ++I) if (!scan(*I)) @@ -527,10 +501,10 @@ bool ScanReachableSymbols::scan(const SymExpr *sym) { return true; isVisited = 1; - if (const SymbolData *sData = dyn_cast<SymbolData>(sym)) - if (!visitor.VisitSymbol(sData)) - return false; + if (!visitor.VisitSymbol(sym)) + return false; + // TODO: should be rewritten using SymExpr::symbol_iterator. switch (sym->getKind()) { case SymExpr::RegionValueKind: case SymExpr::ConjuredKind: @@ -538,8 +512,12 @@ bool ScanReachableSymbols::scan(const SymExpr *sym) { case SymExpr::ExtentKind: case SymExpr::MetadataKind: break; + case SymExpr::CastSymbolKind: + return scan(cast<SymbolCast>(sym)->getOperand()); case SymExpr::SymIntKind: return scan(cast<SymIntExpr>(sym)->getLHS()); + case SymExpr::IntSymKind: + return scan(cast<IntSymExpr>(sym)->getRHS()); case SymExpr::SymSymKind: { const SymSymExpr *x = cast<SymSymExpr>(sym); return scan(x->getLHS()) && scan(x->getRHS()); @@ -575,6 +553,10 @@ bool ScanReachableSymbols::scan(const MemRegion *R) { if (isVisited) return true; isVisited = 1; + + + if (!visitor.VisitMemRegion(R)) + return false; // If this is a symbolic region, visit the symbol for the region. if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(R)) @@ -623,3 +605,105 @@ bool ProgramState::scanReachableSymbols(const MemRegion * const *I, } return true; } + +ProgramStateRef ProgramState::addTaint(const Stmt *S, + const LocationContext *LCtx, + TaintTagType Kind) const { + if (const Expr *E = dyn_cast_or_null<Expr>(S)) + S = E->IgnoreParens(); + + SymbolRef Sym = getSVal(S, LCtx).getAsSymbol(); + if (Sym) + return addTaint(Sym, Kind); + + const MemRegion *R = getSVal(S, LCtx).getAsRegion(); + addTaint(R, Kind); + + // Cannot add taint, so just return the state. + return this; +} + +ProgramStateRef ProgramState::addTaint(const MemRegion *R, + TaintTagType Kind) const { + if (const SymbolicRegion *SR = dyn_cast_or_null<SymbolicRegion>(R)) + return addTaint(SR->getSymbol(), Kind); + return this; +} + +ProgramStateRef ProgramState::addTaint(SymbolRef Sym, + TaintTagType Kind) const { + // If this is a symbol cast, remove the cast before adding the taint. Taint + // is cast agnostic. + while (const SymbolCast *SC = dyn_cast<SymbolCast>(Sym)) + Sym = SC->getOperand(); + + ProgramStateRef NewState = set<TaintMap>(Sym, Kind); + assert(NewState); + return NewState; +} + +bool ProgramState::isTainted(const Stmt *S, const LocationContext *LCtx, + TaintTagType Kind) const { + if (const Expr *E = dyn_cast_or_null<Expr>(S)) + S = E->IgnoreParens(); + + SVal val = getSVal(S, LCtx); + return isTainted(val, Kind); +} + +bool ProgramState::isTainted(SVal V, TaintTagType Kind) const { + if (const SymExpr *Sym = V.getAsSymExpr()) + return isTainted(Sym, Kind); + if (const MemRegion *Reg = V.getAsRegion()) + return isTainted(Reg, Kind); + return false; +} + +bool ProgramState::isTainted(const MemRegion *Reg, TaintTagType K) const { + if (!Reg) + return false; + + // Element region (array element) is tainted if either the base or the offset + // are tainted. + if (const ElementRegion *ER = dyn_cast<ElementRegion>(Reg)) + return isTainted(ER->getSuperRegion(), K) || isTainted(ER->getIndex(), K); + + if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(Reg)) + return isTainted(SR->getSymbol(), K); + + if (const SubRegion *ER = dyn_cast<SubRegion>(Reg)) + return isTainted(ER->getSuperRegion(), K); + + return false; +} + +bool ProgramState::isTainted(SymbolRef Sym, TaintTagType Kind) const { + if (!Sym) + return false; + + // Traverse all the symbols this symbol depends on to see if any are tainted. + bool Tainted = false; + for (SymExpr::symbol_iterator SI = Sym->symbol_begin(), SE =Sym->symbol_end(); + SI != SE; ++SI) { + assert(isa<SymbolData>(*SI)); + const TaintTagType *Tag = get<TaintMap>(*SI); + Tainted = (Tag && *Tag == Kind); + + // If this is a SymbolDerived with a tainted parent, it's also tainted. + if (const SymbolDerived *SD = dyn_cast<SymbolDerived>(*SI)) + Tainted = Tainted || isTainted(SD->getParentSymbol(), Kind); + + // If memory region is tainted, data is also tainted. + if (const SymbolRegionValue *SRV = dyn_cast<SymbolRegionValue>(*SI)) + Tainted = Tainted || isTainted(SRV->getRegion(), Kind); + + // If If this is a SymbolCast from a tainted value, it's also tainted. + if (const SymbolCast *SC = dyn_cast<SymbolCast>(*SI)) + Tainted = Tainted || isTainted(SC->getOperand(), Kind); + + if (Tainted) + return true; + } + + return Tainted; +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp index 9337788..98eb958 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp @@ -204,46 +204,46 @@ struct ProgramStateTrait<ConstraintRange> namespace { class RangeConstraintManager : public SimpleConstraintManager{ - RangeSet GetRange(const ProgramState *state, SymbolRef sym); + RangeSet GetRange(ProgramStateRef state, SymbolRef sym); public: RangeConstraintManager(SubEngine &subengine) : SimpleConstraintManager(subengine) {} - const ProgramState *assumeSymNE(const ProgramState *state, SymbolRef sym, + ProgramStateRef assumeSymNE(ProgramStateRef state, SymbolRef sym, const llvm::APSInt& Int, const llvm::APSInt& Adjustment); - const ProgramState *assumeSymEQ(const ProgramState *state, SymbolRef sym, + ProgramStateRef assumeSymEQ(ProgramStateRef state, SymbolRef sym, const llvm::APSInt& Int, const llvm::APSInt& Adjustment); - const ProgramState *assumeSymLT(const ProgramState *state, SymbolRef sym, + ProgramStateRef assumeSymLT(ProgramStateRef state, SymbolRef sym, const llvm::APSInt& Int, const llvm::APSInt& Adjustment); - const ProgramState *assumeSymGT(const ProgramState *state, SymbolRef sym, + ProgramStateRef assumeSymGT(ProgramStateRef state, SymbolRef sym, const llvm::APSInt& Int, const llvm::APSInt& Adjustment); - const ProgramState *assumeSymGE(const ProgramState *state, SymbolRef sym, + ProgramStateRef assumeSymGE(ProgramStateRef state, SymbolRef sym, const llvm::APSInt& Int, const llvm::APSInt& Adjustment); - const ProgramState *assumeSymLE(const ProgramState *state, SymbolRef sym, + ProgramStateRef assumeSymLE(ProgramStateRef state, SymbolRef sym, const llvm::APSInt& Int, const llvm::APSInt& Adjustment); - const llvm::APSInt* getSymVal(const ProgramState *St, SymbolRef sym) const; + const llvm::APSInt* getSymVal(ProgramStateRef St, SymbolRef sym) const; // FIXME: Refactor into SimpleConstraintManager? - bool isEqual(const ProgramState *St, SymbolRef sym, const llvm::APSInt& V) const { + bool isEqual(ProgramStateRef St, SymbolRef sym, const llvm::APSInt& V) const { const llvm::APSInt *i = getSymVal(St, sym); return i ? *i == V : false; } - const ProgramState *removeDeadBindings(const ProgramState *St, SymbolReaper& SymReaper); + ProgramStateRef removeDeadBindings(ProgramStateRef St, SymbolReaper& SymReaper); - void print(const ProgramState *St, raw_ostream &Out, + void print(ProgramStateRef St, raw_ostream &Out, const char* nl, const char *sep); private: @@ -257,7 +257,7 @@ ConstraintManager* ento::CreateRangeConstraintManager(ProgramStateManager&, return new RangeConstraintManager(subeng); } -const llvm::APSInt* RangeConstraintManager::getSymVal(const ProgramState *St, +const llvm::APSInt* RangeConstraintManager::getSymVal(ProgramStateRef St, SymbolRef sym) const { const ConstraintRangeTy::data_type *T = St->get<ConstraintRange>(sym); return T ? T->getConcreteValue() : NULL; @@ -265,8 +265,8 @@ const llvm::APSInt* RangeConstraintManager::getSymVal(const ProgramState *St, /// Scan all symbols referenced by the constraints. If the symbol is not alive /// as marked in LSymbols, mark it as dead in DSymbols. -const ProgramState* -RangeConstraintManager::removeDeadBindings(const ProgramState *state, +ProgramStateRef +RangeConstraintManager::removeDeadBindings(ProgramStateRef state, SymbolReaper& SymReaper) { ConstraintRangeTy CR = state->get<ConstraintRange>(); @@ -282,7 +282,7 @@ RangeConstraintManager::removeDeadBindings(const ProgramState *state, } RangeSet -RangeConstraintManager::GetRange(const ProgramState *state, SymbolRef sym) { +RangeConstraintManager::GetRange(ProgramStateRef state, SymbolRef sym) { if (ConstraintRangeTy::data_type* V = state->get<ConstraintRange>(sym)) return *V; @@ -305,8 +305,8 @@ RangeConstraintManager::GetRange(const ProgramState *state, SymbolRef sym) { // As an example, the range [UINT_MAX-1, 3) contains five values: UINT_MAX-1, // UINT_MAX, 0, 1, and 2. -const ProgramState* -RangeConstraintManager::assumeSymNE(const ProgramState *state, SymbolRef sym, +ProgramStateRef +RangeConstraintManager::assumeSymNE(ProgramStateRef state, SymbolRef sym, const llvm::APSInt& Int, const llvm::APSInt& Adjustment) { BasicValueFactory &BV = state->getBasicVals(); @@ -322,8 +322,8 @@ RangeConstraintManager::assumeSymNE(const ProgramState *state, SymbolRef sym, return New.isEmpty() ? NULL : state->set<ConstraintRange>(sym, New); } -const ProgramState* -RangeConstraintManager::assumeSymEQ(const ProgramState *state, SymbolRef sym, +ProgramStateRef +RangeConstraintManager::assumeSymEQ(ProgramStateRef state, SymbolRef sym, const llvm::APSInt& Int, const llvm::APSInt& Adjustment) { // [Int-Adjustment, Int-Adjustment] @@ -333,8 +333,8 @@ RangeConstraintManager::assumeSymEQ(const ProgramState *state, SymbolRef sym, return New.isEmpty() ? NULL : state->set<ConstraintRange>(sym, New); } -const ProgramState* -RangeConstraintManager::assumeSymLT(const ProgramState *state, SymbolRef sym, +ProgramStateRef +RangeConstraintManager::assumeSymLT(ProgramStateRef state, SymbolRef sym, const llvm::APSInt& Int, const llvm::APSInt& Adjustment) { BasicValueFactory &BV = state->getBasicVals(); @@ -354,8 +354,8 @@ RangeConstraintManager::assumeSymLT(const ProgramState *state, SymbolRef sym, return New.isEmpty() ? NULL : state->set<ConstraintRange>(sym, New); } -const ProgramState* -RangeConstraintManager::assumeSymGT(const ProgramState *state, SymbolRef sym, +ProgramStateRef +RangeConstraintManager::assumeSymGT(ProgramStateRef state, SymbolRef sym, const llvm::APSInt& Int, const llvm::APSInt& Adjustment) { BasicValueFactory &BV = state->getBasicVals(); @@ -375,8 +375,8 @@ RangeConstraintManager::assumeSymGT(const ProgramState *state, SymbolRef sym, return New.isEmpty() ? NULL : state->set<ConstraintRange>(sym, New); } -const ProgramState* -RangeConstraintManager::assumeSymGE(const ProgramState *state, SymbolRef sym, +ProgramStateRef +RangeConstraintManager::assumeSymGE(ProgramStateRef state, SymbolRef sym, const llvm::APSInt& Int, const llvm::APSInt& Adjustment) { BasicValueFactory &BV = state->getBasicVals(); @@ -397,8 +397,8 @@ RangeConstraintManager::assumeSymGE(const ProgramState *state, SymbolRef sym, return New.isEmpty() ? NULL : state->set<ConstraintRange>(sym, New); } -const ProgramState* -RangeConstraintManager::assumeSymLE(const ProgramState *state, SymbolRef sym, +ProgramStateRef +RangeConstraintManager::assumeSymLE(ProgramStateRef state, SymbolRef sym, const llvm::APSInt& Int, const llvm::APSInt& Adjustment) { BasicValueFactory &BV = state->getBasicVals(); @@ -423,18 +423,20 @@ RangeConstraintManager::assumeSymLE(const ProgramState *state, SymbolRef sym, // Pretty-printing. //===------------------------------------------------------------------------===/ -void RangeConstraintManager::print(const ProgramState *St, raw_ostream &Out, +void RangeConstraintManager::print(ProgramStateRef St, raw_ostream &Out, const char* nl, const char *sep) { ConstraintRangeTy Ranges = St->get<ConstraintRange>(); - if (Ranges.isEmpty()) + if (Ranges.isEmpty()) { + Out << nl << sep << "Ranges are empty." << nl; return; + } - Out << nl << sep << "ranges of symbol values:"; - + Out << nl << sep << "Ranges of symbol values:"; for (ConstraintRangeTy::iterator I=Ranges.begin(), E=Ranges.end(); I!=E; ++I){ Out << nl << ' ' << I.getKey() << " : "; I.getData().print(Out); } + Out << nl; } diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/RegionStore.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/RegionStore.cpp index 4b76cf1..cc3ea8c3 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/RegionStore.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/RegionStore.cpp @@ -20,6 +20,7 @@ #include "clang/Analysis/Analyses/LiveVariables.h" #include "clang/Analysis/AnalysisContext.h" #include "clang/Basic/TargetInfo.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" #include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h" @@ -228,6 +229,16 @@ public: /// For DerivedToBase casts, create a CXXBaseObjectRegion and return it. virtual SVal evalDerivedToBase(SVal derived, QualType basePtrType); + /// \brief Evaluates C++ dynamic_cast cast. + /// The callback may result in the following 3 scenarios: + /// - Successful cast (ex: derived is subclass of base). + /// - Failed cast (ex: derived is definitely not a subclass of base). + /// - We don't know (base is a symbolic region and we don't have + /// enough info to determine if the cast will succeed at run time). + /// The function returns an SVal representing the derived class; it's + /// valid only if Failed flag is set to false. + virtual SVal evalDynamicCast(SVal base, QualType derivedPtrType,bool &Failed); + StoreRef getInitialStore(const LocationContext *InitLoc) { return StoreRef(RBFactory.getEmptyMap().getRootWithoutRetain(), *this); } @@ -235,11 +246,18 @@ public: //===-------------------------------------------------------------------===// // Binding values to regions. //===-------------------------------------------------------------------===// + RegionBindings invalidateGlobalRegion(MemRegion::Kind K, + const Expr *Ex, + unsigned Count, + const LocationContext *LCtx, + RegionBindings B, + InvalidatedRegions *Invalidated); StoreRef invalidateRegions(Store store, ArrayRef<const MemRegion *> Regions, const Expr *E, unsigned Count, + const LocationContext *LCtx, InvalidatedSymbols &IS, - bool invalidateGlobals, + const CallOrObjCMessage *Call, InvalidatedRegions *Invalidated); public: // Made public for helper classes. @@ -273,7 +291,8 @@ public: // Part of public interface to class. RegionBindings B = GetRegionBindings(store); assert(!lookup(B, R, BindingKey::Default)); assert(!lookup(B, R, BindingKey::Direct)); - return StoreRef(addBinding(B, R, BindingKey::Default, V).getRootWithoutRetain(), *this); + return StoreRef(addBinding(B, R, BindingKey::Default, V) + .getRootWithoutRetain(), *this); } StoreRef BindCompoundLiteral(Store store, const CompoundLiteralExpr *CL, @@ -308,12 +327,10 @@ public: // Part of public interface to class. bool includedInBindings(Store store, const MemRegion *region) const; - //===------------------------------------------------------------------===// - // Loading values from regions. - //===------------------------------------------------------------------===// - + /// \brief Return the value bound to specified location in a given state. + /// /// The high level logic for this method is this: - /// Retrieve (L) + /// getBinding (L) /// if L has binding /// return L's binding /// else if L is in killset @@ -323,39 +340,39 @@ public: // Part of public interface to class. /// return undefined /// else /// return symbolic - SVal Retrieve(Store store, Loc L, QualType T = QualType()); + SVal getBinding(Store store, Loc L, QualType T = QualType()); - SVal RetrieveElement(Store store, const ElementRegion *R); + SVal getBindingForElement(Store store, const ElementRegion *R); - SVal RetrieveField(Store store, const FieldRegion *R); + SVal getBindingForField(Store store, const FieldRegion *R); - SVal RetrieveObjCIvar(Store store, const ObjCIvarRegion *R); + SVal getBindingForObjCIvar(Store store, const ObjCIvarRegion *R); - SVal RetrieveVar(Store store, const VarRegion *R); + SVal getBindingForVar(Store store, const VarRegion *R); - SVal RetrieveLazySymbol(const TypedValueRegion *R); + SVal getBindingForLazySymbol(const TypedValueRegion *R); - SVal RetrieveFieldOrElementCommon(Store store, const TypedValueRegion *R, - QualType Ty, const MemRegion *superR); + SVal getBindingForFieldOrElementCommon(Store store, const TypedValueRegion *R, + QualType Ty, const MemRegion *superR); - SVal RetrieveLazyBinding(const MemRegion *lazyBindingRegion, - Store lazyBindingStore); + SVal getLazyBinding(const MemRegion *lazyBindingRegion, + Store lazyBindingStore); - /// Retrieve the values in a struct and return a CompoundVal, used when doing - /// struct copy: + /// Get bindings for the values in a struct and return a CompoundVal, used + /// when doing struct copy: /// struct s x, y; /// x = y; /// y's value is retrieved by this method. - SVal RetrieveStruct(Store store, const TypedValueRegion* R); + SVal getBindingForStruct(Store store, const TypedValueRegion* R); - SVal RetrieveArray(Store store, const TypedValueRegion* R); + SVal getBindingForArray(Store store, const TypedValueRegion* R); /// Used to lazily generate derived symbols for bindings that are defined /// implicitly by default bindings in a super region. - Optional<SVal> RetrieveDerivedDefaultValue(RegionBindings B, - const MemRegion *superR, - const TypedValueRegion *R, - QualType Ty); + Optional<SVal> getBindingForDerivedDefaultValue(RegionBindings B, + const MemRegion *superR, + const TypedValueRegion *R, + QualType Ty); /// Get the state and region whose binding this region R corresponds to. std::pair<Store, const MemRegion*> @@ -374,15 +391,16 @@ public: // Part of public interface to class. StoreRef removeDeadBindings(Store store, const StackFrameContext *LCtx, SymbolReaper& SymReaper); - StoreRef enterStackFrame(const ProgramState *state, - const StackFrameContext *frame); + StoreRef enterStackFrame(ProgramStateRef state, + const LocationContext *callerCtx, + const StackFrameContext *calleeCtx); //===------------------------------------------------------------------===// // Region "extents". //===------------------------------------------------------------------===// // FIXME: This method will soon be eliminated; see the note in Store.h. - DefinedOrUnknownSVal getSizeInElements(const ProgramState *state, + DefinedOrUnknownSVal getSizeInElements(ProgramStateRef state, const MemRegion* R, QualType EleTy); //===------------------------------------------------------------------===// @@ -422,7 +440,8 @@ StoreManager *ento::CreateRegionStoreManager(ProgramStateManager& StMgr) { return new RegionStoreManager(StMgr, F); } -StoreManager *ento::CreateFieldsOnlyRegionStoreManager(ProgramStateManager &StMgr) { +StoreManager * +ento::CreateFieldsOnlyRegionStoreManager(ProgramStateManager &StMgr) { RegionStoreFeatures F = minimal_features_tag(); F.enableFields(true); return new RegionStoreManager(StMgr, F); @@ -587,6 +606,7 @@ class invalidateRegionsWorker : public ClusterAnalysis<invalidateRegionsWorker> { const Expr *Ex; unsigned Count; + const LocationContext *LCtx; StoreManager::InvalidatedSymbols &IS; StoreManager::InvalidatedRegions *Regions; public: @@ -594,11 +614,12 @@ public: ProgramStateManager &stateMgr, RegionBindings b, const Expr *ex, unsigned count, + const LocationContext *lctx, StoreManager::InvalidatedSymbols &is, StoreManager::InvalidatedRegions *r, bool includeGlobals) : ClusterAnalysis<invalidateRegionsWorker>(rm, stateMgr, b, includeGlobals), - Ex(ex), Count(count), IS(is), Regions(r) {} + Ex(ex), Count(count), LCtx(lctx), IS(is), Regions(r) {} void VisitCluster(const MemRegion *baseR, BindingKey *I, BindingKey *E); void VisitBaseRegion(const MemRegion *baseR); @@ -674,7 +695,7 @@ void invalidateRegionsWorker::VisitBaseRegion(const MemRegion *baseR) { // Invalidate the region by setting its default value to // conjured symbol. The type of the symbol is irrelavant. DefinedOrUnknownSVal V = - svalBuilder.getConjuredSymbolVal(baseR, Ex, Ctx.IntTy, Count); + svalBuilder.getConjuredSymbolVal(baseR, Ex, LCtx, Ctx.IntTy, Count); B = RM.addBinding(B, baseR, BindingKey::Default, V); return; } @@ -690,7 +711,7 @@ void invalidateRegionsWorker::VisitBaseRegion(const MemRegion *baseR) { // Invalidate the region by setting its default value to // conjured symbol. The type of the symbol is irrelavant. DefinedOrUnknownSVal V = - svalBuilder.getConjuredSymbolVal(baseR, Ex, Ctx.IntTy, Count); + svalBuilder.getConjuredSymbolVal(baseR, Ex, LCtx, Ctx.IntTy, Count); B = RM.addBinding(B, baseR, BindingKey::Default, V); return; } @@ -698,7 +719,8 @@ void invalidateRegionsWorker::VisitBaseRegion(const MemRegion *baseR) { if (const ArrayType *AT = Ctx.getAsArrayType(T)) { // Set the default value of the array to conjured symbol. DefinedOrUnknownSVal V = - svalBuilder.getConjuredSymbolVal(baseR, Ex, AT->getElementType(), Count); + svalBuilder.getConjuredSymbolVal(baseR, Ex, LCtx, + AT->getElementType(), Count); B = RM.addBinding(B, baseR, BindingKey::Default, V); return; } @@ -713,20 +735,47 @@ void invalidateRegionsWorker::VisitBaseRegion(const MemRegion *baseR) { } - DefinedOrUnknownSVal V = svalBuilder.getConjuredSymbolVal(baseR, Ex, T, Count); + DefinedOrUnknownSVal V = svalBuilder.getConjuredSymbolVal(baseR, Ex, LCtx, + T,Count); assert(SymbolManager::canSymbolicate(T) || V.isUnknown()); B = RM.addBinding(B, baseR, BindingKey::Direct, V); } +RegionBindings RegionStoreManager::invalidateGlobalRegion(MemRegion::Kind K, + const Expr *Ex, + unsigned Count, + const LocationContext *LCtx, + RegionBindings B, + InvalidatedRegions *Invalidated) { + // Bind the globals memory space to a new symbol that we will use to derive + // the bindings for all globals. + const GlobalsSpaceRegion *GS = MRMgr.getGlobalsRegion(K); + SVal V = + svalBuilder.getConjuredSymbolVal(/* SymbolTag = */ (void*) GS, Ex, LCtx, + /* symbol type, doesn't matter */ Ctx.IntTy, + Count); + + B = removeBinding(B, GS); + B = addBinding(B, BindingKey::Make(GS, BindingKey::Default), V); + + // Even if there are no bindings in the global scope, we still need to + // record that we touched it. + if (Invalidated) + Invalidated->push_back(GS); + + return B; +} + StoreRef RegionStoreManager::invalidateRegions(Store store, ArrayRef<const MemRegion *> Regions, const Expr *Ex, unsigned Count, + const LocationContext *LCtx, InvalidatedSymbols &IS, - bool invalidateGlobals, + const CallOrObjCMessage *Call, InvalidatedRegions *Invalidated) { invalidateRegionsWorker W(*this, StateMgr, RegionStoreManager::GetRegionBindings(store), - Ex, Count, IS, Invalidated, invalidateGlobals); + Ex, Count, LCtx, IS, Invalidated, false); // Scan the bindings and generate the clusters. W.GenerateClusters(); @@ -741,20 +790,20 @@ StoreRef RegionStoreManager::invalidateRegions(Store store, // Return the new bindings. RegionBindings B = W.getRegionBindings(); - 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, Ex, - /* symbol type, doesn't matter */ Ctx.IntTy, - Count); - B = addBinding(B, BindingKey::Make(GS, BindingKey::Default), V); - - // Even if there are no bindings in the global scope, we still need to - // record that we touched it. - if (Invalidated) - Invalidated->push_back(GS); + // For all globals which are not static nor immutable: determine which global + // regions should be invalidated and invalidate them. + // TODO: This could possibly be more precise with modules. + // + // System calls invalidate only system globals. + if (Call && Call->isInSystemHeader()) { + B = invalidateGlobalRegion(MemRegion::GlobalSystemSpaceRegionKind, + Ex, Count, LCtx, B, Invalidated); + // Internal calls might invalidate both system and internal globals. + } else { + B = invalidateGlobalRegion(MemRegion::GlobalSystemSpaceRegionKind, + Ex, Count, LCtx, B, Invalidated); + B = invalidateGlobalRegion(MemRegion::GlobalInternalSpaceRegionKind, + Ex, Count, LCtx, B, Invalidated); } return StoreRef(B.getRootWithoutRetain(), *this); @@ -764,9 +813,10 @@ StoreRef RegionStoreManager::invalidateRegions(Store store, // Extents for regions. //===----------------------------------------------------------------------===// -DefinedOrUnknownSVal RegionStoreManager::getSizeInElements(const ProgramState *state, - const MemRegion *R, - QualType EleTy) { +DefinedOrUnknownSVal +RegionStoreManager::getSizeInElements(ProgramStateRef state, + const MemRegion *R, + QualType EleTy) { SVal Size = cast<SubRegion>(R)->getExtent(svalBuilder); const llvm::APSInt *SizeInt = svalBuilder.getKnownValue(state, Size); if (!SizeInt) @@ -837,6 +887,75 @@ SVal RegionStoreManager::evalDerivedToBase(SVal derived, QualType baseType) { return loc::MemRegionVal(baseReg); } +SVal RegionStoreManager::evalDynamicCast(SVal base, QualType derivedType, + bool &Failed) { + Failed = false; + + loc::MemRegionVal *baseRegVal = dyn_cast<loc::MemRegionVal>(&base); + if (!baseRegVal) + return UnknownVal(); + const MemRegion *BaseRegion = baseRegVal->stripCasts(); + + // Assume the derived class is a pointer or a reference to a CXX record. + derivedType = derivedType->getPointeeType(); + assert(!derivedType.isNull()); + const CXXRecordDecl *DerivedDecl = derivedType->getAsCXXRecordDecl(); + if (!DerivedDecl && !derivedType->isVoidType()) + return UnknownVal(); + + // Drill down the CXXBaseObject chains, which represent upcasts (casts from + // derived to base). + const MemRegion *SR = BaseRegion; + while (const TypedRegion *TSR = dyn_cast_or_null<TypedRegion>(SR)) { + QualType BaseType = TSR->getLocationType()->getPointeeType(); + assert(!BaseType.isNull()); + const CXXRecordDecl *SRDecl = BaseType->getAsCXXRecordDecl(); + if (!SRDecl) + return UnknownVal(); + + // If found the derived class, the cast succeeds. + if (SRDecl == DerivedDecl) + return loc::MemRegionVal(TSR); + + // If the region type is a subclass of the derived type. + if (!derivedType->isVoidType() && SRDecl->isDerivedFrom(DerivedDecl)) { + // This occurs in two cases. + // 1) We are processing an upcast. + // 2) We are processing a downcast but we jumped directly from the + // ancestor to a child of the cast value, so conjure the + // appropriate region to represent value (the intermediate node). + return loc::MemRegionVal(MRMgr.getCXXBaseObjectRegion(DerivedDecl, + BaseRegion)); + } + + // If super region is not a parent of derived class, the cast definitely + // fails. + if (!derivedType->isVoidType() && + DerivedDecl->isProvablyNotDerivedFrom(SRDecl)) { + Failed = true; + return UnknownVal(); + } + + if (const CXXBaseObjectRegion *R = dyn_cast<CXXBaseObjectRegion>(TSR)) + // Drill down the chain to get the derived classes. + SR = R->getSuperRegion(); + else { + // We reached the bottom of the hierarchy. + + // If this is a cast to void*, return the region. + if (derivedType->isVoidType()) + return loc::MemRegionVal(TSR); + + // We did not find the derived class. We we must be casting the base to + // derived, so the cast should fail. + Failed = true; + return UnknownVal(); + } + } + + return UnknownVal(); +} + //===----------------------------------------------------------------------===// // Loading values from regions. //===----------------------------------------------------------------------===// @@ -863,7 +982,7 @@ Optional<SVal> RegionStoreManager::getDefaultBinding(RegionBindings B, return Optional<SVal>(); } -SVal RegionStoreManager::Retrieve(Store store, Loc L, QualType T) { +SVal RegionStoreManager::getBinding(Store store, Loc L, QualType T) { assert(!isa<UnknownVal>(L) && "location unknown"); assert(!isa<UndefinedVal>(L) && "location undefined"); @@ -882,18 +1001,20 @@ SVal RegionStoreManager::Retrieve(Store store, Loc L, QualType T) { const MemRegion *MR = cast<loc::MemRegionVal>(L).getRegion(); - if (isa<AllocaRegion>(MR) || isa<SymbolicRegion>(MR)) { + if (isa<AllocaRegion>(MR) || + isa<SymbolicRegion>(MR) || + isa<CodeTextRegion>(MR)) { if (T.isNull()) { - const SymbolicRegion *SR = cast<SymbolicRegion>(MR); - T = SR->getSymbol()->getType(Ctx); + if (const TypedRegion *TR = dyn_cast<TypedRegion>(MR)) + T = TR->getLocationType(); + else { + const SymbolicRegion *SR = cast<SymbolicRegion>(MR); + T = SR->getSymbol()->getType(Ctx); + } } MR = GetElementZeroRegion(MR, T); } - if (isa<CodeTextRegion>(MR)) { - llvm_unreachable("Why load from a code text region?"); - } - // FIXME: Perhaps this method should just take a 'const MemRegion*' argument // instead of 'Loc', and have the other Loc cases handled at a higher level. const TypedValueRegion *R = cast<TypedValueRegion>(MR); @@ -909,21 +1030,21 @@ SVal RegionStoreManager::Retrieve(Store store, Loc L, QualType T) { // Such funny addressing will occur due to layering of regions. if (RTy->isStructureOrClassType()) - return RetrieveStruct(store, R); + return getBindingForStruct(store, R); // FIXME: Handle unions. if (RTy->isUnionType()) return UnknownVal(); if (RTy->isArrayType()) - return RetrieveArray(store, R); + return getBindingForArray(store, R); // FIXME: handle Vector types. if (RTy->isVectorType()) return UnknownVal(); if (const FieldRegion* FR = dyn_cast<FieldRegion>(R)) - return CastRetrievedVal(RetrieveField(store, FR), FR, T, false); + return CastRetrievedVal(getBindingForField(store, FR), FR, T, false); if (const ElementRegion* ER = dyn_cast<ElementRegion>(R)) { // FIXME: Here we actually perform an implicit conversion from the loaded @@ -931,7 +1052,7 @@ SVal RegionStoreManager::Retrieve(Store store, Loc L, QualType T) { // more intelligently. For example, an 'element' can encompass multiple // bound regions (e.g., several bound bytes), or could be a subset of // a larger value. - return CastRetrievedVal(RetrieveElement(store, ER), ER, T, false); + return CastRetrievedVal(getBindingForElement(store, ER), ER, T, false); } if (const ObjCIvarRegion *IVR = dyn_cast<ObjCIvarRegion>(R)) { @@ -941,7 +1062,7 @@ SVal RegionStoreManager::Retrieve(Store store, Loc L, QualType T) { // reinterpretted, it is possible we stored a different value that could // fit within the ivar. Either we need to cast these when storing them // or reinterpret them lazily (as we do here). - return CastRetrievedVal(RetrieveObjCIvar(store, IVR), IVR, T, false); + return CastRetrievedVal(getBindingForObjCIvar(store, IVR), IVR, T, false); } if (const VarRegion *VR = dyn_cast<VarRegion>(R)) { @@ -951,7 +1072,7 @@ SVal RegionStoreManager::Retrieve(Store store, Loc L, QualType T) { // variable is reinterpretted, it is possible we stored a different value // that could fit within the variable. Either we need to cast these when // storing them or reinterpret them lazily (as we do here). - return CastRetrievedVal(RetrieveVar(store, VR), VR, T, false); + return CastRetrievedVal(getBindingForVar(store, VR), VR, T, false); } RegionBindings B = GetRegionBindings(store); @@ -1021,7 +1142,7 @@ RegionStoreManager::GetLazyBinding(RegionBindings B, const MemRegion *R, return std::make_pair((Store) 0, (const MemRegion *) 0); } -SVal RegionStoreManager::RetrieveElement(Store store, +SVal RegionStoreManager::getBindingForElement(Store store, const ElementRegion* R) { // Check if the region has a binding. RegionBindings B = GetRegionBindings(store); @@ -1043,15 +1164,15 @@ SVal RegionStoreManager::RetrieveElement(Store store, if (nonloc::ConcreteInt *CI = dyn_cast<nonloc::ConcreteInt>(&Idx)) { int64_t i = CI->getValue().getSExtValue(); // Abort on string underrun. This can be possible by arbitrary - // clients of RetrieveElement(). + // clients of getBindingForElement(). if (i < 0) return UndefinedVal(); - int64_t byteLength = Str->getByteLength(); - // Technically, only i == byteLength is guaranteed to be null. + int64_t length = Str->getLength(); + // Technically, only i == length is guaranteed to be null. // However, such overflows should be caught before reaching this point; // the only time such an access would be made is if a string literal was // used to initialize a larger array. - char c = (i >= byteLength) ? '\0' : Str->getString()[i]; + char c = (i >= length) ? '\0' : Str->getCodeUnit(i); return svalBuilder.makeIntVal(c, T); } } @@ -1093,10 +1214,11 @@ SVal RegionStoreManager::RetrieveElement(Store store, } } } - return RetrieveFieldOrElementCommon(store, R, R->getElementType(), superR); + return getBindingForFieldOrElementCommon(store, R, R->getElementType(), + superR); } -SVal RegionStoreManager::RetrieveField(Store store, +SVal RegionStoreManager::getBindingForField(Store store, const FieldRegion* R) { // Check if the region has a binding. @@ -1105,14 +1227,14 @@ SVal RegionStoreManager::RetrieveField(Store store, return *V; QualType Ty = R->getValueType(); - return RetrieveFieldOrElementCommon(store, R, Ty, R->getSuperRegion()); + return getBindingForFieldOrElementCommon(store, R, Ty, R->getSuperRegion()); } Optional<SVal> -RegionStoreManager::RetrieveDerivedDefaultValue(RegionBindings B, - const MemRegion *superR, - const TypedValueRegion *R, - QualType Ty) { +RegionStoreManager::getBindingForDerivedDefaultValue(RegionBindings B, + const MemRegion *superR, + const TypedValueRegion *R, + QualType Ty) { if (const Optional<SVal> &D = getDefaultBinding(B, superR)) { const SVal &val = D.getValue(); @@ -1135,30 +1257,39 @@ RegionStoreManager::RetrieveDerivedDefaultValue(RegionBindings B, return Optional<SVal>(); } -SVal RegionStoreManager::RetrieveLazyBinding(const MemRegion *lazyBindingRegion, +SVal RegionStoreManager::getLazyBinding(const MemRegion *lazyBindingRegion, Store lazyBindingStore) { if (const ElementRegion *ER = dyn_cast<ElementRegion>(lazyBindingRegion)) - return RetrieveElement(lazyBindingStore, ER); + return getBindingForElement(lazyBindingStore, ER); - return RetrieveField(lazyBindingStore, - cast<FieldRegion>(lazyBindingRegion)); + return getBindingForField(lazyBindingStore, + cast<FieldRegion>(lazyBindingRegion)); } -SVal RegionStoreManager::RetrieveFieldOrElementCommon(Store store, +SVal RegionStoreManager::getBindingForFieldOrElementCommon(Store store, const TypedValueRegion *R, QualType Ty, const MemRegion *superR) { - // At this point we have already checked in either RetrieveElement or - // RetrieveField if 'R' has a direct binding. - + // At this point we have already checked in either getBindingForElement or + // getBindingForField if 'R' has a direct binding. RegionBindings B = GetRegionBindings(store); + + // Record whether or not we see a symbolic index. That can completely + // be out of scope of our lookup. + bool hasSymbolicIndex = false; while (superR) { if (const Optional<SVal> &D = - RetrieveDerivedDefaultValue(B, superR, R, Ty)) + getBindingForDerivedDefaultValue(B, superR, R, Ty)) return *D; + if (const ElementRegion *ER = dyn_cast<ElementRegion>(superR)) { + NonLoc index = ER->getIndex(); + if (!index.isConstant()) + hasSymbolicIndex = true; + } + // If our super region is a field or element itself, walk up the region // hierarchy to see if there is a default value installed in an ancestor. if (const SubRegion *SR = dyn_cast<SubRegion>(superR)) { @@ -1174,10 +1305,10 @@ SVal RegionStoreManager::RetrieveFieldOrElementCommon(Store store, llvm::tie(lazyBindingStore, lazyBindingRegion) = GetLazyBinding(B, R, R); if (lazyBindingRegion) - return RetrieveLazyBinding(lazyBindingRegion, lazyBindingStore); + return getLazyBinding(lazyBindingRegion, lazyBindingStore); if (R->hasStackNonParametersStorage()) { - if (const ElementRegion *ER = dyn_cast<ElementRegion>(R)) { + if (isa<ElementRegion>(R)) { // Currently we don't reason specially about Clang-style vectors. Check // if superR is a vector and if so return Unknown. if (const TypedValueRegion *typedSuperR = @@ -1185,13 +1316,15 @@ SVal RegionStoreManager::RetrieveFieldOrElementCommon(Store store, if (typedSuperR->getValueType()->isVectorType()) return UnknownVal(); } - - // FIXME: We also need to take ElementRegions with symbolic indexes into - // account. - if (!ER->getIndex().isConstant()) - return UnknownVal(); } + // FIXME: We also need to take ElementRegions with symbolic indexes into + // account. This case handles both directly accessing an ElementRegion + // with a symbolic offset, but also fields within an element with + // a symbolic offset. + if (hasSymbolicIndex) + return UnknownVal(); + return UndefinedVal(); } @@ -1199,7 +1332,8 @@ SVal RegionStoreManager::RetrieveFieldOrElementCommon(Store store, return svalBuilder.getRegionValueSymbolVal(R); } -SVal RegionStoreManager::RetrieveObjCIvar(Store store, const ObjCIvarRegion* R){ +SVal RegionStoreManager::getBindingForObjCIvar(Store store, + const ObjCIvarRegion* R) { // Check if the region has a binding. RegionBindings B = GetRegionBindings(store); @@ -1218,10 +1352,10 @@ SVal RegionStoreManager::RetrieveObjCIvar(Store store, const ObjCIvarRegion* R){ return UnknownVal(); } - return RetrieveLazySymbol(R); + return getBindingForLazySymbol(R); } -SVal RegionStoreManager::RetrieveVar(Store store, const VarRegion *R) { +SVal RegionStoreManager::getBindingForVar(Store store, const VarRegion *R) { // Check if the region has a binding. RegionBindings B = GetRegionBindings(store); @@ -1253,7 +1387,8 @@ SVal RegionStoreManager::RetrieveVar(Store store, const VarRegion *R) { } } - if (const Optional<SVal> &V = RetrieveDerivedDefaultValue(B, MS, R, CT)) + if (const Optional<SVal> &V + = getBindingForDerivedDefaultValue(B, MS, R, CT)) return V.getValue(); return svalBuilder.getRegionValueSymbolVal(R); @@ -1270,19 +1405,18 @@ SVal RegionStoreManager::RetrieveVar(Store store, const VarRegion *R) { return UndefinedVal(); } -SVal RegionStoreManager::RetrieveLazySymbol(const TypedValueRegion *R) { +SVal RegionStoreManager::getBindingForLazySymbol(const TypedValueRegion *R) { // All other values are symbolic. return svalBuilder.getRegionValueSymbolVal(R); } -SVal RegionStoreManager::RetrieveStruct(Store store, +SVal RegionStoreManager::getBindingForStruct(Store store, const TypedValueRegion* R) { - QualType T = R->getValueType(); - assert(T->isStructureOrClassType()); + assert(R->getValueType()->isStructureOrClassType()); return svalBuilder.makeLazyCompoundVal(StoreRef(store, *this), R); } -SVal RegionStoreManager::RetrieveArray(Store store, +SVal RegionStoreManager::getBindingForArray(Store store, const TypedValueRegion * R) { assert(Ctx.getAsConstantArrayType(R->getValueType())); return svalBuilder.makeLazyCompoundVal(StoreRef(store, *this), R); @@ -1506,11 +1640,15 @@ StoreRef RegionStoreManager::BindStruct(Store store, const TypedValueRegion* R, RecordDecl::field_iterator FI, FE; StoreRef newStore(store, *this); - for (FI = RD->field_begin(), FE = RD->field_end(); FI != FE; ++FI, ++VI) { + for (FI = RD->field_begin(), FE = RD->field_end(); FI != FE; ++FI) { if (VI == VE) break; + // Skip any unnamed bitfields to stay in sync with the initializers. + if ((*FI)->isUnnamedBitfield()) + continue; + QualType FTy = (*FI)->getType(); const FieldRegion* FR = MRMgr.getFieldRegion(*FI, R); @@ -1520,6 +1658,7 @@ StoreRef RegionStoreManager::BindStruct(Store store, const TypedValueRegion* R, newStore = BindStruct(newStore.getStore(), FR, *VI); else newStore = Bind(newStore.getStore(), svalBuilder.makeLoc(FR), *VI); + ++VI; } // There may be fewer values in the initialize list than the fields of struct. @@ -1556,7 +1695,7 @@ StoreRef RegionStoreManager::KillStruct(Store store, const TypedRegion* R, // Remove the old bindings, using 'subReg' as the root of all regions // we will invalidate. RegionBindings B = GetRegionBindings(store); - llvm::OwningPtr<RegionStoreSubRegionMap> + OwningPtr<RegionStoreSubRegionMap> SubRegions(getRegionStoreSubRegionMap(store)); RemoveSubRegionBindings(B, subReg, *SubRegions); @@ -1574,7 +1713,7 @@ StoreRef RegionStoreManager::CopyLazyBindings(nonloc::LazyCompoundVal V, // Nuke the old bindings stemming from R. RegionBindings B = GetRegionBindings(store); - llvm::OwningPtr<RegionStoreSubRegionMap> + OwningPtr<RegionStoreSubRegionMap> SubRegions(getRegionStoreSubRegionMap(store)); // B and DVM are updated after the call to RemoveSubRegionBindings. @@ -1641,7 +1780,8 @@ class removeDeadBindingsWorker : const StackFrameContext *CurrentLCtx; public: - removeDeadBindingsWorker(RegionStoreManager &rm, ProgramStateManager &stateMgr, + removeDeadBindingsWorker(RegionStoreManager &rm, + ProgramStateManager &stateMgr, RegionBindings b, SymbolReaper &symReaper, const StackFrameContext *LCtx) : ClusterAnalysis<removeDeadBindingsWorker>(rm, stateMgr, b, @@ -1717,9 +1857,9 @@ void removeDeadBindingsWorker::VisitBinding(SVal V) { if (const MemRegion *R = V.getAsRegion()) AddToWorkList(R); - // Update the set of live symbols. - for (SVal::symbol_iterator SI=V.symbol_begin(), SE=V.symbol_end(); - SI!=SE;++SI) + // Update the set of live symbols. + for (SymExpr::symbol_iterator SI = V.symbol_begin(), SE = V.symbol_end(); + SI!=SE; ++SI) SymReaper.markLive(*SI); } @@ -1807,7 +1947,7 @@ StoreRef RegionStoreManager::removeDeadBindings(Store store, SymReaper.maybeDead(SymR->getSymbol()); SVal X = I.getData(); - SVal::symbol_iterator SI = X.symbol_begin(), SE = X.symbol_end(); + SymExpr::symbol_iterator SI = X.symbol_begin(), SE = X.symbol_end(); for (; SI != SE; ++SI) SymReaper.maybeDead(*SI); } @@ -1816,37 +1956,41 @@ StoreRef RegionStoreManager::removeDeadBindings(Store store, } -StoreRef RegionStoreManager::enterStackFrame(const ProgramState *state, - const StackFrameContext *frame) { - FunctionDecl const *FD = cast<FunctionDecl>(frame->getDecl()); +StoreRef RegionStoreManager::enterStackFrame(ProgramStateRef state, + const LocationContext *callerCtx, + const StackFrameContext *calleeCtx) +{ + FunctionDecl const *FD = cast<FunctionDecl>(calleeCtx->getDecl()); FunctionDecl::param_const_iterator PI = FD->param_begin(), PE = FD->param_end(); StoreRef store = StoreRef(state->getStore(), *this); - if (CallExpr const *CE = dyn_cast<CallExpr>(frame->getCallSite())) { + if (CallExpr const *CE = dyn_cast<CallExpr>(calleeCtx->getCallSite())) { CallExpr::const_arg_iterator AI = CE->arg_begin(), AE = CE->arg_end(); // Copy the arg expression value to the arg variables. We check that // PI != PE because the actual number of arguments may be different than // the function declaration. for (; AI != AE && PI != PE; ++AI, ++PI) { - SVal ArgVal = state->getSVal(*AI); + SVal ArgVal = state->getSVal(*AI, callerCtx); store = Bind(store.getStore(), - svalBuilder.makeLoc(MRMgr.getVarRegion(*PI, frame)), ArgVal); + svalBuilder.makeLoc(MRMgr.getVarRegion(*PI, calleeCtx)), + ArgVal); } } else if (const CXXConstructExpr *CE = - dyn_cast<CXXConstructExpr>(frame->getCallSite())) { + dyn_cast<CXXConstructExpr>(calleeCtx->getCallSite())) { CXXConstructExpr::const_arg_iterator AI = CE->arg_begin(), AE = CE->arg_end(); // Copy the arg expression value to the arg variables. for (; AI != AE; ++AI, ++PI) { - SVal ArgVal = state->getSVal(*AI); + SVal ArgVal = state->getSVal(*AI, callerCtx); store = Bind(store.getStore(), - svalBuilder.makeLoc(MRMgr.getVarRegion(*PI,frame)), ArgVal); + svalBuilder.makeLoc(MRMgr.getVarRegion(*PI, calleeCtx)), + ArgVal); } } else - assert(isa<CXXDestructorDecl>(frame->getDecl())); + assert(isa<CXXDestructorDecl>(calleeCtx->getDecl())); return store; } diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp index ebf7ae2..9e97f5e 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp @@ -12,6 +12,7 @@ // //===----------------------------------------------------------------------===// +#include "clang/AST/ExprCXX.h" #include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h" @@ -25,6 +26,8 @@ using namespace ento; // Basic SVal creation. //===----------------------------------------------------------------------===// +void SValBuilder::anchor() { } + DefinedOrUnknownSVal SValBuilder::makeZeroVal(QualType type) { if (Loc::isLocType(type)) return makeNull(); @@ -37,23 +40,38 @@ DefinedOrUnknownSVal SValBuilder::makeZeroVal(QualType type) { return UnknownVal(); } - NonLoc SValBuilder::makeNonLoc(const SymExpr *lhs, BinaryOperator::Opcode op, const llvm::APSInt& rhs, QualType type) { // The Environment ensures we always get a persistent APSInt in // BasicValueFactory, so we don't need to get the APSInt from // BasicValueFactory again. + assert(lhs); + assert(!Loc::isLocType(type)); + return nonloc::SymbolVal(SymMgr.getSymIntExpr(lhs, op, rhs, type)); +} + +NonLoc SValBuilder::makeNonLoc(const llvm::APSInt& lhs, + BinaryOperator::Opcode op, const SymExpr *rhs, + QualType type) { + assert(rhs); assert(!Loc::isLocType(type)); - return nonloc::SymExprVal(SymMgr.getSymIntExpr(lhs, op, rhs, type)); + return nonloc::SymbolVal(SymMgr.getIntSymExpr(lhs, op, rhs, type)); } NonLoc SValBuilder::makeNonLoc(const SymExpr *lhs, BinaryOperator::Opcode op, const SymExpr *rhs, QualType type) { - assert(SymMgr.getType(lhs) == SymMgr.getType(rhs)); + assert(lhs && rhs); + assert(haveSameType(lhs->getType(Context), rhs->getType(Context)) == true); assert(!Loc::isLocType(type)); - return nonloc::SymExprVal(SymMgr.getSymSymExpr(lhs, op, rhs, type)); + return nonloc::SymbolVal(SymMgr.getSymSymExpr(lhs, op, rhs, type)); } +NonLoc SValBuilder::makeNonLoc(const SymExpr *operand, + QualType fromTy, QualType toTy) { + assert(operand); + assert(!Loc::isLocType(toTy)); + return nonloc::SymbolVal(SymMgr.getCastSymbol(operand, fromTy, toTy)); +} SVal SValBuilder::convertToArrayIndex(SVal val) { if (val.isUnknownOrUndef()) @@ -69,6 +87,10 @@ SVal SValBuilder::convertToArrayIndex(SVal val) { return evalCastFromNonLoc(cast<NonLoc>(val), ArrayIndexTy); } +nonloc::ConcreteInt SValBuilder::makeBoolVal(const CXXBoolLiteralExpr *boolean){ + return makeTruthVal(boolean->getValue()); +} + DefinedOrUnknownSVal SValBuilder::getRegionValueSymbolVal(const TypedValueRegion* region) { QualType T = region->getValueType(); @@ -84,35 +106,46 @@ SValBuilder::getRegionValueSymbolVal(const TypedValueRegion* region) { return nonloc::SymbolVal(sym); } -DefinedOrUnknownSVal SValBuilder::getConjuredSymbolVal(const void *symbolTag, - const Expr *expr, - unsigned count) { +DefinedOrUnknownSVal +SValBuilder::getConjuredSymbolVal(const void *symbolTag, + const Expr *expr, + const LocationContext *LCtx, + unsigned count) { QualType T = expr->getType(); + return getConjuredSymbolVal(symbolTag, expr, LCtx, T, count); +} - if (!SymbolManager::canSymbolicate(T)) +DefinedOrUnknownSVal +SValBuilder::getConjuredSymbolVal(const void *symbolTag, + const Expr *expr, + const LocationContext *LCtx, + QualType type, + unsigned count) { + if (!SymbolManager::canSymbolicate(type)) return UnknownVal(); - SymbolRef sym = SymMgr.getConjuredSymbol(expr, count, symbolTag); + SymbolRef sym = SymMgr.getConjuredSymbol(expr, LCtx, type, count, symbolTag); - if (Loc::isLocType(T)) + if (Loc::isLocType(type)) return loc::MemRegionVal(MemMgr.getSymbolicRegion(sym)); return nonloc::SymbolVal(sym); } -DefinedOrUnknownSVal SValBuilder::getConjuredSymbolVal(const void *symbolTag, - const Expr *expr, - QualType type, - unsigned count) { - + +DefinedOrUnknownSVal +SValBuilder::getConjuredSymbolVal(const Stmt *stmt, + const LocationContext *LCtx, + QualType type, + unsigned visitCount) { if (!SymbolManager::canSymbolicate(type)) return UnknownVal(); - SymbolRef sym = SymMgr.getConjuredSymbol(expr, type, count, symbolTag); - + SymbolRef sym = SymMgr.getConjuredSymbol(stmt, LCtx, type, visitCount); + if (Loc::isLocType(type)) return loc::MemRegionVal(MemMgr.getSymbolicRegion(sym)); - + return nonloc::SymbolVal(sym); } @@ -155,14 +188,41 @@ DefinedSVal SValBuilder::getBlockPointer(const BlockDecl *block, CanQualType locTy, const LocationContext *locContext) { const BlockTextRegion *BC = - MemMgr.getBlockTextRegion(block, locTy, locContext->getAnalysisContext()); + MemMgr.getBlockTextRegion(block, locTy, locContext->getAnalysisDeclContext()); const BlockDataRegion *BD = MemMgr.getBlockDataRegion(BC, locContext); return loc::MemRegionVal(BD); } //===----------------------------------------------------------------------===// -SVal SValBuilder::evalBinOp(const ProgramState *state, BinaryOperator::Opcode op, +SVal SValBuilder::makeGenericVal(ProgramStateRef State, + BinaryOperator::Opcode Op, + NonLoc LHS, NonLoc RHS, + QualType ResultTy) { + // If operands are tainted, create a symbol to ensure that we propagate taint. + if (State->isTainted(RHS) || State->isTainted(LHS)) { + const SymExpr *symLHS; + const SymExpr *symRHS; + + if (const nonloc::ConcreteInt *rInt = dyn_cast<nonloc::ConcreteInt>(&RHS)) { + symLHS = LHS.getAsSymExpr(); + return makeNonLoc(symLHS, Op, rInt->getValue(), ResultTy); + } + + if (const nonloc::ConcreteInt *lInt = dyn_cast<nonloc::ConcreteInt>(&LHS)) { + symRHS = RHS.getAsSymExpr(); + return makeNonLoc(lInt->getValue(), Op, symRHS, ResultTy); + } + + symLHS = LHS.getAsSymExpr(); + symRHS = RHS.getAsSymExpr(); + return makeNonLoc(symLHS, Op, symRHS, ResultTy); + } + return UnknownVal(); +} + + +SVal SValBuilder::evalBinOp(ProgramStateRef state, BinaryOperator::Opcode op, SVal lhs, SVal rhs, QualType type) { if (lhs.isUndef() || rhs.isUndef()) @@ -190,37 +250,50 @@ SVal SValBuilder::evalBinOp(const ProgramState *state, BinaryOperator::Opcode op return evalBinOpNN(state, op, cast<NonLoc>(lhs), cast<NonLoc>(rhs), type); } -DefinedOrUnknownSVal SValBuilder::evalEQ(const ProgramState *state, +DefinedOrUnknownSVal SValBuilder::evalEQ(ProgramStateRef state, DefinedOrUnknownSVal lhs, DefinedOrUnknownSVal rhs) { return cast<DefinedOrUnknownSVal>(evalBinOp(state, BO_EQ, lhs, rhs, Context.IntTy)); } +/// Recursively check if the pointer types are equal modulo const, volatile, +/// and restrict qualifiers. Assumes the input types are canonical. +/// TODO: This is based off of code in SemaCast; can we reuse it. +static bool haveSimilarTypes(ASTContext &Context, QualType T1, + QualType T2) { + while (Context.UnwrapSimilarPointerTypes(T1, T2)) { + Qualifiers Quals1, Quals2; + T1 = Context.getUnqualifiedArrayType(T1, Quals1); + T2 = Context.getUnqualifiedArrayType(T2, Quals2); + + // Make sure that non cvr-qualifiers the other qualifiers (e.g., address + // spaces) are identical. + Quals1.removeCVRQualifiers(); + Quals2.removeCVRQualifiers(); + if (Quals1 != Quals2) + return false; + } + + if (T1 != T2) + return false; + + return true; +} + // FIXME: should rewrite according to the cast kind. SVal SValBuilder::evalCast(SVal val, QualType castTy, QualType originalTy) { + castTy = Context.getCanonicalType(castTy); + originalTy = Context.getCanonicalType(originalTy); if (val.isUnknownOrUndef() || castTy == originalTy) return val; // For const casts, just propagate the value. if (!castTy->isVariableArrayType() && !originalTy->isVariableArrayType()) - if (Context.hasSameUnqualifiedType(castTy, originalTy)) + if (haveSimilarTypes(Context, Context.getPointerType(castTy), + Context.getPointerType(originalTy))) return val; - - // Check for casts to real or complex numbers. We don't handle these at all - // right now. - if (castTy->isFloatingType() || castTy->isAnyComplexType()) - return UnknownVal(); - // Check for casts from integers to integers. - if (castTy->isIntegerType() && originalTy->isIntegerType()) { - if (isa<Loc>(val)) - // This can be a cast to ObjC property of type int. - return evalCastFromLoc(cast<Loc>(val), castTy); - else - return evalCastFromNonLoc(cast<NonLoc>(val), castTy); - } - // Check for casts from pointers to integers. if (castTy->isIntegerType() && Loc::isLocType(originalTy)) return evalCastFromLoc(cast<Loc>(val), castTy); @@ -235,7 +308,7 @@ SVal SValBuilder::evalCast(SVal val, QualType castTy, QualType originalTy) { } return LV->getLoc(); } - goto DispatchCast; + return dispatchCast(val, castTy); } // Just pass through function and block pointers. @@ -309,8 +382,5 @@ SVal SValBuilder::evalCast(SVal val, QualType castTy, QualType originalTy) { return R ? SVal(loc::MemRegionVal(R)) : UnknownVal(); } -DispatchCast: - // All other cases. - return isa<Loc>(val) ? evalCastFromLoc(cast<Loc>(val), castTy) - : evalCastFromNonLoc(cast<NonLoc>(val), castTy); + return dispatchCast(val, castTy); } diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SVals.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SVals.cpp index b5980b9..b94aff4 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SVals.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SVals.cpp @@ -54,13 +54,16 @@ const FunctionDecl *SVal::getAsFunctionDecl() const { return CTR->getDecl(); } - return NULL; + return 0; } -/// getAsLocSymbol - If this SVal is a location (subclasses Loc) and -/// wraps a symbol, return that SymbolRef. Otherwise return 0. -// FIXME: should we consider SymbolRef wrapped in CodeTextRegion? +/// \brief If this SVal is a location (subclasses Loc) and wraps a symbol, +/// return that SymbolRef. Otherwise return 0. +/// +/// Implicit casts (ex: void* -> char*) can turn Symbolic region into Element +/// region. If that is the case, gets the underlining region. SymbolRef SVal::getAsLocSymbol() const { + // FIXME: should we consider SymbolRef wrapped in CodeTextRegion? if (const nonloc::LocAsInteger *X = dyn_cast<nonloc::LocAsInteger>(this)) return X->getLoc().getAsLocSymbol(); @@ -69,7 +72,7 @@ SymbolRef SVal::getAsLocSymbol() const { if (const SymbolicRegion *SymR = dyn_cast<SymbolicRegion>(R)) return SymR->getSymbol(); } - return NULL; + return 0; } /// Get the symbol in the SVal or its base region. @@ -91,29 +94,34 @@ SymbolRef SVal::getLocSymbolInBase() const { return 0; } -/// getAsSymbol - If this Sval wraps a symbol return that SymbolRef. +// TODO: The next 3 functions have to be simplified. + +/// \brief If this SVal wraps a symbol return that SymbolRef. /// Otherwise return 0. -// FIXME: should we consider SymbolRef wrapped in CodeTextRegion? SymbolRef SVal::getAsSymbol() const { + // FIXME: should we consider SymbolRef wrapped in CodeTextRegion? if (const nonloc::SymbolVal *X = dyn_cast<nonloc::SymbolVal>(this)) return X->getSymbol(); - if (const nonloc::SymExprVal *X = dyn_cast<nonloc::SymExprVal>(this)) - if (SymbolRef Y = dyn_cast<SymbolData>(X->getSymbolicExpression())) - return Y; - return getAsLocSymbol(); } /// getAsSymbolicExpression - If this Sval wraps a symbolic expression then /// return that expression. Otherwise return NULL. const SymExpr *SVal::getAsSymbolicExpression() const { - if (const nonloc::SymExprVal *X = dyn_cast<nonloc::SymExprVal>(this)) - return X->getSymbolicExpression(); + if (const nonloc::SymbolVal *X = dyn_cast<nonloc::SymbolVal>(this)) + return X->getSymbol(); return getAsSymbol(); } +const SymExpr* SVal::getAsSymExpr() const { + const SymExpr* Sym = getAsSymbol(); + if (!Sym) + Sym = getAsSymbolicExpression(); + return Sym; +} + const MemRegion *SVal::getAsRegion() const { if (const loc::MemRegionVal *X = dyn_cast<loc::MemRegionVal>(this)) return X->getRegion(); @@ -130,50 +138,6 @@ const MemRegion *loc::MemRegionVal::stripCasts() const { return R ? R->StripCasts() : NULL; } -bool SVal::symbol_iterator::operator==(const symbol_iterator &X) const { - return itr == X.itr; -} - -bool SVal::symbol_iterator::operator!=(const symbol_iterator &X) const { - return itr != X.itr; -} - -SVal::symbol_iterator::symbol_iterator(const SymExpr *SE) { - itr.push_back(SE); - while (!isa<SymbolData>(itr.back())) expand(); -} - -SVal::symbol_iterator &SVal::symbol_iterator::operator++() { - assert(!itr.empty() && "attempting to iterate on an 'end' iterator"); - assert(isa<SymbolData>(itr.back())); - itr.pop_back(); - if (!itr.empty()) - while (!isa<SymbolData>(itr.back())) expand(); - return *this; -} - -SymbolRef SVal::symbol_iterator::operator*() { - assert(!itr.empty() && "attempting to dereference an 'end' iterator"); - return cast<SymbolData>(itr.back()); -} - -void SVal::symbol_iterator::expand() { - const SymExpr *SE = itr.back(); - itr.pop_back(); - - if (const SymIntExpr *SIE = dyn_cast<SymIntExpr>(SE)) { - itr.push_back(SIE->getLHS()); - return; - } - else if (const SymSymExpr *SSE = dyn_cast<SymSymExpr>(SE)) { - itr.push_back(SSE->getLHS()); - itr.push_back(SSE->getRHS()); - return; - } - - llvm_unreachable("unhandled expansion case"); -} - const void *nonloc::LazyCompoundVal::getStore() const { return static_cast<const LazyCompoundValData*>(Data)->getStore(); } @@ -281,8 +245,6 @@ void SVal::dumpToStream(raw_ostream &os) const { case UndefinedKind: os << "Undefined"; break; - default: - assert (false && "Invalid SVal."); } } @@ -298,13 +260,8 @@ void NonLoc::dumpToStream(raw_ostream &os) const { << C.getValue().getBitWidth() << 'b'; break; } - case nonloc::SymbolValKind: - os << '$' << cast<nonloc::SymbolVal>(this)->getSymbol(); - break; - case nonloc::SymExprValKind: { - const nonloc::SymExprVal& C = *cast<nonloc::SymExprVal>(this); - const SymExpr *SE = C.getSymbolicExpression(); - os << SE; + case nonloc::SymbolValKind: { + os << cast<nonloc::SymbolVal>(this)->getSymbol(); break; } case nonloc::LocAsIntegerKind: { diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp index 79d8b8b..a76a2da 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp @@ -23,11 +23,9 @@ namespace ento { SimpleConstraintManager::~SimpleConstraintManager() {} bool SimpleConstraintManager::canReasonAbout(SVal X) const { - if (nonloc::SymExprVal *SymVal = dyn_cast<nonloc::SymExprVal>(&X)) { - const SymExpr *SE = SymVal->getSymbolicExpression(); - - if (isa<SymbolData>(SE)) - return true; + nonloc::SymbolVal *SymVal = dyn_cast<nonloc::SymbolVal>(&X); + if (SymVal && SymVal->isExpression()) { + const SymExpr *SE = SymVal->getSymbol(); if (const SymIntExpr *SIE = dyn_cast<SymIntExpr>(SE)) { switch (SIE->getOpcode()) { @@ -56,7 +54,7 @@ bool SimpleConstraintManager::canReasonAbout(SVal X) const { return true; } -const ProgramState *SimpleConstraintManager::assume(const ProgramState *state, +ProgramStateRef SimpleConstraintManager::assume(ProgramStateRef state, DefinedSVal Cond, bool Assumption) { if (isa<NonLoc>(Cond)) @@ -65,13 +63,13 @@ const ProgramState *SimpleConstraintManager::assume(const ProgramState *state, return assume(state, cast<Loc>(Cond), Assumption); } -const ProgramState *SimpleConstraintManager::assume(const ProgramState *state, Loc cond, +ProgramStateRef SimpleConstraintManager::assume(ProgramStateRef state, Loc cond, bool assumption) { state = assumeAux(state, cond, assumption); return SU.processAssume(state, cond, assumption); } -const ProgramState *SimpleConstraintManager::assumeAux(const ProgramState *state, +ProgramStateRef SimpleConstraintManager::assumeAux(ProgramStateRef state, Loc Cond, bool Assumption) { BasicValueFactory &BasicVals = state->getBasicVals(); @@ -113,7 +111,7 @@ const ProgramState *SimpleConstraintManager::assumeAux(const ProgramState *state } // end switch } -const ProgramState *SimpleConstraintManager::assume(const ProgramState *state, +ProgramStateRef SimpleConstraintManager::assume(ProgramStateRef state, NonLoc cond, bool assumption) { state = assumeAux(state, cond, assumption); @@ -135,16 +133,29 @@ static BinaryOperator::Opcode NegateComparison(BinaryOperator::Opcode op) { } } -const ProgramState *SimpleConstraintManager::assumeAux(const ProgramState *state, + +ProgramStateRef SimpleConstraintManager::assumeAuxForSymbol( + ProgramStateRef State, + SymbolRef Sym, + bool Assumption) { + QualType T = State->getSymbolManager().getType(Sym); + const llvm::APSInt &zero = State->getBasicVals().getValue(0, T); + if (Assumption) + return assumeSymNE(State, Sym, zero, zero); + else + return assumeSymEQ(State, Sym, zero, zero); +} + +ProgramStateRef SimpleConstraintManager::assumeAux(ProgramStateRef state, NonLoc Cond, bool Assumption) { - // We cannot reason about SymSymExprs, - // and can only reason about some SymIntExprs. + // We cannot reason about SymSymExprs, and can only reason about some + // SymIntExprs. if (!canReasonAbout(Cond)) { - // Just return the current state indicating that the path is feasible. - // This may be an over-approximation of what is possible. - return state; + // Just add the constraint to the expression without trying to simplify. + SymbolRef sym = Cond.getAsSymExpr(); + return assumeAuxForSymbol(state, sym, Assumption); } BasicValueFactory &BasicVals = state->getBasicVals(); @@ -157,37 +168,33 @@ const ProgramState *SimpleConstraintManager::assumeAux(const ProgramState *state case nonloc::SymbolValKind: { nonloc::SymbolVal& SV = cast<nonloc::SymbolVal>(Cond); SymbolRef sym = SV.getSymbol(); - QualType T = SymMgr.getType(sym); - const llvm::APSInt &zero = BasicVals.getValue(0, T); - if (Assumption) - return assumeSymNE(state, sym, zero, zero); - else - return assumeSymEQ(state, sym, zero, zero); - } + assert(sym); + + // Handle SymbolData. + if (!SV.isExpression()) { + return assumeAuxForSymbol(state, sym, Assumption); + + // Handle symbolic expression. + } else { + // We can only simplify expressions whose RHS is an integer. + const SymIntExpr *SE = dyn_cast<SymIntExpr>(sym); + if (!SE) + return assumeAuxForSymbol(state, sym, Assumption); + + BinaryOperator::Opcode op = SE->getOpcode(); + // Implicitly compare non-comparison expressions to 0. + if (!BinaryOperator::isComparisonOp(op)) { + QualType T = SymMgr.getType(SE); + const llvm::APSInt &zero = BasicVals.getValue(0, T); + op = (Assumption ? BO_NE : BO_EQ); + return assumeSymRel(state, SE, op, zero); + } + // From here on out, op is the real comparison we'll be testing. + if (!Assumption) + op = NegateComparison(op); - case nonloc::SymExprValKind: { - nonloc::SymExprVal V = cast<nonloc::SymExprVal>(Cond); - - // For now, we only handle expressions whose RHS is an integer. - // All other expressions are assumed to be feasible. - const SymIntExpr *SE = dyn_cast<SymIntExpr>(V.getSymbolicExpression()); - if (!SE) - return state; - - BinaryOperator::Opcode op = SE->getOpcode(); - // Implicitly compare non-comparison expressions to 0. - if (!BinaryOperator::isComparisonOp(op)) { - QualType T = SymMgr.getType(SE); - const llvm::APSInt &zero = BasicVals.getValue(0, T); - op = (Assumption ? BO_NE : BO_EQ); - return assumeSymRel(state, SE, op, zero); + return assumeSymRel(state, SE->getLHS(), op, SE->getRHS()); } - - // From here on out, op is the real comparison we'll be testing. - if (!Assumption) - op = NegateComparison(op); - - return assumeSymRel(state, SE->getLHS(), op, SE->getRHS()); } case nonloc::ConcreteIntKind: { @@ -202,55 +209,52 @@ const ProgramState *SimpleConstraintManager::assumeAux(const ProgramState *state } // end switch } -const ProgramState *SimpleConstraintManager::assumeSymRel(const ProgramState *state, +static llvm::APSInt computeAdjustment(const SymExpr *LHS, + SymbolRef &Sym) { + llvm::APSInt DefaultAdjustment; + DefaultAdjustment = 0; + + // First check if the LHS is a simple symbol reference. + if (isa<SymbolData>(LHS)) + return DefaultAdjustment; + + // Next, see if it's a "($sym+constant1)" expression. + const SymIntExpr *SE = dyn_cast<SymIntExpr>(LHS); + + // We cannot simplify "($sym1+$sym2)". + if (!SE) + return DefaultAdjustment; + + // Get the constant out of the expression "($sym+constant1)" or + // "<expr>+constant1". + Sym = SE->getLHS(); + switch (SE->getOpcode()) { + case BO_Add: + return SE->getRHS(); + case BO_Sub: + return -SE->getRHS(); + default: + // We cannot simplify non-additive operators. + return DefaultAdjustment; + } +} + +ProgramStateRef SimpleConstraintManager::assumeSymRel(ProgramStateRef state, const SymExpr *LHS, BinaryOperator::Opcode op, const llvm::APSInt& Int) { assert(BinaryOperator::isComparisonOp(op) && "Non-comparison ops should be rewritten as comparisons to zero."); - // We only handle simple comparisons of the form "$sym == constant" - // or "($sym+constant1) == constant2". - // The adjustment is "constant1" in the above expression. It's used to - // "slide" the solution range around for modular arithmetic. For example, - // x < 4 has the solution [0, 3]. x+2 < 4 has the solution [0-2, 3-2], which - // in modular arithmetic is [0, 1] U [UINT_MAX-1, UINT_MAX]. It's up to - // the subclasses of SimpleConstraintManager to handle the adjustment. - llvm::APSInt Adjustment; - - // First check if the LHS is a simple symbol reference. - SymbolRef Sym = dyn_cast<SymbolData>(LHS); - if (Sym) { - Adjustment = 0; - } else { - // Next, see if it's a "($sym+constant1)" expression. - const SymIntExpr *SE = dyn_cast<SymIntExpr>(LHS); - - // We don't handle "($sym1+$sym2)". - // Give up and assume the constraint is feasible. - if (!SE) - return state; - - // We don't handle "(<expr>+constant1)". - // Give up and assume the constraint is feasible. - Sym = dyn_cast<SymbolData>(SE->getLHS()); - if (!Sym) - return state; - - // Get the constant out of the expression "($sym+constant1)". - switch (SE->getOpcode()) { - case BO_Add: - Adjustment = SE->getRHS(); - break; - case BO_Sub: - Adjustment = -SE->getRHS(); - break; - default: - // We don't handle non-additive operators. - // Give up and assume the constraint is feasible. - return state; - } - } + // We only handle simple comparisons of the form "$sym == constant" + // or "($sym+constant1) == constant2". + // The adjustment is "constant1" in the above expression. It's used to + // "slide" the solution range around for modular arithmetic. For example, + // x < 4 has the solution [0, 3]. x+2 < 4 has the solution [0-2, 3-2], which + // in modular arithmetic is [0, 1] U [UINT_MAX-1, UINT_MAX]. It's up to + // the subclasses of SimpleConstraintManager to handle the adjustment. + SymbolRef Sym = LHS; + llvm::APSInt Adjustment = computeAdjustment(LHS, Sym); // FIXME: This next section is a hack. It silently converts the integers to // be of the same type as the symbol, which is not always correct. Really the diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SimpleConstraintManager.h b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SimpleConstraintManager.h index d4295d4..e082d9d 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SimpleConstraintManager.h +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SimpleConstraintManager.h @@ -31,16 +31,14 @@ public: // Common implementation for the interface provided by ConstraintManager. //===------------------------------------------------------------------===// - bool canReasonAbout(SVal X) const; - - const ProgramState *assume(const ProgramState *state, DefinedSVal Cond, + ProgramStateRef assume(ProgramStateRef state, DefinedSVal Cond, bool Assumption); - const ProgramState *assume(const ProgramState *state, Loc Cond, bool Assumption); + ProgramStateRef assume(ProgramStateRef state, Loc Cond, bool Assumption); - const ProgramState *assume(const ProgramState *state, NonLoc Cond, bool Assumption); + ProgramStateRef assume(ProgramStateRef state, NonLoc Cond, bool Assumption); - const ProgramState *assumeSymRel(const ProgramState *state, + ProgramStateRef assumeSymRel(ProgramStateRef state, const SymExpr *LHS, BinaryOperator::Opcode op, const llvm::APSInt& Int); @@ -53,27 +51,27 @@ protected: // Each of these is of the form "$sym+Adj <> V", where "<>" is the comparison // operation for the method being invoked. - virtual const ProgramState *assumeSymNE(const ProgramState *state, SymbolRef sym, + virtual ProgramStateRef assumeSymNE(ProgramStateRef state, SymbolRef sym, const llvm::APSInt& V, const llvm::APSInt& Adjustment) = 0; - virtual const ProgramState *assumeSymEQ(const ProgramState *state, SymbolRef sym, + virtual ProgramStateRef assumeSymEQ(ProgramStateRef state, SymbolRef sym, const llvm::APSInt& V, const llvm::APSInt& Adjustment) = 0; - virtual const ProgramState *assumeSymLT(const ProgramState *state, SymbolRef sym, + virtual ProgramStateRef assumeSymLT(ProgramStateRef state, SymbolRef sym, const llvm::APSInt& V, const llvm::APSInt& Adjustment) = 0; - virtual const ProgramState *assumeSymGT(const ProgramState *state, SymbolRef sym, + virtual ProgramStateRef assumeSymGT(ProgramStateRef state, SymbolRef sym, const llvm::APSInt& V, const llvm::APSInt& Adjustment) = 0; - virtual const ProgramState *assumeSymLE(const ProgramState *state, SymbolRef sym, + virtual ProgramStateRef assumeSymLE(ProgramStateRef state, SymbolRef sym, const llvm::APSInt& V, const llvm::APSInt& Adjustment) = 0; - virtual const ProgramState *assumeSymGE(const ProgramState *state, SymbolRef sym, + virtual ProgramStateRef assumeSymGE(ProgramStateRef state, SymbolRef sym, const llvm::APSInt& V, const llvm::APSInt& Adjustment) = 0; @@ -81,9 +79,19 @@ protected: // Internal implementation. //===------------------------------------------------------------------===// - const ProgramState *assumeAux(const ProgramState *state, Loc Cond,bool Assumption); + bool canReasonAbout(SVal X) const; + + ProgramStateRef assumeAux(ProgramStateRef state, + Loc Cond, + bool Assumption); + + ProgramStateRef assumeAux(ProgramStateRef state, + NonLoc Cond, + bool Assumption); - const ProgramState *assumeAux(const ProgramState *state, NonLoc Cond, bool Assumption); + ProgramStateRef assumeAuxForSymbol(ProgramStateRef State, + SymbolRef Sym, + bool Assumption); }; } // end GR namespace diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp index bd63ecf..d0558f1 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp @@ -20,6 +20,7 @@ using namespace ento; namespace { class SimpleSValBuilder : public SValBuilder { protected: + virtual SVal dispatchCast(SVal val, QualType castTy); virtual SVal evalCastFromNonLoc(NonLoc val, QualType castTy); virtual SVal evalCastFromLoc(Loc val, QualType castTy); @@ -31,16 +32,16 @@ public: virtual SVal evalMinus(NonLoc val); virtual SVal evalComplement(NonLoc val); - virtual SVal evalBinOpNN(const ProgramState *state, BinaryOperator::Opcode op, + virtual SVal evalBinOpNN(ProgramStateRef state, BinaryOperator::Opcode op, NonLoc lhs, NonLoc rhs, QualType resultTy); - virtual SVal evalBinOpLL(const ProgramState *state, BinaryOperator::Opcode op, + virtual SVal evalBinOpLL(ProgramStateRef state, BinaryOperator::Opcode op, Loc lhs, Loc rhs, QualType resultTy); - virtual SVal evalBinOpLN(const ProgramState *state, BinaryOperator::Opcode op, + virtual SVal evalBinOpLN(ProgramStateRef state, BinaryOperator::Opcode op, Loc lhs, NonLoc rhs, QualType resultTy); /// getKnownValue - evaluates a given SVal. If the SVal has only one possible /// (integer) value, that value is returned. Otherwise, returns NULL. - virtual const llvm::APSInt *getKnownValue(const ProgramState *state, SVal V); + virtual const llvm::APSInt *getKnownValue(ProgramStateRef state, SVal V); SVal MakeSymIntVal(const SymExpr *LHS, BinaryOperator::Opcode op, const llvm::APSInt &RHS, QualType resultTy); @@ -57,6 +58,12 @@ SValBuilder *ento::createSimpleSValBuilder(llvm::BumpPtrAllocator &alloc, // Transfer function for Casts. //===----------------------------------------------------------------------===// +SVal SimpleSValBuilder::dispatchCast(SVal Val, QualType CastTy) { + assert(isa<Loc>(&Val) || isa<NonLoc>(&Val)); + return isa<Loc>(Val) ? evalCastFromLoc(cast<Loc>(Val), CastTy) + : evalCastFromNonLoc(cast<NonLoc>(Val), CastTy); +} + SVal SimpleSValBuilder::evalCastFromNonLoc(NonLoc val, QualType castTy) { bool isLocType = Loc::isLocType(castTy); @@ -74,25 +81,27 @@ SVal SimpleSValBuilder::evalCastFromNonLoc(NonLoc val, QualType castTy) { if (const SymExpr *se = val.getAsSymbolicExpression()) { QualType T = Context.getCanonicalType(se->getType(Context)); - if (T == Context.getCanonicalType(castTy)) - return val; - + // If types are the same or both are integers, ignore the cast. // FIXME: Remove this hack when we support symbolic truncation/extension. // HACK: If both castTy and T are integers, ignore the cast. This is // not a permanent solution. Eventually we want to precisely handle // extension/truncation of symbolic integers. This prevents us from losing // precision when we assign 'x = y' and 'y' is symbolic and x and y are // different integer types. - if (T->isIntegerType() && castTy->isIntegerType()) + if (haveSameType(T, castTy)) return val; + if (!isLocType) + return makeNonLoc(se, T, castTy); return UnknownVal(); } + // If value is a non integer constant, produce unknown. if (!isa<nonloc::ConcreteInt>(val)) return UnknownVal(); - // Only handle casts from integers to integers. + // Only handle casts from integers to integers - if val is an integer constant + // being cast to a non integer type, produce unknown. if (!isLocType && !castTy->isIntegerType()) return UnknownVal(); @@ -259,18 +268,15 @@ SVal SimpleSValBuilder::MakeSymIntVal(const SymExpr *LHS, // Idempotent ops (like a*1) can still change the type of an expression. // Wrap the LHS up in a NonLoc again and let evalCastFromNonLoc do the // dirty work. - if (isIdempotent) { - if (SymbolRef LHSSym = dyn_cast<SymbolData>(LHS)) - return evalCastFromNonLoc(nonloc::SymbolVal(LHSSym), resultTy); - return evalCastFromNonLoc(nonloc::SymExprVal(LHS), resultTy); - } + if (isIdempotent) + return evalCastFromNonLoc(nonloc::SymbolVal(LHS), resultTy); // If we reach this point, the expression cannot be simplified. - // Make a SymExprVal for the entire thing. + // Make a SymbolVal for the entire expression. return makeNonLoc(LHS, op, RHS, resultTy); } -SVal SimpleSValBuilder::evalBinOpNN(const ProgramState *state, +SVal SimpleSValBuilder::evalBinOpNN(ProgramStateRef state, BinaryOperator::Opcode op, NonLoc lhs, NonLoc rhs, QualType resultTy) { @@ -298,7 +304,7 @@ SVal SimpleSValBuilder::evalBinOpNN(const ProgramState *state, while (1) { switch (lhs.getSubKind()) { default: - return UnknownVal(); + return makeGenericVal(state, op, lhs, rhs, resultTy); case nonloc::LocAsIntegerKind: { Loc lhsL = cast<nonloc::LocAsInteger>(lhs).getLoc(); switch (rhs.getSubKind()) { @@ -321,94 +327,10 @@ SVal SimpleSValBuilder::evalBinOpNN(const ProgramState *state, return makeTruthVal(true, resultTy); default: // This case also handles pointer arithmetic. - return UnknownVal(); + return makeGenericVal(state, op, lhs, rhs, resultTy); } } } - case nonloc::SymExprValKind: { - nonloc::SymExprVal *selhs = cast<nonloc::SymExprVal>(&lhs); - - // Only handle LHS of the form "$sym op constant", at least for now. - const SymIntExpr *symIntExpr = - dyn_cast<SymIntExpr>(selhs->getSymbolicExpression()); - - if (!symIntExpr) - return UnknownVal(); - - // Is this a logical not? (!x is represented as x == 0.) - if (op == BO_EQ && rhs.isZeroConstant()) { - // We know how to negate certain expressions. Simplify them here. - - BinaryOperator::Opcode opc = symIntExpr->getOpcode(); - switch (opc) { - default: - // We don't know how to negate this operation. - // Just handle it as if it were a normal comparison to 0. - break; - case BO_LAnd: - case BO_LOr: - llvm_unreachable("Logical operators handled by branching logic."); - case BO_Assign: - case BO_MulAssign: - case BO_DivAssign: - case BO_RemAssign: - case BO_AddAssign: - case BO_SubAssign: - case BO_ShlAssign: - case BO_ShrAssign: - case BO_AndAssign: - case BO_XorAssign: - case BO_OrAssign: - case BO_Comma: - llvm_unreachable("'=' and ',' operators handled by ExprEngine."); - case BO_PtrMemD: - case BO_PtrMemI: - llvm_unreachable("Pointer arithmetic not handled here."); - case BO_LT: - case BO_GT: - case BO_LE: - case BO_GE: - case BO_EQ: - case BO_NE: - // Negate the comparison and make a value. - opc = NegateComparison(opc); - assert(symIntExpr->getType(Context) == resultTy); - return makeNonLoc(symIntExpr->getLHS(), opc, - symIntExpr->getRHS(), resultTy); - } - } - - // For now, only handle expressions whose RHS is a constant. - const nonloc::ConcreteInt *rhsInt = dyn_cast<nonloc::ConcreteInt>(&rhs); - if (!rhsInt) - return UnknownVal(); - - // If both the LHS and the current expression are additive, - // fold their constants. - if (BinaryOperator::isAdditiveOp(op)) { - BinaryOperator::Opcode lop = symIntExpr->getOpcode(); - if (BinaryOperator::isAdditiveOp(lop)) { - // resultTy may not be the best type to convert to, but it's - // probably the best choice in expressions with mixed type - // (such as x+1U+2LL). The rules for implicit conversions should - // choose a reasonable type to preserve the expression, and will - // at least match how the value is going to be used. - const llvm::APSInt &first = - BasicVals.Convert(resultTy, symIntExpr->getRHS()); - const llvm::APSInt &second = - BasicVals.Convert(resultTy, rhsInt->getValue()); - const llvm::APSInt *newRHS; - if (lop == op) - newRHS = BasicVals.evalAPSInt(BO_Add, first, second); - else - newRHS = BasicVals.evalAPSInt(BO_Sub, first, second); - return MakeSymIntVal(symIntExpr->getLHS(), lop, *newRHS, resultTy); - } - } - - // Otherwise, make a SymExprVal out of the expression. - return MakeSymIntVal(symIntExpr, op, rhsInt->getValue(), resultTy); - } case nonloc::ConcreteIntKind: { const nonloc::ConcreteInt& lhsInt = cast<nonloc::ConcreteInt>(lhs); @@ -467,76 +389,165 @@ SVal SimpleSValBuilder::evalBinOpNN(const ProgramState *state, if (lhsValue == 0) // At this point lhs and rhs have been swapped. return rhs; - return UnknownVal(); + return makeGenericVal(state, op, rhs, lhs, resultTy); default: - return UnknownVal(); + return makeGenericVal(state, op, rhs, lhs, resultTy); } } } case nonloc::SymbolValKind: { - nonloc::SymbolVal *slhs = cast<nonloc::SymbolVal>(&lhs); - SymbolRef Sym = slhs->getSymbol(); - QualType lhsType = Sym->getType(Context); - - // The conversion type is usually the result type, but not in the case - // of relational expressions. - QualType conversionType = resultTy; - if (BinaryOperator::isRelationalOp(op)) - conversionType = lhsType; - - // Does the symbol simplify to a constant? If so, "fold" the constant - // by setting 'lhs' to a ConcreteInt and try again. - if (lhsType->isIntegerType()) - if (const llvm::APSInt *Constant = state->getSymVal(Sym)) { - // The symbol evaluates to a constant. If necessary, promote the - // folded constant (LHS) to the result type. - const llvm::APSInt &lhs_I = BasicVals.Convert(conversionType, - *Constant); - lhs = nonloc::ConcreteInt(lhs_I); - - // Also promote the RHS (if necessary). - - // For shifts, it is not necessary to promote the RHS. - if (BinaryOperator::isShiftOp(op)) - continue; - - // Other operators: do an implicit conversion. This shouldn't be - // necessary once we support truncation/extension of symbolic values. - if (nonloc::ConcreteInt *rhs_I = dyn_cast<nonloc::ConcreteInt>(&rhs)){ - rhs = nonloc::ConcreteInt(BasicVals.Convert(conversionType, - rhs_I->getValue())); + nonloc::SymbolVal *selhs = cast<nonloc::SymbolVal>(&lhs); + + // LHS is a symbolic expression. + if (selhs->isExpression()) { + + // Only handle LHS of the form "$sym op constant", at least for now. + const SymIntExpr *symIntExpr = + dyn_cast<SymIntExpr>(selhs->getSymbol()); + + if (!symIntExpr) + return makeGenericVal(state, op, lhs, rhs, resultTy); + + // Is this a logical not? (!x is represented as x == 0.) + if (op == BO_EQ && rhs.isZeroConstant()) { + // We know how to negate certain expressions. Simplify them here. + + BinaryOperator::Opcode opc = symIntExpr->getOpcode(); + switch (opc) { + default: + // We don't know how to negate this operation. + // Just handle it as if it were a normal comparison to 0. + break; + case BO_LAnd: + case BO_LOr: + llvm_unreachable("Logical operators handled by branching logic."); + case BO_Assign: + case BO_MulAssign: + case BO_DivAssign: + case BO_RemAssign: + case BO_AddAssign: + case BO_SubAssign: + case BO_ShlAssign: + case BO_ShrAssign: + case BO_AndAssign: + case BO_XorAssign: + case BO_OrAssign: + case BO_Comma: + llvm_unreachable("'=' and ',' operators handled by ExprEngine."); + case BO_PtrMemD: + case BO_PtrMemI: + llvm_unreachable("Pointer arithmetic not handled here."); + case BO_LT: + case BO_GT: + case BO_LE: + case BO_GE: + case BO_EQ: + case BO_NE: + // Negate the comparison and make a value. + opc = NegateComparison(opc); + assert(symIntExpr->getType(Context) == resultTy); + return makeNonLoc(symIntExpr->getLHS(), opc, + symIntExpr->getRHS(), resultTy); } - - continue; } - // Is the RHS a symbol we can simplify? - if (const nonloc::SymbolVal *srhs = dyn_cast<nonloc::SymbolVal>(&rhs)) { - SymbolRef RSym = srhs->getSymbol(); - if (RSym->getType(Context)->isIntegerType()) { - if (const llvm::APSInt *Constant = state->getSymVal(RSym)) { - // The symbol evaluates to a constant. - const llvm::APSInt &rhs_I = BasicVals.Convert(conversionType, - *Constant); - rhs = nonloc::ConcreteInt(rhs_I); + // For now, only handle expressions whose RHS is a constant. + const nonloc::ConcreteInt *rhsInt = dyn_cast<nonloc::ConcreteInt>(&rhs); + if (!rhsInt) + return makeGenericVal(state, op, lhs, rhs, resultTy); + + // If both the LHS and the current expression are additive, + // fold their constants. + if (BinaryOperator::isAdditiveOp(op)) { + BinaryOperator::Opcode lop = symIntExpr->getOpcode(); + if (BinaryOperator::isAdditiveOp(lop)) { + // resultTy may not be the best type to convert to, but it's + // probably the best choice in expressions with mixed type + // (such as x+1U+2LL). The rules for implicit conversions should + // choose a reasonable type to preserve the expression, and will + // at least match how the value is going to be used. + const llvm::APSInt &first = + BasicVals.Convert(resultTy, symIntExpr->getRHS()); + const llvm::APSInt &second = + BasicVals.Convert(resultTy, rhsInt->getValue()); + const llvm::APSInt *newRHS; + if (lop == op) + newRHS = BasicVals.evalAPSInt(BO_Add, first, second); + else + newRHS = BasicVals.evalAPSInt(BO_Sub, first, second); + return MakeSymIntVal(symIntExpr->getLHS(), lop, *newRHS, resultTy); } } - } - if (isa<nonloc::ConcreteInt>(rhs)) { - return MakeSymIntVal(slhs->getSymbol(), op, - cast<nonloc::ConcreteInt>(rhs).getValue(), - resultTy); - } + // Otherwise, make a SymbolVal out of the expression. + return MakeSymIntVal(symIntExpr, op, rhsInt->getValue(), resultTy); - return UnknownVal(); + // LHS is a simple symbol (not a symbolic expression). + } else { + nonloc::SymbolVal *slhs = cast<nonloc::SymbolVal>(&lhs); + SymbolRef Sym = slhs->getSymbol(); + QualType lhsType = Sym->getType(Context); + + // The conversion type is usually the result type, but not in the case + // of relational expressions. + QualType conversionType = resultTy; + if (BinaryOperator::isRelationalOp(op)) + conversionType = lhsType; + + // Does the symbol simplify to a constant? If so, "fold" the constant + // by setting 'lhs' to a ConcreteInt and try again. + if (lhsType->isIntegerType()) + if (const llvm::APSInt *Constant = state->getSymVal(Sym)) { + // The symbol evaluates to a constant. If necessary, promote the + // folded constant (LHS) to the result type. + const llvm::APSInt &lhs_I = BasicVals.Convert(conversionType, + *Constant); + lhs = nonloc::ConcreteInt(lhs_I); + + // Also promote the RHS (if necessary). + + // For shifts, it is not necessary to promote the RHS. + if (BinaryOperator::isShiftOp(op)) + continue; + + // Other operators: do an implicit conversion. This shouldn't be + // necessary once we support truncation/extension of symbolic values. + if (nonloc::ConcreteInt *rhs_I = dyn_cast<nonloc::ConcreteInt>(&rhs)){ + rhs = nonloc::ConcreteInt(BasicVals.Convert(conversionType, + rhs_I->getValue())); + } + + continue; + } + + // Is the RHS a symbol we can simplify? + if (const nonloc::SymbolVal *srhs = dyn_cast<nonloc::SymbolVal>(&rhs)) { + SymbolRef RSym = srhs->getSymbol(); + if (RSym->getType(Context)->isIntegerType()) { + if (const llvm::APSInt *Constant = state->getSymVal(RSym)) { + // The symbol evaluates to a constant. + const llvm::APSInt &rhs_I = BasicVals.Convert(conversionType, + *Constant); + rhs = nonloc::ConcreteInt(rhs_I); + } + } + } + + if (isa<nonloc::ConcreteInt>(rhs)) { + return MakeSymIntVal(slhs->getSymbol(), op, + cast<nonloc::ConcreteInt>(rhs).getValue(), + resultTy); + } + + return makeGenericVal(state, op, lhs, rhs, resultTy); + } } } } } // FIXME: all this logic will change if/when we have MemRegion::getLocation(). -SVal SimpleSValBuilder::evalBinOpLL(const ProgramState *state, +SVal SimpleSValBuilder::evalBinOpLL(ProgramStateRef state, BinaryOperator::Opcode op, Loc lhs, Loc rhs, QualType resultTy) { @@ -703,6 +714,24 @@ SVal SimpleSValBuilder::evalBinOpLL(const ProgramState *state, // The two regions are from the same base region. See if they're both a // type of region we know how to compare. + const MemSpaceRegion *LeftMS = LeftBase->getMemorySpace(); + const MemSpaceRegion *RightMS = RightBase->getMemorySpace(); + + // Heuristic: assume that no symbolic region (whose memory space is + // unknown) is on the stack. + // FIXME: we should be able to be more precise once we can do better + // aliasing constraints for symbolic regions, but this is a reasonable, + // albeit unsound, assumption that holds most of the time. + if (isa<StackSpaceRegion>(LeftMS) ^ isa<StackSpaceRegion>(RightMS)) { + switch (op) { + default: + break; + case BO_EQ: + return makeTruthVal(false, resultTy); + case BO_NE: + return makeTruthVal(true, resultTy); + } + } // FIXME: If/when there is a getAsRawOffset() for FieldRegions, this // ElementRegion path and the FieldRegion path below should be unified. @@ -831,7 +860,7 @@ SVal SimpleSValBuilder::evalBinOpLL(const ProgramState *state, } } -SVal SimpleSValBuilder::evalBinOpLN(const ProgramState *state, +SVal SimpleSValBuilder::evalBinOpLN(ProgramStateRef state, BinaryOperator::Opcode op, Loc lhs, NonLoc rhs, QualType resultTy) { @@ -925,7 +954,7 @@ SVal SimpleSValBuilder::evalBinOpLN(const ProgramState *state, return UnknownVal(); } -const llvm::APSInt *SimpleSValBuilder::getKnownValue(const ProgramState *state, +const llvm::APSInt *SimpleSValBuilder::getKnownValue(ProgramStateRef state, SVal V) { if (V.isUnknownOrUndef()) return NULL; diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/Store.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/Store.cpp index 48a6f4f..11748ae 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/Store.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/Store.cpp @@ -14,6 +14,7 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/Store.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" #include "clang/AST/CharUnits.h" +#include "clang/AST/DeclObjC.h" using namespace clang; using namespace ento; @@ -22,8 +23,9 @@ StoreManager::StoreManager(ProgramStateManager &stateMgr) : svalBuilder(stateMgr.getSValBuilder()), StateMgr(stateMgr), MRMgr(svalBuilder.getRegionManager()), Ctx(stateMgr.getContext()) {} -StoreRef StoreManager::enterStackFrame(const ProgramState *state, - const StackFrameContext *frame) { +StoreRef StoreManager::enterStackFrame(ProgramStateRef state, + const LocationContext *callerCtx, + const StackFrameContext *calleeCtx) { return StoreRef(state->getStore(), *this); } @@ -101,8 +103,10 @@ const MemRegion *StoreManager::castRegion(const MemRegion *R, QualType CastToTy) case MemRegion::StackArgumentsSpaceRegionKind: case MemRegion::HeapSpaceRegionKind: case MemRegion::UnknownSpaceRegionKind: - case MemRegion::NonStaticGlobalSpaceRegionKind: - case MemRegion::StaticGlobalSpaceRegionKind: { + case MemRegion::StaticGlobalSpaceRegionKind: + case MemRegion::GlobalInternalSpaceRegionKind: + case MemRegion::GlobalSystemSpaceRegionKind: + case MemRegion::GlobalImmutableSpaceRegionKind: { llvm_unreachable("Invalid region cast"); } @@ -116,6 +120,7 @@ const MemRegion *StoreManager::castRegion(const MemRegion *R, QualType CastToTy) case MemRegion::CompoundLiteralRegionKind: case MemRegion::FieldRegionKind: case MemRegion::ObjCIvarRegionKind: + case MemRegion::ObjCStringRegionKind: case MemRegion::VarRegionKind: case MemRegion::CXXTempObjectRegionKind: case MemRegion::CXXBaseObjectRegionKind: @@ -212,7 +217,7 @@ const MemRegion *StoreManager::castRegion(const MemRegion *R, QualType CastToTy) SVal StoreManager::CastRetrievedVal(SVal V, const TypedValueRegion *R, QualType castTy, bool performTestOnly) { - if (castTy.isNull()) + if (castTy.isNull() || V.isUnknownOrUndef()) return V; ASTContext &Ctx = svalBuilder.getContext(); @@ -227,12 +232,7 @@ SVal StoreManager::CastRetrievedVal(SVal V, const TypedValueRegion *R, return V; } - if (const Loc *L = dyn_cast<Loc>(&V)) - return svalBuilder.evalCastFromLoc(*L, castTy); - else if (const NonLoc *NL = dyn_cast<NonLoc>(&V)) - return svalBuilder.evalCastFromNonLoc(*NL, castTy); - - return V; + return svalBuilder.dispatchCast(V, castTy); } SVal StoreManager::getLValueFieldOrIvar(const Decl *D, SVal Base) { @@ -270,6 +270,10 @@ SVal StoreManager::getLValueFieldOrIvar(const Decl *D, SVal Base) { return loc::MemRegionVal(MRMgr.getFieldRegion(cast<FieldDecl>(D), BaseR)); } +SVal StoreManager::getLValueIvar(const ObjCIvarDecl *decl, SVal base) { + return getLValueFieldOrIvar(decl, base); +} + SVal StoreManager::getLValueElement(QualType elementType, NonLoc Offset, SVal Base) { @@ -336,3 +340,23 @@ SVal StoreManager::getLValueElement(QualType elementType, NonLoc Offset, StoreManager::BindingsHandler::~BindingsHandler() {} +bool StoreManager::FindUniqueBinding::HandleBinding(StoreManager& SMgr, + Store store, + const MemRegion* R, + SVal val) { + SymbolRef SymV = val.getAsLocSymbol(); + if (!SymV || SymV != Sym) + return true; + + if (Binding) { + First = false; + return false; + } + else + Binding = R; + + return true; +} + +void SubRegionMap::anchor() { } +void SubRegionMap::Visitor::anchor() { } diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SubEngine.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SubEngine.cpp new file mode 100644 index 0000000..350f4b8 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SubEngine.cpp @@ -0,0 +1,14 @@ +//== SubEngine.cpp - Interface of the subengine of CoreEngine ------*- 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/PathSensitive/SubEngine.h" + +using namespace clang::ento; + +void SubEngine::anchor() { } diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SymbolManager.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SymbolManager.cpp index b843ab1..adefb58 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SymbolManager.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SymbolManager.cpp @@ -21,6 +21,8 @@ using namespace clang; using namespace ento; +void SymExpr::anchor() { } + void SymExpr::dump() const { dumpToStream(llvm::errs()); } @@ -57,6 +59,15 @@ void SymIntExpr::dumpToStream(raw_ostream &os) const { if (getRHS().isUnsigned()) os << 'U'; } +void IntSymExpr::dumpToStream(raw_ostream &os) const { + os << ' ' << getLHS().getZExtValue(); + if (getLHS().isUnsigned()) os << 'U'; + print(os, getOpcode()); + os << '('; + getRHS()->dumpToStream(os); + os << ") "; +} + void SymSymExpr::dumpToStream(raw_ostream &os) const { os << '('; getLHS()->dumpToStream(os); @@ -66,6 +77,12 @@ void SymSymExpr::dumpToStream(raw_ostream &os) const { os << ')'; } +void SymbolCast::dumpToStream(raw_ostream &os) const { + os << '(' << ToTy.getAsString() << ") ("; + Operand->dumpToStream(os); + os << ')'; +} + void SymbolConjured::dumpToStream(raw_ostream &os) const { os << "conj_$" << getSymbolID() << '{' << T.getAsString() << '}'; } @@ -84,10 +101,69 @@ void SymbolMetadata::dumpToStream(raw_ostream &os) const { << getRegion() << ',' << T.getAsString() << '}'; } +void SymbolData::anchor() { } + void SymbolRegionValue::dumpToStream(raw_ostream &os) const { os << "reg_$" << getSymbolID() << "<" << R << ">"; } +bool SymExpr::symbol_iterator::operator==(const symbol_iterator &X) const { + return itr == X.itr; +} + +bool SymExpr::symbol_iterator::operator!=(const symbol_iterator &X) const { + return itr != X.itr; +} + +SymExpr::symbol_iterator::symbol_iterator(const SymExpr *SE) { + itr.push_back(SE); + while (!isa<SymbolData>(itr.back())) expand(); +} + +SymExpr::symbol_iterator &SymExpr::symbol_iterator::operator++() { + assert(!itr.empty() && "attempting to iterate on an 'end' iterator"); + assert(isa<SymbolData>(itr.back())); + itr.pop_back(); + if (!itr.empty()) + while (!isa<SymbolData>(itr.back())) expand(); + return *this; +} + +SymbolRef SymExpr::symbol_iterator::operator*() { + assert(!itr.empty() && "attempting to dereference an 'end' iterator"); + return cast<SymbolData>(itr.back()); +} + +void SymExpr::symbol_iterator::expand() { + const SymExpr *SE = itr.back(); + itr.pop_back(); + + switch (SE->getKind()) { + case SymExpr::RegionValueKind: + case SymExpr::ConjuredKind: + case SymExpr::DerivedKind: + case SymExpr::ExtentKind: + case SymExpr::MetadataKind: + return; + case SymExpr::CastSymbolKind: + itr.push_back(cast<SymbolCast>(SE)->getOperand()); + return; + case SymExpr::SymIntKind: + itr.push_back(cast<SymIntExpr>(SE)->getLHS()); + return; + case SymExpr::IntSymKind: + itr.push_back(cast<IntSymExpr>(SE)->getRHS()); + return; + case SymExpr::SymSymKind: { + const SymSymExpr *x = cast<SymSymExpr>(SE); + itr.push_back(x->getLHS()); + itr.push_back(x->getRHS()); + return; + } + } + llvm_unreachable("unhandled expansion case"); +} + const SymbolRegionValue* SymbolManager::getRegionValueSymbol(const TypedValueRegion* R) { llvm::FoldingSetNodeID profile; @@ -105,16 +181,17 @@ SymbolManager::getRegionValueSymbol(const TypedValueRegion* R) { } const SymbolConjured* -SymbolManager::getConjuredSymbol(const Stmt *E, QualType T, unsigned Count, +SymbolManager::getConjuredSymbol(const Stmt *E, const LocationContext *LCtx, + QualType T, unsigned Count, const void *SymbolTag) { llvm::FoldingSetNodeID profile; - SymbolConjured::Profile(profile, E, T, Count, SymbolTag); + SymbolConjured::Profile(profile, E, T, Count, LCtx, SymbolTag); void *InsertPos; SymExpr *SD = DataSet.FindNodeOrInsertPos(profile, InsertPos); if (!SD) { SD = (SymExpr*) BPAlloc.Allocate<SymbolConjured>(); - new (SD) SymbolConjured(SymbolCounter, E, T, Count, SymbolTag); + new (SD) SymbolConjured(SymbolCounter, E, LCtx, T, Count, SymbolTag); DataSet.InsertNode(SD, InsertPos); ++SymbolCounter; } @@ -174,6 +251,22 @@ SymbolManager::getMetadataSymbol(const MemRegion* R, const Stmt *S, QualType T, return cast<SymbolMetadata>(SD); } +const SymbolCast* +SymbolManager::getCastSymbol(const SymExpr *Op, + QualType From, QualType To) { + llvm::FoldingSetNodeID ID; + SymbolCast::Profile(ID, Op, From, To); + void *InsertPos; + SymExpr *data = DataSet.FindNodeOrInsertPos(ID, InsertPos); + if (!data) { + data = (SymbolCast*) BPAlloc.Allocate<SymbolCast>(); + new (data) SymbolCast(Op, From, To); + DataSet.InsertNode(data, InsertPos); + } + + return cast<SymbolCast>(data); +} + const SymIntExpr *SymbolManager::getSymIntExpr(const SymExpr *lhs, BinaryOperator::Opcode op, const llvm::APSInt& v, @@ -192,6 +285,24 @@ const SymIntExpr *SymbolManager::getSymIntExpr(const SymExpr *lhs, return cast<SymIntExpr>(data); } +const IntSymExpr *SymbolManager::getIntSymExpr(const llvm::APSInt& lhs, + BinaryOperator::Opcode op, + const SymExpr *rhs, + QualType t) { + llvm::FoldingSetNodeID ID; + IntSymExpr::Profile(ID, lhs, op, rhs, t); + void *InsertPos; + SymExpr *data = DataSet.FindNodeOrInsertPos(ID, InsertPos); + + if (!data) { + data = (IntSymExpr*) BPAlloc.Allocate<IntSymExpr>(); + new (data) IntSymExpr(lhs, op, rhs, t); + DataSet.InsertNode(data, InsertPos); + } + + return cast<IntSymExpr>(data); +} + const SymSymExpr *SymbolManager::getSymSymExpr(const SymExpr *lhs, BinaryOperator::Opcode op, const SymExpr *rhs, @@ -381,7 +492,16 @@ bool SymbolReaper::isLive(SymbolRef sym) { return isa<SymbolRegionValue>(sym); } -bool SymbolReaper::isLive(const Stmt *ExprVal) const { +bool +SymbolReaper::isLive(const Stmt *ExprVal, const LocationContext *ELCtx) const { + if (LCtx != ELCtx) { + // If the reaper's location context is a parent of the expression's + // location context, then the expression value is now "out of scope". + if (LCtx->isParentOf(ELCtx)) + return false; + return true; + } + return LCtx->getAnalysis<RelaxedLiveVariables>()->isLive(Loc, ExprVal); } diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/TextPathDiagnostics.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/TextPathDiagnostics.cpp index 3543f7f..fe912df 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/TextPathDiagnostics.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/TextPathDiagnostics.cpp @@ -31,9 +31,8 @@ public: TextPathDiagnostics(const std::string& output, DiagnosticsEngine &diag) : OutputFile(output), Diag(diag) {} - void HandlePathDiagnosticImpl(const PathDiagnostic* D); - - void FlushDiagnostics(SmallVectorImpl<std::string> *FilesMade) { } + void FlushDiagnosticsImpl(std::vector<const PathDiagnostic *> &Diags, + SmallVectorImpl<std::string> *FilesMade); virtual StringRef getName() const { return "TextPathDiagnostics"; @@ -53,18 +52,18 @@ ento::createTextPathDiagnosticConsumer(const std::string& out, return new TextPathDiagnostics(out, PP.getDiagnostics()); } -void TextPathDiagnostics::HandlePathDiagnosticImpl(const PathDiagnostic* D) { - if (!D) - return; - - if (D->empty()) { - delete D; - return; - } - - for (PathDiagnostic::const_iterator I=D->begin(), E=D->end(); I != E; ++I) { - unsigned diagID = Diag.getDiagnosticIDs()->getCustomDiagID( - DiagnosticIDs::Note, I->getString()); - Diag.Report(I->getLocation().asLocation(), diagID); +void TextPathDiagnostics::FlushDiagnosticsImpl( + std::vector<const PathDiagnostic *> &Diags, + SmallVectorImpl<std::string> *FilesMade) { + for (std::vector<const PathDiagnostic *>::iterator it = Diags.begin(), + et = Diags.end(); it != et; ++it) { + const PathDiagnostic *D = *it; + for (PathPieces::const_iterator I = D->path.begin(), E = D->path.end(); + I != E; ++I) { + unsigned diagID = + Diag.getDiagnosticIDs()->getCustomDiagID(DiagnosticIDs::Note, + (*I)->getString()); + Diag.Report((*I)->getLocation().asLocation(), diagID); + } } } diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp index 34a358f..c19ebcb 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp @@ -11,13 +11,17 @@ // //===----------------------------------------------------------------------===// +#define DEBUG_TYPE "AnalysisConsumer" + #include "AnalysisConsumer.h" #include "clang/AST/ASTConsumer.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/ParentMap.h" +#include "clang/AST/RecursiveASTVisitor.h" #include "clang/Analysis/CFG.h" +#include "clang/Analysis/CallGraph.h" #include "clang/StaticAnalyzer/Frontend/CheckerRegistration.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Checkers/LocalCheckers.h" @@ -34,13 +38,26 @@ #include "llvm/Support/raw_ostream.h" #include "llvm/Support/Path.h" #include "llvm/Support/Program.h" +#include "llvm/Support/Timer.h" +#include "llvm/ADT/DepthFirstIterator.h" #include "llvm/ADT/OwningPtr.h" +#include "llvm/ADT/SmallPtrSet.h" +#include "llvm/ADT/Statistic.h" + +#include <queue> using namespace clang; using namespace ento; +using llvm::SmallPtrSet; static ExplodedNode::Auditor* CreateUbiViz(); +STATISTIC(NumFunctionTopLevel, "The # of functions at top level."); +STATISTIC(NumFunctionsAnalyzed, "The # of functions analysed (as top level)."); +STATISTIC(NumBlocksInAnalyzedFunctions, + "The # of basic blocks in the analyzed functions."); +STATISTIC(PercentReachableBlocks, "The % of reachable basic blocks."); + //===----------------------------------------------------------------------===// // Special PathDiagnosticConsumers. //===----------------------------------------------------------------------===// @@ -59,7 +76,19 @@ createPlistHTMLDiagnosticConsumer(const std::string& prefix, namespace { -class AnalysisConsumer : public ASTConsumer { +class AnalysisConsumer : public ASTConsumer, + public RecursiveASTVisitor<AnalysisConsumer> { + enum AnalysisMode { + ANALYSIS_SYNTAX, + ANALYSIS_PATH, + ANALYSIS_ALL + }; + + /// Mode of the analyzes while recursively visiting Decls. + AnalysisMode RecVisitorMode; + /// Bug Reporter to use while recursively visiting Decls. + BugReporter *RecVisitorBR; + public: ASTContext *Ctx; const Preprocessor &PP; @@ -67,21 +96,45 @@ public: AnalyzerOptions Opts; ArrayRef<std::string> Plugins; + /// \brief Stores the declarations from the local translation unit. + /// Note, we pre-compute the local declarations at parse time as an + /// optimization to make sure we do not deserialize everything from disk. + /// The local declaration to all declarations ratio might be very small when + /// working with a PCH file. + SetOfDecls LocalTUDecls; + // PD is owned by AnalysisManager. PathDiagnosticConsumer *PD; StoreManagerCreator CreateStoreMgr; ConstraintManagerCreator CreateConstraintMgr; - llvm::OwningPtr<CheckerManager> checkerMgr; - llvm::OwningPtr<AnalysisManager> Mgr; + OwningPtr<CheckerManager> checkerMgr; + OwningPtr<AnalysisManager> Mgr; + + /// Time the analyzes time of each translation unit. + static llvm::Timer* TUTotalTimer; + + /// The information about analyzed functions shared throughout the + /// translation unit. + FunctionSummariesTy FunctionSummaries; AnalysisConsumer(const Preprocessor& pp, const std::string& outdir, const AnalyzerOptions& opts, ArrayRef<std::string> plugins) - : Ctx(0), PP(pp), OutDir(outdir), Opts(opts), Plugins(plugins), PD(0) { + : RecVisitorMode(ANALYSIS_ALL), RecVisitorBR(0), + Ctx(0), PP(pp), OutDir(outdir), Opts(opts), Plugins(plugins), PD(0) { DigestAnalyzerOptions(); + if (Opts.PrintStats) { + llvm::EnableStatistics(); + TUTotalTimer = new llvm::Timer("Analyzer Total Time"); + } + } + + ~AnalysisConsumer() { + if (Opts.PrintStats) + delete TUTotalTimer; } void DigestAnalyzerOptions() { @@ -117,15 +170,20 @@ public: } } - void DisplayFunction(const Decl *D) { + void DisplayFunction(const Decl *D, AnalysisMode Mode) { if (!Opts.AnalyzerDisplayProgress) return; SourceManager &SM = Mgr->getASTContext().getSourceManager(); PresumedLoc Loc = SM.getPresumedLoc(D->getLocation()); if (Loc.isValid()) { - llvm::errs() << "ANALYZE: " << Loc.getFilename(); - + llvm::errs() << "ANALYZE"; + switch (Mode) { + case ANALYSIS_SYNTAX: llvm::errs() << "(Syntax)"; break; + case ANALYSIS_PATH: llvm::errs() << "(Path Sensitive)"; break; + case ANALYSIS_ALL: break; + }; + llvm::errs() << ": " << Loc.getFilename(); if (isa<FunctionDecl>(D) || isa<ObjCMethodDecl>(D)) { const NamedDecl *ND = cast<NamedDecl>(D); llvm::errs() << ' ' << *ND << '\n'; @@ -143,112 +201,236 @@ public: virtual void Initialize(ASTContext &Context) { Ctx = &Context; - checkerMgr.reset(createCheckerManager(Opts, PP.getLangOptions(), Plugins, + checkerMgr.reset(createCheckerManager(Opts, PP.getLangOpts(), Plugins, PP.getDiagnostics())); Mgr.reset(new AnalysisManager(*Ctx, PP.getDiagnostics(), - PP.getLangOptions(), PD, + PP.getLangOpts(), PD, CreateStoreMgr, CreateConstraintMgr, checkerMgr.get(), /* Indexer */ 0, Opts.MaxNodes, Opts.MaxLoop, Opts.VisualizeEGDot, Opts.VisualizeEGUbi, Opts.AnalysisPurgeOpt, Opts.EagerlyAssume, - Opts.TrimGraph, Opts.InlineCall, + Opts.TrimGraph, Opts.UnoptimizedCFG, Opts.CFGAddImplicitDtors, Opts.CFGAddInitializers, - Opts.EagerlyTrimEGraph)); + Opts.EagerlyTrimEGraph, + Opts.IPAMode, + Opts.InlineMaxStackDepth, + Opts.InlineMaxFunctionSize, + Opts.InliningMode, + Opts.NoRetryExhausted)); } + /// \brief Store the top level decls in the set to be processed later on. + /// (Doing this pre-processing avoids deserialization of data from PCH.) + virtual bool HandleTopLevelDecl(DeclGroupRef D); + virtual void HandleTopLevelDeclInObjCContainer(DeclGroupRef D); + virtual void HandleTranslationUnit(ASTContext &C); - void HandleDeclContext(ASTContext &C, DeclContext *dc); - void HandleDeclContextDecl(ASTContext &C, Decl *D); - void HandleCode(Decl *D); + /// \brief Build the call graph for all the top level decls of this TU and + /// use it to define the order in which the functions should be visited. + void HandleDeclsGallGraph(); + + /// \brief Run analyzes(syntax or path sensitive) on the given function. + /// \param Mode - determines if we are requesting syntax only or path + /// sensitive only analysis. + /// \param VisitedCallees - The output parameter, which is populated with the + /// set of functions which should be considered analyzed after analyzing the + /// given root function. + void HandleCode(Decl *D, AnalysisMode Mode, + SetOfConstDecls *VisitedCallees = 0); + + void RunPathSensitiveChecks(Decl *D, SetOfConstDecls *VisitedCallees); + void ActionExprEngine(Decl *D, bool ObjCGCEnabled, + SetOfConstDecls *VisitedCallees); + + /// Visitors for the RecursiveASTVisitor. + + /// Handle callbacks for arbitrary Decls. + bool VisitDecl(Decl *D) { + checkerMgr->runCheckersOnASTDecl(D, *Mgr, *RecVisitorBR); + return true; + } + + bool VisitFunctionDecl(FunctionDecl *FD) { + IdentifierInfo *II = FD->getIdentifier(); + if (II && II->getName().startswith("__inline")) + return true; + + // We skip function template definitions, as their semantics is + // only determined when they are instantiated. + if (FD->isThisDeclarationADefinition() && + !FD->isDependentContext()) { + HandleCode(FD, RecVisitorMode); + } + return true; + } + + bool VisitObjCMethodDecl(ObjCMethodDecl *MD) { + checkerMgr->runCheckersOnASTDecl(MD, *Mgr, *RecVisitorBR); + if (MD->isThisDeclarationADefinition()) + HandleCode(MD, RecVisitorMode); + return true; + } + +private: + void storeTopLevelDecls(DeclGroupRef DG); + + /// \brief Check if we should skip (not analyze) the given function. + bool skipFunction(Decl *D); + }; } // end anonymous namespace + //===----------------------------------------------------------------------===// // AnalysisConsumer implementation. //===----------------------------------------------------------------------===// +llvm::Timer* AnalysisConsumer::TUTotalTimer = 0; + +bool AnalysisConsumer::HandleTopLevelDecl(DeclGroupRef DG) { + storeTopLevelDecls(DG); + return true; +} -void AnalysisConsumer::HandleDeclContext(ASTContext &C, DeclContext *dc) { - for (DeclContext::decl_iterator I = dc->decls_begin(), E = dc->decls_end(); - I != E; ++I) { - HandleDeclContextDecl(C, *I); +void AnalysisConsumer::HandleTopLevelDeclInObjCContainer(DeclGroupRef DG) { + storeTopLevelDecls(DG); +} + +void AnalysisConsumer::storeTopLevelDecls(DeclGroupRef DG) { + for (DeclGroupRef::iterator I = DG.begin(), E = DG.end(); I != E; ++I) { + + // Skip ObjCMethodDecl, wait for the objc container to avoid + // analyzing twice. + if (isa<ObjCMethodDecl>(*I)) + continue; + + LocalTUDecls.insert(*I); } } -void AnalysisConsumer::HandleDeclContextDecl(ASTContext &C, Decl *D) { - { // Handle callbacks for arbitrary decls. - BugReporter BR(*Mgr); - checkerMgr->runCheckersOnASTDecl(D, *Mgr, BR); +void AnalysisConsumer::HandleDeclsGallGraph() { + // Otherwise, use the Callgraph to derive the order. + // Build the Call Graph. + CallGraph CG; + // Add all the top level declarations to the graph. + for (SetOfDecls::iterator I = LocalTUDecls.begin(), + E = LocalTUDecls.end(); I != E; ++I) + CG.addToCallGraph(*I); + + // Find the top level nodes - children of root + the unreachable (parentless) + // nodes. + llvm::SmallVector<CallGraphNode*, 24> TopLevelFunctions; + for (CallGraph::nodes_iterator TI = CG.parentless_begin(), + TE = CG.parentless_end(); TI != TE; ++TI) { + TopLevelFunctions.push_back(*TI); + NumFunctionTopLevel++; + } + CallGraphNode *Entry = CG.getRoot(); + for (CallGraphNode::iterator I = Entry->begin(), + E = Entry->end(); I != E; ++I) { + TopLevelFunctions.push_back(*I); + NumFunctionTopLevel++; } - switch (D->getKind()) { - case Decl::Namespace: { - HandleDeclContext(C, cast<NamespaceDecl>(D)); - break; - } - case Decl::CXXConstructor: - case Decl::CXXDestructor: - case Decl::CXXConversion: - case Decl::CXXMethod: - case Decl::Function: { - FunctionDecl *FD = cast<FunctionDecl>(D); - // We skip function template definitions, as their semantics is - // only determined when they are instantiated. - if (FD->isThisDeclarationADefinition() && - !FD->isDependentContext()) { - if (!Opts.AnalyzeSpecificFunction.empty() && - FD->getDeclName().getAsString() != Opts.AnalyzeSpecificFunction) - break; - DisplayFunction(FD); - HandleCode(FD); - } - break; + // Make sure the nodes are sorted in order reverse of their definition in the + // translation unit. This step is very important for performance. It ensures + // that we analyze the root functions before the externally available + // subroutines. + std::queue<CallGraphNode*> BFSQueue; + for (llvm::SmallVector<CallGraphNode*, 24>::reverse_iterator + TI = TopLevelFunctions.rbegin(), TE = TopLevelFunctions.rend(); + TI != TE; ++TI) + BFSQueue.push(*TI); + + // BFS over all of the functions, while skipping the ones inlined into + // the previously processed functions. Use external Visited set, which is + // also modified when we inline a function. + SmallPtrSet<CallGraphNode*,24> Visited; + while(!BFSQueue.empty()) { + CallGraphNode *N = BFSQueue.front(); + BFSQueue.pop(); + + // Skip the functions which have been processed already or previously + // inlined. + if (Visited.count(N)) + continue; + + // Analyze the function. + SetOfConstDecls VisitedCallees; + Decl *D = N->getDecl(); + assert(D); + HandleCode(D, ANALYSIS_PATH, + (Mgr->InliningMode == All ? 0 : &VisitedCallees)); + + // Add the visited callees to the global visited set. + for (SetOfConstDecls::const_iterator I = VisitedCallees.begin(), + E = VisitedCallees.end(); I != E; ++I){ + CallGraphNode *VN = CG.getNode(*I); + if (VN) + Visited.insert(VN); } - - case Decl::ObjCCategoryImpl: - case Decl::ObjCImplementation: { - ObjCImplDecl *ID = cast<ObjCImplDecl>(D); - HandleCode(ID); - - for (ObjCContainerDecl::method_iterator MI = ID->meth_begin(), - ME = ID->meth_end(); MI != ME; ++MI) { - BugReporter BR(*Mgr); - checkerMgr->runCheckersOnASTDecl(*MI, *Mgr, BR); - - if ((*MI)->isThisDeclarationADefinition()) { - if (!Opts.AnalyzeSpecificFunction.empty() && - Opts.AnalyzeSpecificFunction != - (*MI)->getSelector().getAsString()) - continue; - DisplayFunction(*MI); - HandleCode(*MI); - } - } - break; + Visited.insert(N); + + // Push the children into the queue. + for (CallGraphNode::const_iterator CI = N->begin(), + CE = N->end(); CI != CE; ++CI) { + BFSQueue.push(*CI); } - - default: - break; } } void AnalysisConsumer::HandleTranslationUnit(ASTContext &C) { - BugReporter BR(*Mgr); - TranslationUnitDecl *TU = C.getTranslationUnitDecl(); - checkerMgr->runCheckersOnASTDecl(TU, *Mgr, BR); - HandleDeclContext(C, TU); + // Don't run the actions if an error has occurred with parsing the file. + DiagnosticsEngine &Diags = PP.getDiagnostics(); + if (Diags.hasErrorOccurred() || Diags.hasFatalErrorOccurred()) + return; - // After all decls handled, run checkers on the entire TranslationUnit. - checkerMgr->runCheckersOnEndOfTranslationUnit(TU, *Mgr, BR); + { + if (TUTotalTimer) TUTotalTimer->startTimer(); + + // Introduce a scope to destroy BR before Mgr. + BugReporter BR(*Mgr); + TranslationUnitDecl *TU = C.getTranslationUnitDecl(); + checkerMgr->runCheckersOnASTDecl(TU, *Mgr, BR); + + // Run the AST-only checks using the order in which functions are defined. + // If inlining is not turned on, use the simplest function order for path + // sensitive analyzes as well. + RecVisitorMode = (Mgr->shouldInlineCall() ? ANALYSIS_SYNTAX : ANALYSIS_ALL); + RecVisitorBR = &BR; + + // Process all the top level declarations. + for (SetOfDecls::iterator I = LocalTUDecls.begin(), + E = LocalTUDecls.end(); I != E; ++I) + TraverseDecl(*I); + + if (Mgr->shouldInlineCall()) + HandleDeclsGallGraph(); + + // After all decls handled, run checkers on the entire TranslationUnit. + checkerMgr->runCheckersOnEndOfTranslationUnit(TU, *Mgr, BR); + + RecVisitorBR = 0; + } // Explicitly destroy the PathDiagnosticConsumer. This will flush its output. // FIXME: This should be replaced with something that doesn't rely on // side-effects in PathDiagnosticConsumer's destructor. This is required when // used with option -disable-free. Mgr.reset(NULL); + + if (TUTotalTimer) TUTotalTimer->stopTimer(); + + // Count how many basic blocks we have not covered. + NumBlocksInAnalyzedFunctions = FunctionSummaries.getTotalNumBasicBlocks(); + if (NumBlocksInAnalyzedFunctions > 0) + PercentReachableBlocks = + (FunctionSummaries.getTotalNumVisitedBasicBlocks() * 100) / + NumBlocksInAnalyzedFunctions; + } static void FindBlocks(DeclContext *D, SmallVectorImpl<Decl*> &WL) { @@ -261,24 +443,41 @@ static void FindBlocks(DeclContext *D, SmallVectorImpl<Decl*> &WL) { FindBlocks(DC, WL); } -static void RunPathSensitiveChecks(AnalysisConsumer &C, AnalysisManager &mgr, - Decl *D); - -void AnalysisConsumer::HandleCode(Decl *D) { +static std::string getFunctionName(const Decl *D) { + if (const ObjCMethodDecl *ID = dyn_cast<ObjCMethodDecl>(D)) { + return ID->getSelector().getAsString(); + } + if (const FunctionDecl *ND = dyn_cast<FunctionDecl>(D)) { + IdentifierInfo *II = ND->getIdentifier(); + if (II) + return II->getName(); + } + return ""; +} - // Don't run the actions if an error has occurred with parsing the file. - DiagnosticsEngine &Diags = PP.getDiagnostics(); - if (Diags.hasErrorOccurred() || Diags.hasFatalErrorOccurred()) - return; +bool AnalysisConsumer::skipFunction(Decl *D) { + if (!Opts.AnalyzeSpecificFunction.empty() && + getFunctionName(D) != Opts.AnalyzeSpecificFunction) + return true; // Don't run the actions on declarations in header files unless // otherwise specified. SourceManager &SM = Ctx->getSourceManager(); SourceLocation SL = SM.getExpansionLoc(D->getLocation()); if (!Opts.AnalyzeAll && !SM.isFromMainFile(SL)) + return true; + + return false; +} + +void AnalysisConsumer::HandleCode(Decl *D, AnalysisMode Mode, + SetOfConstDecls *VisitedCallees) { + if (skipFunction(D)) return; - // Clear the AnalysisManager of old AnalysisContexts. + DisplayFunction(D, Mode); + + // Clear the AnalysisManager of old AnalysisDeclContexts. Mgr->ClearContexts(); // Dispatch on the actions. @@ -292,9 +491,12 @@ void AnalysisConsumer::HandleCode(Decl *D) { for (SmallVectorImpl<Decl*>::iterator WI=WL.begin(), WE=WL.end(); WI != WE; ++WI) if ((*WI)->hasBody()) { - checkerMgr->runCheckersOnASTBody(*WI, *Mgr, BR); - if (checkerMgr->hasPathSensitiveCheckers()) - RunPathSensitiveChecks(*this, *Mgr, *WI); + if (Mode != ANALYSIS_PATH) + checkerMgr->runCheckersOnASTBody(*WI, *Mgr, BR); + if (Mode != ANALYSIS_SYNTAX && checkerMgr->hasPathSensitiveCheckers()) { + RunPathSensitiveChecks(*WI, VisitedCallees); + NumFunctionsAnalyzed++; + } } } @@ -302,53 +504,53 @@ void AnalysisConsumer::HandleCode(Decl *D) { // Path-sensitive checking. //===----------------------------------------------------------------------===// -static void ActionExprEngine(AnalysisConsumer &C, AnalysisManager &mgr, - Decl *D, bool ObjCGCEnabled) { +void AnalysisConsumer::ActionExprEngine(Decl *D, bool ObjCGCEnabled, + SetOfConstDecls *VisitedCallees) { // Construct the analysis engine. First check if the CFG is valid. // FIXME: Inter-procedural analysis will need to handle invalid CFGs. - if (!mgr.getCFG(D)) + if (!Mgr->getCFG(D)) return; - ExprEngine Eng(mgr, ObjCGCEnabled); + + ExprEngine Eng(*Mgr, ObjCGCEnabled, VisitedCallees, &FunctionSummaries); // Set the graph auditor. - llvm::OwningPtr<ExplodedNode::Auditor> Auditor; - if (mgr.shouldVisualizeUbigraph()) { + OwningPtr<ExplodedNode::Auditor> Auditor; + if (Mgr->shouldVisualizeUbigraph()) { Auditor.reset(CreateUbiViz()); ExplodedNode::SetAuditor(Auditor.get()); } // Execute the worklist algorithm. - Eng.ExecuteWorkList(mgr.getStackFrame(D, 0), mgr.getMaxNodes()); + Eng.ExecuteWorkList(Mgr->getAnalysisDeclContextManager().getStackFrame(D, 0), + Mgr->getMaxNodes()); // Release the auditor (if any) so that it doesn't monitor the graph // created BugReporter. ExplodedNode::SetAuditor(0); // Visualize the exploded graph. - if (mgr.shouldVisualizeGraphviz()) - Eng.ViewGraph(mgr.shouldTrimGraph()); + if (Mgr->shouldVisualizeGraphviz()) + Eng.ViewGraph(Mgr->shouldTrimGraph()); // Display warnings. Eng.getBugReporter().FlushReports(); } -static void RunPathSensitiveChecks(AnalysisConsumer &C, AnalysisManager &mgr, - Decl *D) { +void AnalysisConsumer::RunPathSensitiveChecks(Decl *D, + SetOfConstDecls *Visited) { - switch (mgr.getLangOptions().getGC()) { - default: - llvm_unreachable("Invalid GC mode."); + switch (Mgr->getLangOpts().getGC()) { case LangOptions::NonGC: - ActionExprEngine(C, mgr, D, false); + ActionExprEngine(D, false, Visited); break; case LangOptions::GCOnly: - ActionExprEngine(C, mgr, D, true); + ActionExprEngine(D, true, Visited); break; case LangOptions::HybridGC: - ActionExprEngine(C, mgr, D, false); - ActionExprEngine(C, mgr, D, true); + ActionExprEngine(D, false, Visited); + ActionExprEngine(D, true, Visited); break; } } @@ -374,7 +576,7 @@ ASTConsumer* ento::CreateAnalysisConsumer(const Preprocessor& pp, namespace { class UbigraphViz : public ExplodedNode::Auditor { - llvm::OwningPtr<raw_ostream> Out; + OwningPtr<raw_ostream> Out; llvm::sys::Path Dir, Filename; unsigned Cntr; @@ -408,7 +610,7 @@ static ExplodedNode::Auditor* CreateUbiViz() { llvm::errs() << "Writing '" << Filename.str() << "'.\n"; - llvm::OwningPtr<llvm::raw_fd_ostream> Stream; + OwningPtr<llvm::raw_fd_ostream> Stream; Stream.reset(new llvm::raw_fd_ostream(Filename.c_str(), ErrMsg)); if (!ErrMsg.empty()) diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Frontend/CheckerRegistration.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Frontend/CheckerRegistration.cpp index a59fcad..c06da0d 100644 --- a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Frontend/CheckerRegistration.cpp +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Frontend/CheckerRegistration.cpp @@ -104,7 +104,7 @@ CheckerManager *ento::createCheckerManager(const AnalyzerOptions &opts, const LangOptions &langOpts, ArrayRef<std::string> plugins, DiagnosticsEngine &diags) { - llvm::OwningPtr<CheckerManager> checkerMgr(new CheckerManager(langOpts)); + OwningPtr<CheckerManager> checkerMgr(new CheckerManager(langOpts)); SmallVector<CheckerOptInfo, 8> checkerOpts; for (unsigned i = 0, e = opts.CheckersControlList.size(); i != e; ++i) { |