diff options
Diffstat (limited to 'lib/StaticAnalyzer')
103 files changed, 6650 insertions, 4035 deletions
diff --git a/lib/StaticAnalyzer/Checkers/AnalyzerStatsChecker.cpp b/lib/StaticAnalyzer/Checkers/AnalyzerStatsChecker.cpp index aa6f97b2fa8f..9af0a5ac4fd5 100644 --- a/lib/StaticAnalyzer/Checkers/AnalyzerStatsChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/AnalyzerStatsChecker.cpp @@ -11,17 +11,17 @@ #define DEBUG_TYPE "StatsChecker" #include "ClangSACheckers.h" +#include "clang/AST/DeclObjC.h" +#include "clang/Basic/SourceManager.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/ExplodedGraph.h" #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" +#include "llvm/Support/raw_ostream.h" using namespace clang; using namespace ento; @@ -60,7 +60,7 @@ void AnalyzerStatsChecker::checkEndAnalysis(ExplodedGraph &G, if (D != P.getLocationContext()->getDecl()) continue; - if (const BlockEntrance *BE = dyn_cast<BlockEntrance>(&P)) { + if (Optional<BlockEntrance> BE = P.getAs<BlockEntrance>()) { const CFGBlock *CB = BE->getBlock(); reachable.insert(CB); } @@ -123,14 +123,14 @@ 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)) { + if (Optional<CFGStmt> CS = CE.getAs<CFGStmt>()) { 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)); + B.EmitBasicReport( + D, "Sink Point", "Internal Statistics", outputI.str(), + PathDiagnosticLocation::createBegin(CS->getStmt(), SM, LC)); } } } diff --git a/lib/StaticAnalyzer/Checkers/ArrayBoundChecker.cpp b/lib/StaticAnalyzer/Checkers/ArrayBoundChecker.cpp index 535d8eede46a..312bc749b181 100644 --- a/lib/StaticAnalyzer/Checkers/ArrayBoundChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/ArrayBoundChecker.cpp @@ -13,10 +13,10 @@ //===----------------------------------------------------------------------===// #include "ClangSACheckers.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.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/ExprEngine.h" using namespace clang; @@ -44,7 +44,7 @@ void ArrayBoundChecker::checkLocation(SVal l, bool isLoad, const Stmt* LoadS, return; // Get the index of the accessed element. - DefinedOrUnknownSVal Idx = cast<DefinedOrUnknownSVal>(ER->getIndex()); + DefinedOrUnknownSVal Idx = ER->getIndex().castAs<DefinedOrUnknownSVal>(); // Zero index is always in bound, this also passes ElementRegions created for // pointer casts. diff --git a/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp b/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp index 457c870943dd..5e4b824df4b9 100644 --- a/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp +++ b/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp @@ -13,14 +13,14 @@ //===----------------------------------------------------------------------===// #include "ClangSACheckers.h" +#include "clang/AST/CharUnits.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.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/ExprEngine.h" -#include "clang/AST/CharUnits.h" #include "llvm/ADT/SmallString.h" -#include "llvm/ADT/STLExtras.h" +#include "llvm/Support/raw_ostream.h" using namespace clang; using namespace ento; @@ -53,7 +53,7 @@ public: RegionRawOffsetV2(const SubRegion* base, SVal offset) : baseRegion(base), byteOffset(offset) {} - NonLoc getByteOffset() const { return cast<NonLoc>(byteOffset); } + NonLoc getByteOffset() const { return byteOffset.castAs<NonLoc>(); } const SubRegion *getRegion() const { return baseRegion; } static RegionRawOffsetV2 computeOffset(ProgramStateRef state, @@ -110,13 +110,12 @@ void ArrayBoundCheckerV2::checkLocation(SVal location, bool isLoad, SVal extentBegin = computeExtentBegin(svalBuilder, rawOffset.getRegion()); - if (isa<NonLoc>(extentBegin)) { - SVal lowerBound - = svalBuilder.evalBinOpNN(state, BO_LT, rawOffset.getByteOffset(), - cast<NonLoc>(extentBegin), + if (Optional<NonLoc> NV = extentBegin.getAs<NonLoc>()) { + SVal lowerBound = + svalBuilder.evalBinOpNN(state, BO_LT, rawOffset.getByteOffset(), *NV, svalBuilder.getConditionType()); - NonLoc *lowerBoundToCheck = dyn_cast<NonLoc>(&lowerBound); + Optional<NonLoc> lowerBoundToCheck = lowerBound.getAs<NonLoc>(); if (!lowerBoundToCheck) return; @@ -140,15 +139,15 @@ void ArrayBoundCheckerV2::checkLocation(SVal location, bool isLoad, // we are doing a load/store after the last valid offset. DefinedOrUnknownSVal extentVal = rawOffset.getRegion()->getExtent(svalBuilder); - if (!isa<NonLoc>(extentVal)) + if (!extentVal.getAs<NonLoc>()) break; SVal upperbound = svalBuilder.evalBinOpNN(state, BO_GE, rawOffset.getByteOffset(), - cast<NonLoc>(extentVal), + extentVal.castAs<NonLoc>(), svalBuilder.getConditionType()); - NonLoc *upperboundToCheck = dyn_cast<NonLoc>(&upperbound); + Optional<NonLoc> upperboundToCheck = upperbound.getAs<NonLoc>(); if (!upperboundToCheck) break; @@ -235,7 +234,7 @@ static bool IsCompleteType(ASTContext &Ctx, QualType Ty) { // is unknown or undefined, we lazily substitute '0'. Otherwise, // return 'val'. static inline SVal getValue(SVal val, SValBuilder &svalBuilder) { - return isa<UndefinedVal>(val) ? svalBuilder.makeArrayIndex(0) : val; + return val.getAs<UndefinedVal>() ? svalBuilder.makeArrayIndex(0) : val; } // Scale a base value by a scaling factor, and return the scaled @@ -256,9 +255,9 @@ static SVal addValue(ProgramStateRef state, SVal x, SVal y, // only care about computing offsets. if (x.isUnknownOrUndef() || y.isUnknownOrUndef()) return UnknownVal(); - - return svalBuilder.evalBinOpNN(state, BO_Add, - cast<NonLoc>(x), cast<NonLoc>(y), + + return svalBuilder.evalBinOpNN(state, BO_Add, x.castAs<NonLoc>(), + y.castAs<NonLoc>(), svalBuilder.getArrayIndexType()); } @@ -284,7 +283,7 @@ RegionRawOffsetV2 RegionRawOffsetV2::computeOffset(ProgramStateRef state, case MemRegion::ElementRegionKind: { const ElementRegion *elemReg = cast<ElementRegion>(region); SVal index = elemReg->getIndex(); - if (!isa<NonLoc>(index)) + if (!index.getAs<NonLoc>()) return RegionRawOffsetV2(); QualType elemType = elemReg->getElementType(); // If the element is an incomplete type, go no further. @@ -296,7 +295,7 @@ RegionRawOffsetV2 RegionRawOffsetV2::computeOffset(ProgramStateRef state, offset = addValue(state, getValue(offset, svalBuilder), scaleValue(state, - cast<NonLoc>(index), + index.castAs<NonLoc>(), astContext.getTypeSizeInChars(elemType), svalBuilder), svalBuilder); diff --git a/lib/StaticAnalyzer/Checkers/AttrNonNullChecker.cpp b/lib/StaticAnalyzer/Checkers/AttrNonNullChecker.cpp deleted file mode 100644 index 81e8dd885a34..000000000000 --- a/lib/StaticAnalyzer/Checkers/AttrNonNullChecker.cpp +++ /dev/null @@ -1,130 +0,0 @@ -//===--- AttrNonNullChecker.h - Undefined arguments 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 AttrNonNullChecker, a builtin check in ExprEngine that -// performs checks for arguments declared to have nonnull attribute. -// -//===----------------------------------------------------------------------===// - -#include "ClangSACheckers.h" -#include "clang/StaticAnalyzer/Core/Checker.h" -#include "clang/StaticAnalyzer/Core/CheckerManager.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" -#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" - -using namespace clang; -using namespace ento; - -namespace { -class AttrNonNullChecker - : public Checker< check::PreCall > { - mutable OwningPtr<BugType> BT; -public: - - void checkPreCall(const CallEvent &Call, CheckerContext &C) const; -}; -} // end anonymous namespace - -void AttrNonNullChecker::checkPreCall(const CallEvent &Call, - CheckerContext &C) const { - const Decl *FD = Call.getDecl(); - if (!FD) - return; - - const NonNullAttr *Att = FD->getAttr<NonNullAttr>(); - if (!Att) - return; - - ProgramStateRef state = C.getState(); - - // Iterate through the arguments of CE and check them for null. - for (unsigned idx = 0, count = Call.getNumArgs(); idx != count; ++idx) { - if (!Att->isNonNull(idx)) - continue; - - SVal V = Call.getArgSVal(idx); - DefinedSVal *DV = dyn_cast<DefinedSVal>(&V); - - // If the value is unknown or undefined, we can't perform this check. - if (!DV) - continue; - - if (!isa<Loc>(*DV)) { - // If the argument is a union type, we want to handle a potential - // transparent_union GCC extension. - const Expr *ArgE = Call.getArgExpr(idx); - if (!ArgE) - continue; - - QualType T = ArgE->getType(); - const RecordType *UT = T->getAsUnionType(); - if (!UT || !UT->getDecl()->hasAttr<TransparentUnionAttr>()) - continue; - - if (nonloc::CompoundVal *CSV = dyn_cast<nonloc::CompoundVal>(DV)) { - nonloc::CompoundVal::iterator CSV_I = CSV->begin(); - assert(CSV_I != CSV->end()); - V = *CSV_I; - DV = dyn_cast<DefinedSVal>(&V); - assert(++CSV_I == CSV->end()); - if (!DV) - continue; - } else { - // FIXME: Handle LazyCompoundVals? - continue; - } - } - - ConstraintManager &CM = C.getConstraintManager(); - ProgramStateRef stateNotNull, stateNull; - llvm::tie(stateNotNull, stateNull) = CM.assumeDual(state, *DV); - - if (stateNull && !stateNotNull) { - // Generate an error node. Check for a null node in case - // we cache out. - if (ExplodedNode *errorNode = C.generateSink(stateNull)) { - - // Lazily allocate the BugType object if it hasn't already been - // created. Ownership is transferred to the BugReporter object once - // the BugReport is passed to 'EmitWarning'. - if (!BT) - BT.reset(new BugType("Argument with 'nonnull' attribute passed null", - "API")); - - BugReport *R = - new BugReport(*BT, "Null pointer passed as an argument to a " - "'nonnull' parameter", errorNode); - - // Highlight the range of the argument that was null. - R->addRange(Call.getArgSourceRange(idx)); - if (const Expr *ArgE = Call.getArgExpr(idx)) - bugreporter::trackNullOrUndefValue(errorNode, ArgE, *R); - // Emit the bug report. - C.emitReport(R); - } - - // Always return. Either we cached out or we just emitted an error. - return; - } - - // If a pointer value passed the check we should assume that it is - // indeed not null from this point forward. - assert(stateNotNull); - state = stateNotNull; - } - - // If we reach here all of the arguments passed the nonnull check. - // If 'state' has been updated generated a new node. - C.addTransition(state); -} - -void ento::registerAttrNonNullChecker(CheckerManager &mgr) { - mgr.registerChecker<AttrNonNullChecker>(); -} diff --git a/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp b/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp index eba534e08f6b..533a324e7507 100644 --- a/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp +++ b/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp @@ -14,23 +14,24 @@ //===----------------------------------------------------------------------===// #include "ClangSACheckers.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/DeclObjC.h" +#include "clang/AST/Expr.h" +#include "clang/AST/ExprObjC.h" +#include "clang/AST/StmtObjC.h" #include "clang/Analysis/DomainSpecific/CocoaConventions.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" -#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h" -#include "clang/AST/DeclObjC.h" -#include "clang/AST/Expr.h" -#include "clang/AST/ExprObjC.h" -#include "clang/AST/StmtObjC.h" -#include "clang/AST/ASTContext.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringMap.h" +#include "llvm/Support/raw_ostream.h" using namespace clang; using namespace ento; @@ -82,10 +83,6 @@ static FoundationClass findKnownClass(const ObjCInterfaceDecl *ID) { return result; } -static inline bool isNil(SVal X) { - return isa<loc::ConcreteInt>(X); -} - //===----------------------------------------------------------------------===// // NilArgChecker - Check for prohibited nil arguments to ObjC method calls. //===----------------------------------------------------------------------===// @@ -94,29 +91,55 @@ namespace { class NilArgChecker : public Checker<check::PreObjCMessage> { mutable OwningPtr<APIMisuse> BT; - void WarnNilArg(CheckerContext &C, - const ObjCMethodCall &msg, unsigned Arg) const; + void WarnIfNilArg(CheckerContext &C, + const ObjCMethodCall &msg, unsigned Arg, + FoundationClass Class, + bool CanBeSubscript = false) const; public: void checkPreObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const; }; } -void NilArgChecker::WarnNilArg(CheckerContext &C, - const ObjCMethodCall &msg, - unsigned int Arg) const -{ +void NilArgChecker::WarnIfNilArg(CheckerContext &C, + const ObjCMethodCall &msg, + unsigned int Arg, + FoundationClass Class, + bool CanBeSubscript) const { + // Check if the argument is nil. + ProgramStateRef State = C.getState(); + if (!State->isNull(msg.getArgSVal(Arg)).isConstrainedTrue()) + return; + if (!BT) BT.reset(new APIMisuse("nil argument")); - + if (ExplodedNode *N = C.generateSink()) { SmallString<128> sbuf; llvm::raw_svector_ostream os(sbuf); - os << "Argument to '" << GetReceiverInterfaceName(msg) << "' method '" - << msg.getSelector().getAsString() << "' cannot be nil"; + + if (CanBeSubscript && msg.getMessageKind() == OCM_Subscript) { + + if (Class == FC_NSArray) { + os << "Array element cannot be nil"; + } else if (Class == FC_NSDictionary) { + if (Arg == 0) + os << "Dictionary object cannot be nil"; + else { + assert(Arg == 1); + os << "Dictionary key cannot be nil"; + } + } else + llvm_unreachable("Missing foundation class for the subscript expr"); + + } else { + os << "Argument to '" << GetReceiverInterfaceName(msg) << "' method '" + << msg.getSelector().getAsString() << "' cannot be nil"; + } BugReport *R = new BugReport(*BT, os.str(), N); R->addRange(msg.getArgSourceRange(Arg)); + bugreporter::trackNullOrUndefValue(N, msg.getArgExpr(Arg), *R); C.emitReport(R); } } @@ -126,8 +149,14 @@ void NilArgChecker::checkPreObjCMessage(const ObjCMethodCall &msg, const ObjCInterfaceDecl *ID = msg.getReceiverInterface(); if (!ID) return; + + FoundationClass Class = findKnownClass(ID); + + static const unsigned InvalidArgIndex = UINT_MAX; + unsigned Arg = InvalidArgIndex; + bool CanBeSubscript = false; - if (findKnownClass(ID) == FC_NSString) { + if (Class == FC_NSString) { Selector S = msg.getSelector(); if (S.isUnarySelector()) @@ -151,10 +180,58 @@ void NilArgChecker::checkPreObjCMessage(const ObjCMethodCall &msg, Name == "compare:options:range:locale:" || Name == "componentsSeparatedByCharactersInSet:" || Name == "initWithFormat:") { - if (isNil(msg.getArgSVal(0))) - WarnNilArg(C, msg, 0); + Arg = 0; + } + } else if (Class == FC_NSArray) { + Selector S = msg.getSelector(); + + if (S.isUnarySelector()) + return; + + if (S.getNameForSlot(0).equals("addObject")) { + Arg = 0; + } else if (S.getNameForSlot(0).equals("insertObject") && + S.getNameForSlot(1).equals("atIndex")) { + Arg = 0; + } else if (S.getNameForSlot(0).equals("replaceObjectAtIndex") && + S.getNameForSlot(1).equals("withObject")) { + Arg = 1; + } else if (S.getNameForSlot(0).equals("setObject") && + S.getNameForSlot(1).equals("atIndexedSubscript")) { + Arg = 0; + CanBeSubscript = true; + } else if (S.getNameForSlot(0).equals("arrayByAddingObject")) { + Arg = 0; + } + } else if (Class == FC_NSDictionary) { + Selector S = msg.getSelector(); + + if (S.isUnarySelector()) + return; + + if (S.getNameForSlot(0).equals("dictionaryWithObject") && + S.getNameForSlot(1).equals("forKey")) { + Arg = 0; + WarnIfNilArg(C, msg, /* Arg */1, Class); + } else if (S.getNameForSlot(0).equals("setObject") && + S.getNameForSlot(1).equals("forKey")) { + Arg = 0; + WarnIfNilArg(C, msg, /* Arg */1, Class); + } else if (S.getNameForSlot(0).equals("setObject") && + S.getNameForSlot(1).equals("forKeyedSubscript")) { + CanBeSubscript = true; + Arg = 0; + WarnIfNilArg(C, msg, /* Arg */1, Class, CanBeSubscript); + } else if (S.getNameForSlot(0).equals("removeObjectForKey")) { + Arg = 0; } } + + + // If argument is '0', report a warning. + if ((Arg != InvalidArgIndex)) + WarnIfNilArg(C, msg, Arg, Class, CanBeSubscript); + } //===----------------------------------------------------------------------===// @@ -195,28 +272,6 @@ enum CFNumberType { kCFNumberCGFloatType = 16 }; -namespace { - template<typename T> - class Optional { - bool IsKnown; - T Val; - public: - Optional() : IsKnown(false), Val(0) {} - Optional(const T& val) : IsKnown(true), Val(val) {} - - bool isKnown() const { return IsKnown; } - - const T& getValue() const { - assert (isKnown()); - return Val; - } - - operator const T&() const { - return getValue(); - } - }; -} - static Optional<uint64_t> GetCFNumberSize(ASTContext &Ctx, uint64_t i) { static const unsigned char FixedSize[] = { 8, 16, 32, 64, 32, 64 }; @@ -238,7 +293,7 @@ static Optional<uint64_t> GetCFNumberSize(ASTContext &Ctx, uint64_t i) { case kCFNumberCGFloatType: // FIXME: We need a way to map from names to Type*. default: - return Optional<uint64_t>(); + return None; } return Ctx.getTypeSize(T); @@ -289,17 +344,19 @@ void CFNumberCreateChecker::checkPreStmt(const CallExpr *CE, // FIXME: We really should allow ranges of valid theType values, and // bifurcate the state appropriately. - nonloc::ConcreteInt* V = dyn_cast<nonloc::ConcreteInt>(&TheTypeVal); + Optional<nonloc::ConcreteInt> V = TheTypeVal.getAs<nonloc::ConcreteInt>(); if (!V) return; uint64_t NumberKind = V->getValue().getLimitedValue(); - Optional<uint64_t> TargetSize = GetCFNumberSize(Ctx, NumberKind); + Optional<uint64_t> OptTargetSize = GetCFNumberSize(Ctx, NumberKind); // FIXME: In some cases we can emit an error. - if (!TargetSize.isKnown()) + if (!OptTargetSize) return; + uint64_t TargetSize = *OptTargetSize; + // 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. @@ -307,7 +364,7 @@ void CFNumberCreateChecker::checkPreStmt(const CallExpr *CE, // FIXME: Eventually we should handle arbitrary locations. We can do this // by having an enhanced memory model that does low-level typing. - loc::MemRegionVal* LV = dyn_cast<loc::MemRegionVal>(&TheValueExpr); + Optional<loc::MemRegionVal> LV = TheValueExpr.getAs<loc::MemRegionVal>(); if (!LV) return; @@ -403,18 +460,19 @@ void CFRetainReleaseChecker::checkPreStmt(const CallExpr *CE, return; // FIXME: The rest of this just checks that the argument is non-null. - // It should probably be refactored and combined with AttrNonNullChecker. + // It should probably be refactored and combined with NonNullParamChecker. // Get the argument's value. const Expr *Arg = CE->getArg(0); SVal ArgVal = state->getSVal(Arg, C.getLocationContext()); - DefinedSVal *DefArgVal = dyn_cast<DefinedSVal>(&ArgVal); + Optional<DefinedSVal> DefArgVal = ArgVal.getAs<DefinedSVal>(); if (!DefArgVal) return; // Get a NULL value. SValBuilder &svalBuilder = C.getSValBuilder(); - DefinedSVal zero = cast<DefinedSVal>(svalBuilder.makeZeroVal(Arg->getType())); + DefinedSVal zero = + svalBuilder.makeZeroVal(Arg->getType()).castAs<DefinedSVal>(); // Make an expression asserting that they're equal. DefinedOrUnknownSVal ArgIsNull = svalBuilder.evalEQ(state, zero, *DefArgVal); @@ -605,7 +663,7 @@ void VariadicMethodTypeChecker::checkPreObjCMessage(const ObjCMethodCall &msg, return; // Verify that all arguments have Objective-C types. - llvm::Optional<ExplodedNode*> errorNode; + Optional<ExplodedNode*> errorNode; ProgramStateRef state = C.getState(); for (unsigned I = variadicArgsBegin; I != variadicArgsEnd; ++I) { @@ -618,7 +676,7 @@ void VariadicMethodTypeChecker::checkPreObjCMessage(const ObjCMethodCall &msg, continue; // Ignore pointer constants. - if (isa<loc::ConcreteInt>(msg.getArgSVal(I))) + if (msg.getArgSVal(I).getAs<loc::ConcreteInt>()) continue; // Ignore pointer types annotated with 'NSObject' attribute. @@ -715,12 +773,12 @@ void ObjCLoopChecker::checkPostStmt(const ObjCForCollectionStmt *FCS, ElementVar = State->getSVal(Element, C.getLocationContext()); } - if (!isa<Loc>(ElementVar)) + if (!ElementVar.getAs<Loc>()) return; // Go ahead and assume the value is non-nil. - SVal Val = State->getSVal(cast<Loc>(ElementVar)); - State = State->assume(cast<DefinedOrUnknownSVal>(Val), true); + SVal Val = State->getSVal(ElementVar.castAs<Loc>()); + State = State->assume(Val.castAs<DefinedOrUnknownSVal>(), true); C.addTransition(State); } @@ -744,7 +802,7 @@ static ProgramStateRef assumeExprIsNonNull(const Expr *NonNullExpr, ProgramStateRef State, CheckerContext &C) { SVal Val = State->getSVal(NonNullExpr, C.getLocationContext()); - if (DefinedOrUnknownSVal *DV = dyn_cast<DefinedOrUnknownSVal>(&Val)) + if (Optional<DefinedOrUnknownSVal> DV = Val.getAs<DefinedOrUnknownSVal>()) return State->assume(*DV, true); return State; } diff --git a/lib/StaticAnalyzer/Checkers/BoolAssignmentChecker.cpp b/lib/StaticAnalyzer/Checkers/BoolAssignmentChecker.cpp index 92edefe7b170..5169244a6f90 100644 --- a/lib/StaticAnalyzer/Checkers/BoolAssignmentChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/BoolAssignmentChecker.cpp @@ -13,17 +13,17 @@ //===----------------------------------------------------------------------===// #include "ClangSACheckers.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.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; + mutable OwningPtr<BuiltinBug> BT; void emitReport(ProgramStateRef state, CheckerContext &C) const; public: void checkBind(SVal loc, SVal val, const Stmt *S, CheckerContext &C) const; @@ -69,7 +69,7 @@ void BoolAssignmentChecker::checkBind(SVal loc, SVal val, const Stmt *S, // 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); + Optional<DefinedSVal> DV = val.getAs<DefinedSVal>(); if (!DV) return; @@ -85,10 +85,10 @@ void BoolAssignmentChecker::checkBind(SVal loc, SVal val, const Stmt *S, SVal greaterThanOrEqualToZeroVal = svalBuilder.evalBinOp(state, BO_GE, *DV, zeroVal, svalBuilder.getConditionType()); - - DefinedSVal *greaterThanEqualToZero = - dyn_cast<DefinedSVal>(&greaterThanOrEqualToZeroVal); - + + Optional<DefinedSVal> greaterThanEqualToZero = + greaterThanOrEqualToZeroVal.getAs<DefinedSVal>(); + if (!greaterThanEqualToZero) { // The SValBuilder cannot construct a valid SVal for this condition. // This means we cannot properly reason about it. @@ -121,10 +121,10 @@ void BoolAssignmentChecker::checkBind(SVal loc, SVal val, const Stmt *S, SVal lessThanEqToOneVal = svalBuilder.evalBinOp(state, BO_LE, *DV, OneVal, svalBuilder.getConditionType()); - - DefinedSVal *lessThanEqToOne = - dyn_cast<DefinedSVal>(&lessThanEqToOneVal); - + + Optional<DefinedSVal> lessThanEqToOne = + lessThanEqToOneVal.getAs<DefinedSVal>(); + if (!lessThanEqToOne) { // The SValBuilder cannot construct a valid SVal for this condition. // This means we cannot properly reason about it. diff --git a/lib/StaticAnalyzer/Checkers/BuiltinFunctionChecker.cpp b/lib/StaticAnalyzer/Checkers/BuiltinFunctionChecker.cpp index 6ef022b60925..a3327d8b3194 100644 --- a/lib/StaticAnalyzer/Checkers/BuiltinFunctionChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/BuiltinFunctionChecker.cpp @@ -12,10 +12,10 @@ //===----------------------------------------------------------------------===// #include "ClangSACheckers.h" +#include "clang/Basic/Builtins.h" #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" -#include "clang/Basic/Builtins.h" using namespace clang; using namespace ento; @@ -61,13 +61,14 @@ bool BuiltinFunctionChecker::evalCall(const CallExpr *CE, // 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()), LCtx)); + state->getSVal(*(CE->arg_begin()), LCtx).castAs<DefinedOrUnknownSVal>(); SValBuilder& svalBuilder = C.getSValBuilder(); DefinedOrUnknownSVal Extent = R->getExtent(svalBuilder); DefinedOrUnknownSVal extentMatchesSizeArg = svalBuilder.evalEQ(state, Extent, Size); state = state->assume(extentMatchesSizeArg, true); + assert(state && "The region should not have any previous constraints"); C.addTransition(state->BindExpr(CE, LCtx, loc::MemRegionVal(R))); return true; diff --git a/lib/StaticAnalyzer/Checkers/CMakeLists.txt b/lib/StaticAnalyzer/Checkers/CMakeLists.txt index 8e455de3bf46..b7df10e7ffbe 100644 --- a/lib/StaticAnalyzer/Checkers/CMakeLists.txt +++ b/lib/StaticAnalyzer/Checkers/CMakeLists.txt @@ -7,7 +7,6 @@ add_clang_library(clangStaticAnalyzerCheckers AnalyzerStatsChecker.cpp ArrayBoundChecker.cpp ArrayBoundCheckerV2.cpp - AttrNonNullChecker.cpp BasicObjCFoundationChecks.cpp BoolAssignmentChecker.cpp BuiltinFunctionChecker.cpp @@ -31,7 +30,6 @@ add_clang_library(clangStaticAnalyzerCheckers DivZeroChecker.cpp DynamicTypePropagation.cpp ExprInspectionChecker.cpp - SimpleStreamChecker.cpp FixedAddressChecker.cpp GenericTaintChecker.cpp IdempotentOperationChecker.cpp @@ -44,6 +42,7 @@ add_clang_library(clangStaticAnalyzerCheckers MallocSizeofChecker.cpp NSAutoreleasePoolChecker.cpp NSErrorChecker.cpp + NonNullParamChecker.cpp NoReturnFunctionChecker.cpp ObjCAtSyncChecker.cpp ObjCContainersASTChecker.cpp @@ -57,6 +56,7 @@ add_clang_library(clangStaticAnalyzerCheckers RetainCountChecker.cpp ReturnPointerRangeChecker.cpp ReturnUndefChecker.cpp + SimpleStreamChecker.cpp StackAddrEscapeChecker.cpp StreamChecker.cpp TaintTesterChecker.cpp diff --git a/lib/StaticAnalyzer/Checkers/CStringChecker.cpp b/lib/StaticAnalyzer/Checkers/CStringChecker.cpp index eae9ddfc05b9..cc55e9f6ecf0 100644 --- a/lib/StaticAnalyzer/Checkers/CStringChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/CStringChecker.cpp @@ -14,14 +14,16 @@ #include "ClangSACheckers.h" #include "InterCheckerAPI.h" +#include "clang/Basic/CharInfo.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.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/SmallString.h" #include "llvm/ADT/StringSwitch.h" +#include "llvm/Support/raw_ostream.h" using namespace clang; using namespace ento; @@ -63,7 +65,7 @@ public: ProgramStateRef checkRegionChanges(ProgramStateRef state, - const StoreManager::InvalidatedSymbols *, + const InvalidatedSymbols *, ArrayRef<const MemRegion *> ExplicitRegions, ArrayRef<const MemRegion *> Regions, const CallEvent *Call) const; @@ -199,7 +201,7 @@ REGISTER_MAP_WITH_PROGRAMSTATE(CStringLength, const MemRegion *, SVal) std::pair<ProgramStateRef , ProgramStateRef > CStringChecker::assumeZero(CheckerContext &C, ProgramStateRef state, SVal V, QualType Ty) { - DefinedSVal *val = dyn_cast<DefinedSVal>(&V); + Optional<DefinedSVal> val = V.getAs<DefinedSVal>(); if (!val) return std::pair<ProgramStateRef , ProgramStateRef >(state, state); @@ -276,10 +278,10 @@ ProgramStateRef CStringChecker::CheckLocation(CheckerContext &C, SValBuilder &svalBuilder = C.getSValBuilder(); SVal Extent = svalBuilder.convertToArrayIndex(superReg->getExtent(svalBuilder)); - DefinedOrUnknownSVal Size = cast<DefinedOrUnknownSVal>(Extent); + DefinedOrUnknownSVal Size = Extent.castAs<DefinedOrUnknownSVal>(); // Get the index of the accessed element. - DefinedOrUnknownSVal Idx = cast<DefinedOrUnknownSVal>(ER->getIndex()); + DefinedOrUnknownSVal Idx = ER->getIndex().castAs<DefinedOrUnknownSVal>(); ProgramStateRef StInBound = state->assumeInBound(Idx, Size, true); ProgramStateRef StOutBound = state->assumeInBound(Idx, Size, false); @@ -304,7 +306,7 @@ ProgramStateRef CStringChecker::CheckLocation(CheckerContext &C, SmallString<80> buf; llvm::raw_svector_ostream os(buf); - os << (char)toupper(CurrentFunctionDescription[0]) + os << toUppercase(CurrentFunctionDescription[0]) << &CurrentFunctionDescription[1] << " accesses out-of-bound array element"; report = new BugReport(*BT, os.str(), N); @@ -357,18 +359,18 @@ ProgramStateRef CStringChecker::CheckBufferAccess(CheckerContext &C, // FIXME: This assumes the caller has already checked that the access length // is positive. And that it's unsigned. SVal LengthVal = state->getSVal(Size, LCtx); - NonLoc *Length = dyn_cast<NonLoc>(&LengthVal); + Optional<NonLoc> Length = LengthVal.getAs<NonLoc>(); if (!Length) return state; // Compute the offset of the last element to be accessed: size-1. - NonLoc One = cast<NonLoc>(svalBuilder.makeIntVal(1, sizeTy)); - NonLoc LastOffset = cast<NonLoc>(svalBuilder.evalBinOpNN(state, BO_Sub, - *Length, One, sizeTy)); + NonLoc One = svalBuilder.makeIntVal(1, sizeTy).castAs<NonLoc>(); + NonLoc LastOffset = svalBuilder + .evalBinOpNN(state, BO_Sub, *Length, One, sizeTy).castAs<NonLoc>(); // Check that the first buffer is sufficiently long. SVal BufStart = svalBuilder.evalCast(BufVal, PtrTy, FirstBuf->getType()); - if (Loc *BufLoc = dyn_cast<Loc>(&BufStart)) { + if (Optional<Loc> BufLoc = BufStart.getAs<Loc>()) { const Expr *warningExpr = (WarnAboutSize ? Size : FirstBuf); SVal BufEnd = svalBuilder.evalBinOpLN(state, BO_Add, *BufLoc, @@ -388,7 +390,7 @@ ProgramStateRef CStringChecker::CheckBufferAccess(CheckerContext &C, return NULL; BufStart = svalBuilder.evalCast(BufVal, PtrTy, SecondBuf->getType()); - if (Loc *BufLoc = dyn_cast<Loc>(&BufStart)) { + if (Optional<Loc> BufLoc = BufStart.getAs<Loc>()) { const Expr *warningExpr = (WarnAboutSize ? Size : SecondBuf); SVal BufEnd = svalBuilder.evalBinOpLN(state, BO_Add, *BufLoc, @@ -424,11 +426,11 @@ ProgramStateRef CStringChecker::CheckOverlap(CheckerContext &C, SVal firstVal = state->getSVal(First, LCtx); SVal secondVal = state->getSVal(Second, LCtx); - Loc *firstLoc = dyn_cast<Loc>(&firstVal); + Optional<Loc> firstLoc = firstVal.getAs<Loc>(); if (!firstLoc) return state; - Loc *secondLoc = dyn_cast<Loc>(&secondVal); + Optional<Loc> secondLoc = secondVal.getAs<Loc>(); if (!secondLoc) return state; @@ -451,7 +453,8 @@ ProgramStateRef CStringChecker::CheckOverlap(CheckerContext &C, QualType cmpTy = svalBuilder.getConditionType(); SVal reverse = svalBuilder.evalBinOpLL(state, BO_GT, *firstLoc, *secondLoc, cmpTy); - DefinedOrUnknownSVal *reverseTest = dyn_cast<DefinedOrUnknownSVal>(&reverse); + Optional<DefinedOrUnknownSVal> reverseTest = + reverse.getAs<DefinedOrUnknownSVal>(); if (!reverseTest) return state; @@ -462,20 +465,16 @@ ProgramStateRef CStringChecker::CheckOverlap(CheckerContext &C, return state; } else { // Switch the values so that firstVal is before secondVal. - Loc *tmpLoc = firstLoc; - firstLoc = secondLoc; - secondLoc = tmpLoc; + std::swap(firstLoc, secondLoc); // Switch the Exprs as well, so that they still correspond. - const Expr *tmpExpr = First; - First = Second; - Second = tmpExpr; + std::swap(First, Second); } } // Get the length, and make sure it too is known. SVal LengthVal = state->getSVal(Size, LCtx); - NonLoc *Length = dyn_cast<NonLoc>(&LengthVal); + Optional<NonLoc> Length = LengthVal.getAs<NonLoc>(); if (!Length) return state; @@ -485,21 +484,22 @@ ProgramStateRef CStringChecker::CheckOverlap(CheckerContext &C, QualType CharPtrTy = Ctx.getPointerType(Ctx.CharTy); SVal FirstStart = svalBuilder.evalCast(*firstLoc, CharPtrTy, First->getType()); - Loc *FirstStartLoc = dyn_cast<Loc>(&FirstStart); + Optional<Loc> FirstStartLoc = FirstStart.getAs<Loc>(); if (!FirstStartLoc) return state; // Compute the end of the first buffer. Bail out if THAT fails. SVal FirstEnd = svalBuilder.evalBinOpLN(state, BO_Add, *FirstStartLoc, *Length, CharPtrTy); - Loc *FirstEndLoc = dyn_cast<Loc>(&FirstEnd); + Optional<Loc> FirstEndLoc = FirstEnd.getAs<Loc>(); if (!FirstEndLoc) return state; // Is the end of the first buffer past the start of the second buffer? SVal Overlap = svalBuilder.evalBinOpLL(state, BO_GT, *FirstEndLoc, *secondLoc, cmpTy); - DefinedOrUnknownSVal *OverlapTest = dyn_cast<DefinedOrUnknownSVal>(&Overlap); + Optional<DefinedOrUnknownSVal> OverlapTest = + Overlap.getAs<DefinedOrUnknownSVal>(); if (!OverlapTest) return state; @@ -555,7 +555,7 @@ ProgramStateRef CStringChecker::checkAdditionOverflow(CheckerContext &C, NonLoc maxVal = svalBuilder.makeIntVal(maxValInt); SVal maxMinusRight; - if (isa<nonloc::ConcreteInt>(right)) { + if (right.getAs<nonloc::ConcreteInt>()) { maxMinusRight = svalBuilder.evalBinOpNN(state, BO_Sub, maxVal, right, sizeTy); } else { @@ -566,7 +566,7 @@ ProgramStateRef CStringChecker::checkAdditionOverflow(CheckerContext &C, left = right; } - if (NonLoc *maxMinusRightNL = dyn_cast<NonLoc>(&maxMinusRight)) { + if (Optional<NonLoc> maxMinusRightNL = maxMinusRight.getAs<NonLoc>()) { QualType cmpTy = svalBuilder.getConditionType(); // If left > max - right, we have an overflow. SVal willOverflow = svalBuilder.evalBinOpNN(state, BO_GT, left, @@ -574,7 +574,7 @@ ProgramStateRef CStringChecker::checkAdditionOverflow(CheckerContext &C, ProgramStateRef stateOverflow, stateOkay; llvm::tie(stateOverflow, stateOkay) = - state->assume(cast<DefinedOrUnknownSVal>(willOverflow)); + state->assume(willOverflow.castAs<DefinedOrUnknownSVal>()); if (stateOverflow && !stateOkay) { // We have an overflow. Emit a bug report. @@ -681,7 +681,7 @@ SVal CStringChecker::getCStringLength(CheckerContext &C, ProgramStateRef &state, // If we can't get a region, see if it's something we /know/ isn't a // 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 (Optional<loc::GotoLabel> Label = Buf.getAs<loc::GotoLabel>()) { if (!Filter.CheckCStringNotNullTerm) return UndefinedVal(); @@ -796,14 +796,14 @@ const StringLiteral *CStringChecker::getCStringLiteral(CheckerContext &C, ProgramStateRef CStringChecker::InvalidateBuffer(CheckerContext &C, ProgramStateRef state, const Expr *E, SVal V) { - Loc *L = dyn_cast<Loc>(&V); + Optional<Loc> L = V.getAs<Loc>(); if (!L) return state; // FIXME: This is a simplified version of what's in CFRefCount.cpp -- it makes // some assumptions about the value that CFRefCount can't. Even so, it should // probably be refactored. - if (loc::MemRegionVal* MR = dyn_cast<loc::MemRegionVal>(L)) { + if (Optional<loc::MemRegionVal> MR = L->getAs<loc::MemRegionVal>()) { const MemRegion *R = MR->getRegion()->StripCasts(); // Are we dealing with an ElementRegion? If so, we should be invalidating @@ -815,7 +815,8 @@ ProgramStateRef CStringChecker::InvalidateBuffer(CheckerContext &C, // Invalidate this region. const LocationContext *LCtx = C.getPredecessor()->getLocationContext(); - return state->invalidateRegions(R, E, C.blockCount(), LCtx); + return state->invalidateRegions(R, E, C.blockCount(), LCtx, + /*CausesPointerEscape*/ false); } // If we have a non-region value by chance, just remove the binding. @@ -926,16 +927,13 @@ void CStringChecker::evalCopyCommon(CheckerContext &C, // If this is mempcpy, get the byte after the last byte copied and // bind the expr. if (IsMempcpy) { - loc::MemRegionVal *destRegVal = dyn_cast<loc::MemRegionVal>(&destVal); - assert(destRegVal && "Destination should be a known MemRegionVal here"); + loc::MemRegionVal destRegVal = destVal.castAs<loc::MemRegionVal>(); // Get the length to copy. - NonLoc *lenValNonLoc = dyn_cast<NonLoc>(&sizeVal); - - if (lenValNonLoc) { + if (Optional<NonLoc> lenValNonLoc = sizeVal.getAs<NonLoc>()) { // Get the byte after the last byte copied. SVal lastElement = C.getSValBuilder().evalBinOpLN(state, BO_Add, - *destRegVal, + destRegVal, *lenValNonLoc, Dest->getType()); @@ -1051,9 +1049,9 @@ void CStringChecker::evalMemcmp(CheckerContext &C, const CallExpr *CE) const { // First, get the two buffers' addresses. Another checker will have already // made sure they're not undefined. DefinedOrUnknownSVal LV = - cast<DefinedOrUnknownSVal>(state->getSVal(Left, LCtx)); + state->getSVal(Left, LCtx).castAs<DefinedOrUnknownSVal>(); DefinedOrUnknownSVal RV = - cast<DefinedOrUnknownSVal>(state->getSVal(Right, LCtx)); + state->getSVal(Right, LCtx).castAs<DefinedOrUnknownSVal>(); // See if they are the same. DefinedOrUnknownSVal SameBuf = svalBuilder.evalEQ(state, LV, RV); @@ -1163,19 +1161,17 @@ void CStringChecker::evalstrLengthCommon(CheckerContext &C, const CallExpr *CE, const Expr *maxlenExpr = CE->getArg(1); SVal maxlenVal = state->getSVal(maxlenExpr, LCtx); - NonLoc *strLengthNL = dyn_cast<NonLoc>(&strLength); - NonLoc *maxlenValNL = dyn_cast<NonLoc>(&maxlenVal); + Optional<NonLoc> strLengthNL = strLength.getAs<NonLoc>(); + Optional<NonLoc> maxlenValNL = maxlenVal.getAs<NonLoc>(); if (strLengthNL && maxlenValNL) { ProgramStateRef stateStringTooLong, stateStringNotTooLong; // Check if the strLength is greater than the maxlen. llvm::tie(stateStringTooLong, stateStringNotTooLong) = - state->assume(cast<DefinedOrUnknownSVal> - (C.getSValBuilder().evalBinOpNN(state, BO_GT, - *strLengthNL, - *maxlenValNL, - cmpTy))); + state->assume(C.getSValBuilder().evalBinOpNN( + state, BO_GT, *strLengthNL, *maxlenValNL, cmpTy) + .castAs<DefinedOrUnknownSVal>()); if (stateStringTooLong && !stateStringNotTooLong) { // If the string is longer than maxlen, return maxlen. @@ -1192,28 +1188,24 @@ 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. result = C.getSValBuilder().conjureSymbolVal(0, CE, LCtx, C.blockCount()); - NonLoc *resultNL = cast<NonLoc>(&result); + NonLoc resultNL = result.castAs<NonLoc>(); if (strLengthNL) { - state = state->assume(cast<DefinedOrUnknownSVal> - (C.getSValBuilder().evalBinOpNN(state, BO_LE, - *resultNL, - *strLengthNL, - cmpTy)), true); + state = state->assume(C.getSValBuilder().evalBinOpNN( + state, BO_LE, resultNL, *strLengthNL, cmpTy) + .castAs<DefinedOrUnknownSVal>(), true); } if (maxlenValNL) { - state = state->assume(cast<DefinedOrUnknownSVal> - (C.getSValBuilder().evalBinOpNN(state, BO_LE, - *resultNL, - *maxlenValNL, - cmpTy)), true); + state = state->assume(C.getSValBuilder().evalBinOpNN( + state, BO_LE, resultNL, *maxlenValNL, cmpTy) + .castAs<DefinedOrUnknownSVal>(), true); } } } else { // This is a plain strlen(), not strnlen(). - result = cast<DefinedOrUnknownSVal>(strLength); + result = strLength.castAs<DefinedOrUnknownSVal>(); // If we don't know the length of the string, conjure a return // value, so it can be used in constraints, at least. @@ -1332,8 +1324,8 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE, // Protect against misdeclared strncpy(). lenVal = svalBuilder.evalCast(lenVal, sizeTy, lenExpr->getType()); - NonLoc *strLengthNL = dyn_cast<NonLoc>(&strLength); - NonLoc *lenValNL = dyn_cast<NonLoc>(&lenVal); + Optional<NonLoc> strLengthNL = strLength.getAs<NonLoc>(); + Optional<NonLoc> lenValNL = lenVal.getAs<NonLoc>(); // If we know both values, we might be able to figure out how much // we're copying. @@ -1343,10 +1335,9 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE, // 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- // terminate the result! - llvm::tie(stateSourceTooLong, stateSourceNotTooLong) = - state->assume(cast<DefinedOrUnknownSVal> - (svalBuilder.evalBinOpNN(state, BO_GE, *strLengthNL, - *lenValNL, cmpTy))); + llvm::tie(stateSourceTooLong, stateSourceNotTooLong) = state->assume( + svalBuilder.evalBinOpNN(state, BO_GE, *strLengthNL, *lenValNL, cmpTy) + .castAs<DefinedOrUnknownSVal>()); if (stateSourceTooLong && !stateSourceNotTooLong) { // Max number to copy is less than the length of the src, so the actual @@ -1373,7 +1364,7 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE, if (dstStrLength.isUndef()) return; - if (NonLoc *dstStrLengthNL = dyn_cast<NonLoc>(&dstStrLength)) { + if (Optional<NonLoc> dstStrLengthNL = dstStrLength.getAs<NonLoc>()) { maxLastElementIndex = svalBuilder.evalBinOpNN(state, BO_Add, *lenValNL, *dstStrLengthNL, @@ -1404,7 +1395,7 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE, // Otherwise, go ahead and figure out the last element we'll touch. // We don't record the non-zero assumption here because we can't // be sure. We won't warn on a possible zero. - NonLoc one = cast<NonLoc>(svalBuilder.makeIntVal(1, sizeTy)); + NonLoc one = svalBuilder.makeIntVal(1, sizeTy).castAs<NonLoc>(); maxLastElementIndex = svalBuilder.evalBinOpNN(state, BO_Sub, *lenValNL, one, sizeTy); boundWarning = "Size argument is greater than the length of the " @@ -1422,15 +1413,15 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE, amountCopied = getCStringLength(C, state, lenExpr, srcVal, true); assert(!amountCopied.isUndef()); - if (NonLoc *amountCopiedNL = dyn_cast<NonLoc>(&amountCopied)) { + if (Optional<NonLoc> amountCopiedNL = amountCopied.getAs<NonLoc>()) { if (lenValNL) { // amountCopied <= lenVal SVal copiedLessThanBound = svalBuilder.evalBinOpNN(state, BO_LE, *amountCopiedNL, *lenValNL, cmpTy); - state = state->assume(cast<DefinedOrUnknownSVal>(copiedLessThanBound), - true); + state = state->assume( + copiedLessThanBound.castAs<DefinedOrUnknownSVal>(), true); if (!state) return; } @@ -1441,8 +1432,8 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE, *amountCopiedNL, *strLengthNL, cmpTy); - state = state->assume(cast<DefinedOrUnknownSVal>(copiedLessThanSrc), - true); + state = state->assume( + copiedLessThanSrc.castAs<DefinedOrUnknownSVal>(), true); if (!state) return; } @@ -1472,8 +1463,8 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE, if (dstStrLength.isUndef()) return; - NonLoc *srcStrLengthNL = dyn_cast<NonLoc>(&amountCopied); - NonLoc *dstStrLengthNL = dyn_cast<NonLoc>(&dstStrLength); + Optional<NonLoc> srcStrLengthNL = amountCopied.getAs<NonLoc>(); + Optional<NonLoc> dstStrLengthNL = dstStrLength.getAs<NonLoc>(); // If we know both string lengths, we might know the final string length. if (srcStrLengthNL && dstStrLengthNL) { @@ -1494,14 +1485,14 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE, finalStrLength = getCStringLength(C, state, CE, DstVal, true); assert(!finalStrLength.isUndef()); - if (NonLoc *finalStrLengthNL = dyn_cast<NonLoc>(&finalStrLength)) { + if (Optional<NonLoc> finalStrLengthNL = finalStrLength.getAs<NonLoc>()) { if (srcStrLengthNL) { // finalStrLength >= srcStrLength SVal sourceInResult = svalBuilder.evalBinOpNN(state, BO_GE, *finalStrLengthNL, *srcStrLengthNL, cmpTy); - state = state->assume(cast<DefinedOrUnknownSVal>(sourceInResult), + state = state->assume(sourceInResult.castAs<DefinedOrUnknownSVal>(), true); if (!state) return; @@ -1513,8 +1504,8 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE, *finalStrLengthNL, *dstStrLengthNL, cmpTy); - state = state->assume(cast<DefinedOrUnknownSVal>(destInResult), - true); + state = + state->assume(destInResult.castAs<DefinedOrUnknownSVal>(), true); if (!state) return; } @@ -1535,13 +1526,14 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE, // If the destination is a MemRegion, try to check for a buffer overflow and // record the new string length. - if (loc::MemRegionVal *dstRegVal = dyn_cast<loc::MemRegionVal>(&DstVal)) { + if (Optional<loc::MemRegionVal> dstRegVal = + DstVal.getAs<loc::MemRegionVal>()) { QualType ptrTy = Dst->getType(); // If we have an exact value on a bounded copy, use that to check for // overflows, rather than our estimate about how much is actually copied. if (boundWarning) { - if (NonLoc *maxLastNL = dyn_cast<NonLoc>(&maxLastElementIndex)) { + if (Optional<NonLoc> maxLastNL = maxLastElementIndex.getAs<NonLoc>()) { SVal maxLastElement = svalBuilder.evalBinOpLN(state, BO_Add, *dstRegVal, *maxLastNL, ptrTy); state = CheckLocation(C, state, CE->getArg(2), maxLastElement, @@ -1552,7 +1544,7 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE, } // Then, if the final length is known... - if (NonLoc *knownStrLength = dyn_cast<NonLoc>(&finalStrLength)) { + if (Optional<NonLoc> knownStrLength = finalStrLength.getAs<NonLoc>()) { SVal lastElement = svalBuilder.evalBinOpLN(state, BO_Add, *dstRegVal, *knownStrLength, ptrTy); @@ -1670,8 +1662,8 @@ void CStringChecker::evalStrcmpCommon(CheckerContext &C, const CallExpr *CE, // 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>(s1Val); - DefinedOrUnknownSVal RV = cast<DefinedOrUnknownSVal>(s2Val); + DefinedOrUnknownSVal LV = s1Val.castAs<DefinedOrUnknownSVal>(); + DefinedOrUnknownSVal RV = s2Val.castAs<DefinedOrUnknownSVal>(); // See if they are the same. SValBuilder &svalBuilder = C.getSValBuilder(); @@ -1856,8 +1848,8 @@ void CStringChecker::checkPreStmt(const DeclStmt *DS, CheckerContext &C) const { 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)); + DefinedOrUnknownSVal strLength = + getCStringLength(C, state, Init, StrVal).castAs<DefinedOrUnknownSVal>(); state = state->set<CStringLength>(MR, strLength); } @@ -1872,7 +1864,7 @@ bool CStringChecker::wantsRegionChangeUpdate(ProgramStateRef state) const { ProgramStateRef CStringChecker::checkRegionChanges(ProgramStateRef state, - const StoreManager::InvalidatedSymbols *, + const InvalidatedSymbols *, ArrayRef<const MemRegion *> ExplicitRegions, ArrayRef<const MemRegion *> Regions, const CallEvent *Call) const { diff --git a/lib/StaticAnalyzer/Checkers/CStringSyntaxChecker.cpp b/lib/StaticAnalyzer/Checkers/CStringSyntaxChecker.cpp index f1a3aacc7c4c..3a57a56aea6e 100644 --- a/lib/StaticAnalyzer/Checkers/CStringSyntaxChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/CStringSyntaxChecker.cpp @@ -13,14 +13,14 @@ // //===----------------------------------------------------------------------===// #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/Analysis/AnalysisContext.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/Checker.h" #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" #include "llvm/ADT/SmallString.h" diff --git a/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp b/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp index 82bc1361acfe..4965d2299616 100644 --- a/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp @@ -13,14 +13,15 @@ //===----------------------------------------------------------------------===// #include "ClangSACheckers.h" +#include "clang/AST/ParentMap.h" +#include "clang/Basic/TargetInfo.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" -#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" -#include "clang/AST/ParentMap.h" -#include "clang/Basic/TargetInfo.h" #include "llvm/ADT/SmallString.h" +#include "llvm/Support/raw_ostream.h" using namespace clang; using namespace ento; @@ -75,6 +76,8 @@ void CallAndMessageChecker::emitBadCall(BugType *BT, CheckerContext &C, BugReport *R = new BugReport(*BT, BT->getName(), N); if (BadE) { R->addRange(BadE->getSourceRange()); + if (BadE->isGLValue()) + BadE = bugreporter::getDerefExpr(BadE); bugreporter::trackNullOrUndefValue(N, BadE, *R); } C.emitReport(R); @@ -130,9 +133,9 @@ bool CallAndMessageChecker::PreVisitProcessArg(CheckerContext &C, if (!checkUninitFields) return false; - - if (const nonloc::LazyCompoundVal *LV = - dyn_cast<nonloc::LazyCompoundVal>(&V)) { + + if (Optional<nonloc::LazyCompoundVal> LV = + V.getAs<nonloc::LazyCompoundVal>()) { class FindUninitializedField { public: @@ -233,7 +236,8 @@ void CallAndMessageChecker::checkPreStmt(const CallExpr *CE, } ProgramStateRef StNonNull, StNull; - llvm::tie(StNonNull, StNull) = State->assume(cast<DefinedOrUnknownSVal>(L)); + llvm::tie(StNonNull, StNull) = + State->assume(L.castAs<DefinedOrUnknownSVal>()); if (StNull && !StNonNull) { if (!BT_call_null) @@ -262,7 +266,8 @@ void CallAndMessageChecker::checkPreCall(const CallEvent &Call, } ProgramStateRef StNonNull, StNull; - llvm::tie(StNonNull, StNull) = State->assume(cast<DefinedOrUnknownSVal>(V)); + llvm::tie(StNonNull, StNull) = + State->assume(V.castAs<DefinedOrUnknownSVal>()); if (StNull && !StNonNull) { if (!BT_cxx_call_null) @@ -341,7 +346,7 @@ void CallAndMessageChecker::checkPreObjCMessage(const ObjCMethodCall &msg, return; } else { // Bifurcate the state into nil and non-nil ones. - DefinedOrUnknownSVal receiverVal = cast<DefinedOrUnknownSVal>(recVal); + DefinedOrUnknownSVal receiverVal = recVal.castAs<DefinedOrUnknownSVal>(); ProgramStateRef state = C.getState(); ProgramStateRef notNilState, nilState; @@ -361,17 +366,23 @@ void CallAndMessageChecker::emitNilReceiverBug(CheckerContext &C, if (!BT_msg_ret) BT_msg_ret.reset( - new BuiltinBug("Receiver in message expression is " - "'nil' and returns a garbage value")); + new BuiltinBug("Receiver in message expression is 'nil'")); const ObjCMessageExpr *ME = msg.getOriginExpr(); + QualType ResTy = msg.getResultType(); + SmallString<200> buf; llvm::raw_svector_ostream os(buf); os << "The receiver of message '" << ME->getSelector().getAsString() - << "' is nil and returns a value of type '"; - msg.getResultType().print(os, C.getLangOpts()); - os << "' that will be garbage"; + << "' is nil"; + if (ResTy->isReferenceType()) { + os << ", which results in forming a null reference"; + } else { + os << " and returns a value of type '"; + msg.getResultType().print(os, C.getLangOpts()); + os << "' that will be garbage"; + } BugReport *report = new BugReport(*BT_msg_ret, os.str(), N); report->addRange(ME->getReceiverRange()); @@ -392,6 +403,7 @@ void CallAndMessageChecker::HandleNilReceiver(CheckerContext &C, ProgramStateRef state, const ObjCMethodCall &Msg) const { ASTContext &Ctx = C.getASTContext(); + static SimpleProgramPointTag Tag("CallAndMessageChecker : NilReceiver"); // Check the return type of the message expression. A message to nil will // return different values depending on the return type and the architecture. @@ -402,7 +414,7 @@ void CallAndMessageChecker::HandleNilReceiver(CheckerContext &C, if (CanRetTy->isStructureOrClassType()) { // Structure returns are safe since the compiler zeroes them out. SVal V = C.getSValBuilder().makeZeroVal(RetTy); - C.addTransition(state->BindExpr(Msg.getOriginExpr(), LCtx, V)); + C.addTransition(state->BindExpr(Msg.getOriginExpr(), LCtx, V), &Tag); return; } @@ -413,14 +425,15 @@ void CallAndMessageChecker::HandleNilReceiver(CheckerContext &C, const uint64_t voidPtrSize = Ctx.getTypeSize(Ctx.VoidPtrTy); const uint64_t returnTypeSize = Ctx.getTypeSize(CanRetTy); - if (voidPtrSize < returnTypeSize && - !(supportsNilWithFloatRet(Ctx.getTargetInfo().getTriple()) && - (Ctx.FloatTy == CanRetTy || - Ctx.DoubleTy == CanRetTy || - Ctx.LongDoubleTy == CanRetTy || - Ctx.LongLongTy == CanRetTy || - Ctx.UnsignedLongLongTy == CanRetTy))) { - if (ExplodedNode *N = C.generateSink(state)) + if (CanRetTy.getTypePtr()->isReferenceType()|| + (voidPtrSize < returnTypeSize && + !(supportsNilWithFloatRet(Ctx.getTargetInfo().getTriple()) && + (Ctx.FloatTy == CanRetTy || + Ctx.DoubleTy == CanRetTy || + Ctx.LongDoubleTy == CanRetTy || + Ctx.LongLongTy == CanRetTy || + Ctx.UnsignedLongLongTy == CanRetTy)))) { + if (ExplodedNode *N = C.generateSink(state, 0 , &Tag)) emitNilReceiverBug(C, Msg, N); return; } @@ -439,7 +452,7 @@ void CallAndMessageChecker::HandleNilReceiver(CheckerContext &C, // of this case unless we have *a lot* more knowledge. // SVal V = C.getSValBuilder().makeZeroVal(RetTy); - C.addTransition(state->BindExpr(Msg.getOriginExpr(), LCtx, V)); + C.addTransition(state->BindExpr(Msg.getOriginExpr(), LCtx, V), &Tag); return; } diff --git a/lib/StaticAnalyzer/Checkers/CastSizeChecker.cpp b/lib/StaticAnalyzer/Checkers/CastSizeChecker.cpp index 1cb8a8de7348..5e6e10541483 100644 --- a/lib/StaticAnalyzer/Checkers/CastSizeChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/CastSizeChecker.cpp @@ -12,11 +12,11 @@ // //===----------------------------------------------------------------------===// #include "ClangSACheckers.h" +#include "clang/AST/CharUnits.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.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/AST/CharUnits.h" using namespace clang; using namespace ento; diff --git a/lib/StaticAnalyzer/Checkers/CastToStructChecker.cpp b/lib/StaticAnalyzer/Checkers/CastToStructChecker.cpp index d6d0e3c7b3b8..60348c73584b 100644 --- a/lib/StaticAnalyzer/Checkers/CastToStructChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/CastToStructChecker.cpp @@ -14,10 +14,10 @@ //===----------------------------------------------------------------------===// #include "ClangSACheckers.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.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; diff --git a/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp b/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp index 90872058af55..3f9b3cc7f805 100644 --- a/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp +++ b/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp @@ -14,14 +14,15 @@ //===----------------------------------------------------------------------===// #include "ClangSACheckers.h" -#include "clang/StaticAnalyzer/Core/Checker.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" -#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" -#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" -#include "clang/AST/ExprObjC.h" -#include "clang/AST/Expr.h" +#include "clang/AST/Attr.h" #include "clang/AST/DeclObjC.h" +#include "clang/AST/Expr.h" +#include "clang/AST/ExprObjC.h" #include "clang/Basic/LangOptions.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" +#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" #include "llvm/Support/raw_ostream.h" using namespace clang; diff --git a/lib/StaticAnalyzer/Checkers/CheckObjCInstMethSignature.cpp b/lib/StaticAnalyzer/Checkers/CheckObjCInstMethSignature.cpp index 6df47b1d9998..9cb1d2d6909b 100644 --- a/lib/StaticAnalyzer/Checkers/CheckObjCInstMethSignature.cpp +++ b/lib/StaticAnalyzer/Checkers/CheckObjCInstMethSignature.cpp @@ -14,13 +14,12 @@ //===----------------------------------------------------------------------===// #include "ClangSACheckers.h" -#include "clang/StaticAnalyzer/Core/Checker.h" -#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" -#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" +#include "clang/AST/ASTContext.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/Type.h" -#include "clang/AST/ASTContext.h" - +#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" +#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" +#include "clang/StaticAnalyzer/Core/Checker.h" #include "llvm/ADT/DenseMap.h" #include "llvm/Support/raw_ostream.h" diff --git a/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp b/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp index 5cd61941841d..7ef13ab53865 100644 --- a/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp +++ b/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp @@ -12,11 +12,11 @@ //===----------------------------------------------------------------------===// #include "ClangSACheckers.h" -#include "clang/Analysis/AnalysisContext.h" #include "clang/AST/StmtVisitor.h" +#include "clang/Analysis/AnalysisContext.h" #include "clang/Basic/TargetInfo.h" -#include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" +#include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringSwitch.h" @@ -36,13 +36,6 @@ 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; diff --git a/lib/StaticAnalyzer/Checkers/CheckSizeofPointer.cpp b/lib/StaticAnalyzer/Checkers/CheckSizeofPointer.cpp index cc7fd37ff602..f2c50501a65c 100644 --- a/lib/StaticAnalyzer/Checkers/CheckSizeofPointer.cpp +++ b/lib/StaticAnalyzer/Checkers/CheckSizeofPointer.cpp @@ -14,8 +14,8 @@ #include "ClangSACheckers.h" #include "clang/AST/StmtVisitor.h" -#include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" +#include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" using namespace clang; diff --git a/lib/StaticAnalyzer/Checkers/CheckerDocumentation.cpp b/lib/StaticAnalyzer/Checkers/CheckerDocumentation.cpp index efaec2b3f1e3..a9dd19a395c5 100644 --- a/lib/StaticAnalyzer/Checkers/CheckerDocumentation.cpp +++ b/lib/StaticAnalyzer/Checkers/CheckerDocumentation.cpp @@ -13,10 +13,10 @@ //===----------------------------------------------------------------------===// #include "ClangSACheckers.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.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; @@ -44,13 +44,15 @@ class CheckerDocumentation : public Checker< check::PreStmt<ReturnStmt>, check::Location, check::Bind, check::DeadSymbols, - check::EndPath, + check::EndFunction, check::EndAnalysis, check::EndOfTranslationUnit, eval::Call, eval::Assume, check::LiveSymbols, check::RegionChanges, + check::PointerEscape, + check::ConstPointerEscape, check::Event<ImplicitNullDerefEvent>, check::ASTDecl<FunctionDecl> > { public: @@ -152,11 +154,11 @@ public: /// check::DeadSymbols void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const {} - /// \brief Called when the analyzer core reaches the end of the top-level + /// \brief Called when the analyzer core reaches the end of a /// function being analyzed. /// - /// check::EndPath - void checkEndPath(CheckerContext &Ctx) const {} + /// check::EndFunction + void checkEndFunction(CheckerContext &Ctx) const {} /// \brief Called after all the paths in the ExplodedGraph reach end of path /// - the symbolic execution graph is fully explored. @@ -246,13 +248,44 @@ public: /// check::RegionChanges ProgramStateRef checkRegionChanges(ProgramStateRef State, - const StoreManager::InvalidatedSymbols *Invalidated, + const InvalidatedSymbols *Invalidated, ArrayRef<const MemRegion *> ExplicitRegions, ArrayRef<const MemRegion *> Regions, const CallEvent *Call) const { return State; } + /// \brief Called when pointers escape. + /// + /// This notifies the checkers about pointer escape, which occurs whenever + /// the analyzer cannot track the symbol any more. For example, as a + /// result of assigning a pointer into a global or when it's passed to a + /// function call the analyzer cannot model. + /// + /// \param State The state at the point of escape. + /// \param Escaped The list of escaped symbols. + /// \param Call The corresponding CallEvent, if the symbols escape as + /// parameters to the given call. + /// \param Kind How the symbols have escaped. + /// \returns Checkers can modify the state by returning a new state. + ProgramStateRef checkPointerEscape(ProgramStateRef State, + const InvalidatedSymbols &Escaped, + const CallEvent *Call, + PointerEscapeKind Kind) const { + return State; + } + + /// \brief Called when const pointers escape. + /// + /// Note: in most cases checkPointerEscape callback is sufficient. + /// \sa checkPointerEscape + ProgramStateRef checkConstPointerEscape(ProgramStateRef State, + const InvalidatedSymbols &Escaped, + const CallEvent *Call, + PointerEscapeKind Kind) const { + return State; + } + /// check::Event<ImplicitNullDerefEvent> void checkEvent(ImplicitNullDerefEvent Event) const {} diff --git a/lib/StaticAnalyzer/Checkers/Checkers.td b/lib/StaticAnalyzer/Checkers/Checkers.td index 235e63306f04..3db3fb9962a5 100644 --- a/lib/StaticAnalyzer/Checkers/Checkers.td +++ b/lib/StaticAnalyzer/Checkers/Checkers.td @@ -60,9 +60,9 @@ def CallAndMessageChecker : Checker<"CallAndMessage">, HelpText<"Check for logical errors for function calls and Objective-C message expressions (e.g., uninitialized arguments, null function pointers)">, DescFile<"CallAndMessageChecker.cpp">; -def AttrNonNullChecker : Checker<"AttributeNonNull">, - HelpText<"Check for null pointers passed as arguments to a function whose arguments are marked with the 'nonnull' attribute">, - DescFile<"AttrNonNullChecker.cpp">; +def NonNullParamChecker : Checker<"NonNullParamChecker">, + HelpText<"Check for null pointers passed as arguments to a function whose arguments are references or marked with the 'nonnull' attribute">, + DescFile<"NonNullParamChecker.cpp">; def VLASizeChecker : Checker<"VLASize">, HelpText<"Check for declarations of VLA of undefined or zero size">, @@ -166,12 +166,19 @@ def ReturnUndefChecker : Checker<"UndefReturn">, // C++ checkers. //===----------------------------------------------------------------------===// +let ParentPackage = Cplusplus in { +} // end: "cplusplus" + let ParentPackage = CplusplusAlpha in { def VirtualCallChecker : Checker<"VirtualCall">, HelpText<"Check virtual function calls during construction or destruction">, DescFile<"VirtualCallChecker.cpp">; +def NewDeleteChecker : Checker<"NewDelete">, + HelpText<"Check for memory leaks, double free, and use-after-free problems. Traces memory managed by new/delete.">, + DescFile<"MallocChecker.cpp">; + } // end: "alpha.cplusplus" //===----------------------------------------------------------------------===// @@ -276,12 +283,16 @@ def UnixAPIChecker : Checker<"API">, DescFile<"UnixAPIChecker.cpp">; def MallocPessimistic : Checker<"Malloc">, - HelpText<"Check for memory leaks, double free, and use-after-free problems.">, + HelpText<"Check for memory leaks, double free, and use-after-free problems. Traces memory managed by malloc()/free().">, DescFile<"MallocChecker.cpp">; def MallocSizeofChecker : Checker<"MallocSizeof">, HelpText<"Check for dubious malloc arguments involving sizeof">, DescFile<"MallocSizeofChecker.cpp">; + +def MismatchedDeallocatorChecker : Checker<"MismatchedDeallocator">, + HelpText<"Check for mismatched deallocators.">, + DescFile<"MallocChecker.cpp">; } // end "unix" @@ -292,7 +303,7 @@ def ChrootChecker : Checker<"Chroot">, DescFile<"ChrootChecker.cpp">; 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.">, + HelpText<"Check for memory leaks, double free, and use-after-free problems. Traces memory managed by malloc()/free(). Assumes that all user-defined functions which might free a pointer are annotated.">, DescFile<"MallocChecker.cpp">; def PthreadLockChecker : Checker<"PthreadLock">, @@ -343,7 +354,7 @@ let ParentPackage = OSX in { def MacOSXAPIChecker : Checker<"API">, InPackage<OSX>, - HelpText<"Check for proper uses of various Mac OS X APIs">, + HelpText<"Check for proper uses of various Apple APIs">, DescFile<"MacOSXAPIChecker.cpp">; def MacOSKeychainAPIChecker : Checker<"SecKeychainAPI">, @@ -351,7 +362,7 @@ def MacOSKeychainAPIChecker : Checker<"SecKeychainAPI">, HelpText<"Check for proper uses of Secure Keychain APIs">, DescFile<"MacOSKeychainAPIChecker.cpp">; -} // end "macosx" +} // end "osx" let ParentPackage = Cocoa in { @@ -412,12 +423,20 @@ def ObjCDeallocChecker : Checker<"Dealloc">, HelpText<"Warn about Objective-C classes that lack a correct implementation of -dealloc">, DescFile<"CheckObjCDealloc.cpp">; -def IvarInvalidationChecker : Checker<"InstanceVariableInvalidation">, +def InstanceVariableInvalidation : Checker<"InstanceVariableInvalidation">, HelpText<"Check that the invalidatable instance variables are invalidated in the methods annotated with objc_instance_variable_invalidator">, DescFile<"IvarInvalidationChecker.cpp">; +def MissingInvalidationMethod : Checker<"MissingInvalidationMethod">, + HelpText<"Check that the invalidation methods are present in classes that contain invalidatable instance variables">, + DescFile<"IvarInvalidationChecker.cpp">; + def DirectIvarAssignment : Checker<"DirectIvarAssignment">, - HelpText<"Check that the invalidatable instance variables are invalidated in the methods annotated with objc_instance_variable_invalidator">, + HelpText<"Check for direct assignments to instance variables">, + DescFile<"DirectIvarAssignment.cpp">; + +def DirectIvarAssignmentForAnnotatedFunctions : Checker<"DirectIvarAssignmentForAnnotatedFunctions">, + HelpText<"Check for direct assignments to instance variables in the methods annotated with objc_no_direct_instance_variable_assignment">, DescFile<"DirectIvarAssignment.cpp">; def ObjCSuperCallChecker : Checker<"MissingSuperCall">, @@ -515,4 +534,3 @@ def ExprInspectionChecker : Checker<"ExprInspection">, DescFile<"ExprInspectionChecker.cpp">; } // end "debug" - diff --git a/lib/StaticAnalyzer/Checkers/ChrootChecker.cpp b/lib/StaticAnalyzer/Checkers/ChrootChecker.cpp index c8856162fe89..991296538a5b 100644 --- a/lib/StaticAnalyzer/Checkers/ChrootChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/ChrootChecker.cpp @@ -12,10 +12,10 @@ //===----------------------------------------------------------------------===// #include "ClangSACheckers.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.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/ProgramState.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h" diff --git a/lib/StaticAnalyzer/Checkers/ClangSACheckers.h b/lib/StaticAnalyzer/Checkers/ClangSACheckers.h index 230baa759c5a..bea908dfa687 100644 --- a/lib/StaticAnalyzer/Checkers/ClangSACheckers.h +++ b/lib/StaticAnalyzer/Checkers/ClangSACheckers.h @@ -12,11 +12,11 @@ // //===----------------------------------------------------------------------===// -#include "clang/StaticAnalyzer/Checkers/CommonBugCategories.h" - #ifndef LLVM_CLANG_SA_LIB_CHECKERS_CLANGSACHECKERS_H #define LLVM_CLANG_SA_LIB_CHECKERS_CLANGSACHECKERS_H +#include "clang/StaticAnalyzer/Checkers/CommonBugCategories.h" + namespace clang { namespace ento { diff --git a/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp b/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp index 59e03ecd5c61..f2e3e6d7815e 100644 --- a/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp @@ -14,6 +14,7 @@ #include "ClangSACheckers.h" #include "clang/AST/ASTContext.h" +#include "clang/AST/Attr.h" #include "clang/AST/ParentMap.h" #include "clang/AST/RecursiveASTVisitor.h" #include "clang/Analysis/Analyses/LiveVariables.h" @@ -125,7 +126,7 @@ class DeadStoreObs : public LiveVariables::Observer { llvm::SmallPtrSet<const VarDecl*, 20> Escaped; OwningPtr<ReachableCode> reachableCode; const CFGBlock *currentBlock; - llvm::OwningPtr<llvm::DenseSet<const VarDecl *> > InEH; + OwningPtr<llvm::DenseSet<const VarDecl *> > InEH; enum DeadStoreKind { Standard, Enclosing, DeadIncrement, DeadInit }; @@ -418,6 +419,15 @@ class DeadStoresChecker : public Checker<check::ASTCodeBody> { public: void checkASTCodeBody(const Decl *D, AnalysisManager& mgr, BugReporter &BR) const { + + // Don't do anything for template instantiations. + // Proving that code in a template instantiation is "dead" + // means proving that it is dead in all instantiations. + // This same problem exists with -Wunreachable-code. + if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) + if (FD->isTemplateInstantiation()) + return; + if (LiveVariables *L = mgr.getAnalysis<LiveVariables>(D)) { CFG &cfg = *mgr.getCFG(D); AnalysisDeclContext *AC = mgr.getAnalysisDeclContext(D); diff --git a/lib/StaticAnalyzer/Checkers/DebugCheckers.cpp b/lib/StaticAnalyzer/Checkers/DebugCheckers.cpp index 7ad9c59a1bb2..29b4a637cda4 100644 --- a/lib/StaticAnalyzer/Checkers/DebugCheckers.cpp +++ b/lib/StaticAnalyzer/Checkers/DebugCheckers.cpp @@ -12,11 +12,11 @@ //===----------------------------------------------------------------------===// #include "ClangSACheckers.h" -#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/Analyses/LiveVariables.h" #include "clang/Analysis/CallGraph.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" #include "llvm/Support/Process.h" using namespace clang; diff --git a/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp b/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp index 3ace4be44804..72d46c50e109 100644 --- a/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp @@ -14,11 +14,12 @@ #include "ClangSACheckers.h" #include "clang/AST/ExprObjC.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.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" +#include "llvm/Support/raw_ostream.h" using namespace clang; using namespace ento; @@ -75,6 +76,14 @@ DereferenceChecker::AddDerefSource(raw_ostream &os, Ranges.push_back(SourceRange(L, L)); break; } + case Stmt::ObjCIvarRefExprClass: { + const ObjCIvarRefExpr *IV = cast<ObjCIvarRefExpr>(Ex); + os << " (" << (loadedFrom ? "loaded from" : "via") + << " ivar '" << IV->getDecl()->getName() << "')"; + SourceLocation L = IV->getLocation(); + Ranges.push_back(SourceRange(L, L)); + break; + } } } @@ -156,7 +165,7 @@ void DereferenceChecker::reportBug(ProgramStateRef State, const Stmt *S, buf.empty() ? BT_null->getDescription() : buf.str(), N); - bugreporter::trackNullOrUndefValue(N, bugreporter::GetDerefExpr(N), *report); + bugreporter::trackNullOrUndefValue(N, bugreporter::getDerefExpr(S), *report); for (SmallVectorImpl<SourceRange>::iterator I = Ranges.begin(), E = Ranges.end(); I!=E; ++I) @@ -175,17 +184,17 @@ void DereferenceChecker::checkLocation(SVal l, bool isLoad, const Stmt* S, BugReport *report = new BugReport(*BT_undef, BT_undef->getDescription(), N); - bugreporter::trackNullOrUndefValue(N, bugreporter::GetDerefExpr(N), + bugreporter::trackNullOrUndefValue(N, bugreporter::getDerefExpr(S), *report); C.emitReport(report); } return; } - DefinedOrUnknownSVal location = cast<DefinedOrUnknownSVal>(l); + DefinedOrUnknownSVal location = l.castAs<DefinedOrUnknownSVal>(); // Check for null dereferences. - if (!isa<Loc>(location)) + if (!location.getAs<Loc>()) return; ProgramStateRef state = C.getState(); @@ -230,7 +239,8 @@ void DereferenceChecker::checkBind(SVal L, SVal V, const Stmt *S, ProgramStateRef State = C.getState(); ProgramStateRef StNonNull, StNull; - llvm::tie(StNonNull, StNull) = State->assume(cast<DefinedOrUnknownSVal>(V)); + llvm::tie(StNonNull, StNull) = + State->assume(V.castAs<DefinedOrUnknownSVal>()); if (StNull) { if (!StNonNull) { diff --git a/lib/StaticAnalyzer/Checkers/DirectIvarAssignment.cpp b/lib/StaticAnalyzer/Checkers/DirectIvarAssignment.cpp index dc90b67e20fa..6d3dd1e42f02 100644 --- a/lib/StaticAnalyzer/Checkers/DirectIvarAssignment.cpp +++ b/lib/StaticAnalyzer/Checkers/DirectIvarAssignment.cpp @@ -7,18 +7,27 @@ // //===----------------------------------------------------------------------===// // -// Check that Objective C properties follow the following rules: -// - The property should be set with the setter, not though a direct -// assignment. +// Check that Objective C properties are set with the setter, not though a +// direct assignment. +// +// Two versions of a checker exist: one that checks all methods and the other +// that only checks the methods annotated with +// __attribute__((annotate("objc_no_direct_instance_variable_assignment"))) +// +// The checker does not warn about assignments to Ivars, annotated with +// __attribute__((objc_allow_direct_instance_variable_assignment"))). This +// annotation serves as a false positive suppression mechanism for the +// checker. The annotation is allowed on properties and Ivars. // //===----------------------------------------------------------------------===// #include "ClangSACheckers.h" -#include "clang/StaticAnalyzer/Core/Checker.h" -#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" +#include "clang/AST/Attr.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/StmtVisitor.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" #include "llvm/ADT/DenseMap.h" using namespace clang; @@ -26,6 +35,27 @@ using namespace ento; namespace { +/// The default method filter, which is used to filter out the methods on which +/// the check should not be performed. +/// +/// Checks for the init, dealloc, and any other functions that might be allowed +/// to perform direct instance variable assignment based on their name. +struct MethodFilter { + virtual ~MethodFilter() {} + virtual bool operator()(ObjCMethodDecl *M) { + if (M->getMethodFamily() == OMF_init || + M->getMethodFamily() == OMF_dealloc || + M->getMethodFamily() == OMF_copy || + M->getMethodFamily() == OMF_mutableCopy || + M->getSelector().getNameForSlot(0).find("init") != StringRef::npos || + M->getSelector().getNameForSlot(0).find("Init") != StringRef::npos) + return true; + return false; + } +}; + +static MethodFilter DefaultMethodFilter; + class DirectIvarAssignment : public Checker<check::ASTDecl<ObjCImplementationDecl> > { @@ -59,6 +89,10 @@ class DirectIvarAssignment : }; public: + MethodFilter *ShouldSkipMethod; + + DirectIvarAssignment() : ShouldSkipMethod(&DefaultMethodFilter) {} + void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager& Mgr, BugReporter &BR) const; }; @@ -118,14 +152,7 @@ void DirectIvarAssignment::checkASTDecl(const ObjCImplementationDecl *D, ObjCMethodDecl *M = *I; AnalysisDeclContext *DCtx = Mgr.getAnalysisDeclContext(M); - // Skip the init, dealloc functions and any functions that might be doing - // initialization based on their name. - if (M->getMethodFamily() == OMF_init || - M->getMethodFamily() == OMF_dealloc || - M->getMethodFamily() == OMF_copy || - M->getMethodFamily() == OMF_mutableCopy || - M->getSelector().getNameForSlot(0).find("init") != StringRef::npos || - M->getSelector().getNameForSlot(0).find("Init") != StringRef::npos) + if ((*ShouldSkipMethod)(M)) continue; const Stmt *Body = M->getBody(); @@ -136,6 +163,18 @@ void DirectIvarAssignment::checkASTDecl(const ObjCImplementationDecl *D, } } +static bool isAnnotatedToAllowDirectAssignment(const Decl *D) { + for (specific_attr_iterator<AnnotateAttr> + AI = D->specific_attr_begin<AnnotateAttr>(), + AE = D->specific_attr_end<AnnotateAttr>(); AI != AE; ++AI) { + const AnnotateAttr *Ann = *AI; + if (Ann->getAnnotation() == + "objc_allow_direct_instance_variable_assignment") + return true; + } + return false; +} + void DirectIvarAssignment::MethodCrawler::VisitBinaryOperator( const BinaryOperator *BO) { if (!BO->isAssignmentOp()) @@ -149,8 +188,16 @@ void DirectIvarAssignment::MethodCrawler::VisitBinaryOperator( if (const ObjCIvarDecl *D = IvarRef->getDecl()) { IvarToPropertyMapTy::const_iterator I = IvarToPropMap.find(D); + if (I != IvarToPropMap.end()) { const ObjCPropertyDecl *PD = I->second; + // Skip warnings on Ivars, annotated with + // objc_allow_direct_instance_variable_assignment. This annotation serves + // as a false positive suppression mechanism for the checker. The + // annotation is allowed on properties and ivars. + if (isAnnotatedToAllowDirectAssignment(PD) || + isAnnotatedToAllowDirectAssignment(D)) + return; ObjCMethodDecl *GetterMethod = InterfD->getInstanceMethod(PD->getGetterName()); @@ -175,6 +222,33 @@ void DirectIvarAssignment::MethodCrawler::VisitBinaryOperator( } } +// Register the checker that checks for direct accesses in all functions, +// except for the initialization and copy routines. void ento::registerDirectIvarAssignment(CheckerManager &mgr) { mgr.registerChecker<DirectIvarAssignment>(); } + +// Register the checker that checks for direct accesses in functions annotated +// with __attribute__((annotate("objc_no_direct_instance_variable_assignment"))). +namespace { +struct InvalidatorMethodFilter : MethodFilter { + virtual ~InvalidatorMethodFilter() {} + virtual bool operator()(ObjCMethodDecl *M) { + for (specific_attr_iterator<AnnotateAttr> + AI = M->specific_attr_begin<AnnotateAttr>(), + AE = M->specific_attr_end<AnnotateAttr>(); AI != AE; ++AI) { + const AnnotateAttr *Ann = *AI; + if (Ann->getAnnotation() == "objc_no_direct_instance_variable_assignment") + return false; + } + return true; + } +}; + +InvalidatorMethodFilter AttrFilter; +} + +void ento::registerDirectIvarAssignmentForAnnotatedFunctions( + CheckerManager &mgr) { + mgr.registerChecker<DirectIvarAssignment>()->ShouldSkipMethod = &AttrFilter; +} diff --git a/lib/StaticAnalyzer/Checkers/DivZeroChecker.cpp b/lib/StaticAnalyzer/Checkers/DivZeroChecker.cpp index 76fb3f2b288e..93daf94fbe32 100644 --- a/lib/StaticAnalyzer/Checkers/DivZeroChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/DivZeroChecker.cpp @@ -13,10 +13,10 @@ //===----------------------------------------------------------------------===// #include "ClangSACheckers.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.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; @@ -58,7 +58,7 @@ void DivZeroChecker::checkPreStmt(const BinaryOperator *B, return; SVal Denom = C.getState()->getSVal(B->getRHS(), C.getLocationContext()); - const DefinedSVal *DV = dyn_cast<DefinedSVal>(&Denom); + Optional<DefinedSVal> DV = Denom.getAs<DefinedSVal>(); // Divide-by-undefined handled in the generic checking for uses of // undefined values. diff --git a/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp b/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp index b0a4bc67485e..9f176a4b5bf7 100644 --- a/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp +++ b/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp @@ -12,13 +12,13 @@ //===----------------------------------------------------------------------===// #include "ClangSACheckers.h" +#include "clang/Basic/Builtins.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.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" using namespace clang; using namespace ento; @@ -110,38 +110,40 @@ void DynamicTypePropagation::checkPostCall(const CallEvent &Call, return; ProgramStateRef State = C.getState(); - - switch (Msg->getMethodFamily()) { - default: - break; - - // We assume that the type of the object returned by alloc and new are the - // pointer to the object of the class specified in the receiver of the - // message. - case OMF_alloc: - case OMF_new: { - // Get the type of object that will get created. - const ObjCMessageExpr *MsgE = Msg->getOriginExpr(); - const ObjCObjectType *ObjTy = getObjectTypeForAllocAndNew(MsgE, C); - if (!ObjTy) - return; - QualType DynResTy = + const ObjCMethodDecl *D = Msg->getDecl(); + + if (D && D->hasRelatedResultType()) { + switch (Msg->getMethodFamily()) { + default: + break; + + // We assume that the type of the object returned by alloc and new are the + // pointer to the object of the class specified in the receiver of the + // message. + case OMF_alloc: + case OMF_new: { + // Get the type of object that will get created. + const ObjCMessageExpr *MsgE = Msg->getOriginExpr(); + const ObjCObjectType *ObjTy = getObjectTypeForAllocAndNew(MsgE, C); + if (!ObjTy) + return; + QualType DynResTy = C.getASTContext().getObjCObjectPointerType(QualType(ObjTy, 0)); - C.addTransition(State->setDynamicTypeInfo(RetReg, DynResTy, false)); - break; - } - case OMF_init: { - // Assume, the result of the init method has the same dynamic type as - // the receiver and propagate the dynamic type info. - const MemRegion *RecReg = Msg->getReceiverSVal().getAsRegion(); - if (!RecReg) - return; - DynamicTypeInfo RecDynType = State->getDynamicTypeInfo(RecReg); - C.addTransition(State->setDynamicTypeInfo(RetReg, RecDynType)); - break; - } + C.addTransition(State->setDynamicTypeInfo(RetReg, DynResTy, false)); + break; + } + case OMF_init: { + // Assume, the result of the init method has the same dynamic type as + // the receiver and propagate the dynamic type info. + const MemRegion *RecReg = Msg->getReceiverSVal().getAsRegion(); + if (!RecReg) + return; + DynamicTypeInfo RecDynType = State->getDynamicTypeInfo(RecReg); + C.addTransition(State->setDynamicTypeInfo(RetReg, RecDynType)); + break; + } + } } - return; } diff --git a/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp b/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp index e7e316281faa..810473f1a6e0 100644 --- a/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp @@ -8,9 +8,10 @@ //===----------------------------------------------------------------------===// #include "ClangSACheckers.h" -#include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "llvm/ADT/StringSwitch.h" using namespace clang; using namespace ento; @@ -64,7 +65,7 @@ static const char *getArgumentValueString(const CallExpr *CE, ProgramStateRef StTrue, StFalse; llvm::tie(StTrue, StFalse) = - State->assume(cast<DefinedOrUnknownSVal>(AssertionVal)); + State->assume(AssertionVal.castAs<DefinedOrUnknownSVal>()); if (StTrue) { if (StFalse) diff --git a/lib/StaticAnalyzer/Checkers/FixedAddressChecker.cpp b/lib/StaticAnalyzer/Checkers/FixedAddressChecker.cpp index 7fde68923124..085a991f7866 100644 --- a/lib/StaticAnalyzer/Checkers/FixedAddressChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/FixedAddressChecker.cpp @@ -14,10 +14,10 @@ //===----------------------------------------------------------------------===// #include "ClangSACheckers.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.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; diff --git a/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp b/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp index a9e02173c3a9..c67c597feced 100644 --- a/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp @@ -15,12 +15,13 @@ // //===----------------------------------------------------------------------===// #include "ClangSACheckers.h" +#include "clang/AST/Attr.h" +#include "clang/Basic/Builtins.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.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; @@ -102,7 +103,7 @@ private: CheckerContext &C) const; - typedef llvm::SmallVector<unsigned, 2> ArgVector; + typedef SmallVector<unsigned, 2> ArgVector; /// \brief A struct used to specify taint propagation rules for a function. /// @@ -430,7 +431,7 @@ SymbolRef GenericTaintChecker::getPointedToSymbol(CheckerContext &C, if (AddrVal.isUnknownOrUndef()) return 0; - Loc *AddrLoc = dyn_cast<Loc>(&AddrVal); + Optional<Loc> AddrLoc = AddrVal.getAs<Loc>(); if (!AddrLoc) return 0; diff --git a/lib/StaticAnalyzer/Checkers/IdempotentOperationChecker.cpp b/lib/StaticAnalyzer/Checkers/IdempotentOperationChecker.cpp index ffbbb8b68d8a..271ba4702c57 100644 --- a/lib/StaticAnalyzer/Checkers/IdempotentOperationChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/IdempotentOperationChecker.cpp @@ -43,23 +43,24 @@ // - Handling ~0 values #include "ClangSACheckers.h" -#include "clang/Analysis/CFGStmtMap.h" -#include "clang/Analysis/Analyses/PseudoConstantAnalysis.h" +#include "clang/AST/Stmt.h" #include "clang/Analysis/Analyses/CFGReachabilityAnalysis.h" +#include "clang/Analysis/Analyses/PseudoConstantAnalysis.h" +#include "clang/Analysis/CFGStmtMap.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" -#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" -#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" -#include "clang/AST/Stmt.h" +#include "llvm/ADT/BitVector.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" +#include "llvm/Support/raw_ostream.h" using namespace clang; using namespace ento; @@ -172,11 +173,11 @@ void IdempotentOperationChecker::checkPreStmt(const BinaryOperator *B, case BO_ShrAssign: case BO_Assign: // Assign statements have one extra level of indirection - if (!isa<Loc>(LHSVal)) { + if (!LHSVal.getAs<Loc>()) { A = Impossible; return; } - LHSVal = state->getSVal(cast<Loc>(LHSVal), LHS->getType()); + LHSVal = state->getSVal(LHSVal.castAs<Loc>(), LHS->getType()); } @@ -331,9 +332,9 @@ void IdempotentOperationChecker::checkPostStmt(const BinaryOperator *B, // Add the ExplodedNode we just visited BinaryOperatorData &Data = hash[B]; - const Stmt *predStmt - = cast<StmtPoint>(C.getPredecessor()->getLocation()).getStmt(); - + const Stmt *predStmt = + C.getPredecessor()->getLocation().castAs<StmtPoint>().getStmt(); + // Ignore implicit calls to setters. if (!isa<BinaryOperator>(predStmt)) return; @@ -422,12 +423,12 @@ void IdempotentOperationChecker::checkEndAnalysis(ExplodedGraph &G, if (LHSRelevant) { const Expr *LHS = i->first->getLHS(); report->addRange(LHS->getSourceRange()); - FindLastStoreBRVisitor::registerStatementVarDecls(*report, LHS); + FindLastStoreBRVisitor::registerStatementVarDecls(*report, LHS, false); } if (RHSRelevant) { const Expr *RHS = i->first->getRHS(); report->addRange(i->first->getRHS()->getSourceRange()); - FindLastStoreBRVisitor::registerStatementVarDecls(*report, RHS); + FindLastStoreBRVisitor::registerStatementVarDecls(*report, RHS, false); } BR.emitReport(report); @@ -581,16 +582,13 @@ IdempotentOperationChecker::pathWasCompletelyAnalyzed(AnalysisDeclContext *AC, virtual bool visit(const WorkListUnit &U) { ProgramPoint P = U.getNode()->getLocation(); const CFGBlock *B = 0; - if (StmtPoint *SP = dyn_cast<StmtPoint>(&P)) { + if (Optional<StmtPoint> SP = P.getAs<StmtPoint>()) { B = CBM->getBlock(SP->getStmt()); - } - else if (BlockEdge *BE = dyn_cast<BlockEdge>(&P)) { + } else if (Optional<BlockEdge> BE = P.getAs<BlockEdge>()) { B = BE->getDst(); - } - else if (BlockEntrance *BEnt = dyn_cast<BlockEntrance>(&P)) { + } else if (Optional<BlockEntrance> BEnt = P.getAs<BlockEntrance>()) { B = BEnt->getBlock(); - } - else if (BlockExit *BExit = dyn_cast<BlockExit>(&P)) { + } else if (Optional<BlockExit> BExit = P.getAs<BlockExit>()) { B = BExit->getBlock(); } if (!B) diff --git a/lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp b/lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp index bf256cd9fa45..5ed28e955d4e 100644 --- a/lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp @@ -20,25 +20,40 @@ // been called on them. An invalidation method should either invalidate all // the ivars or call another invalidation method (on self). // +// Partial invalidor annotation allows to addess cases when ivars are +// invalidated by other methods, which might or might not be called from +// the invalidation method. The checker checks that each invalidation +// method and all the partial methods cumulatively invalidate all ivars. +// __attribute__((annotate("objc_instance_variable_invalidator_partial"))); +// //===----------------------------------------------------------------------===// #include "ClangSACheckers.h" -#include "clang/StaticAnalyzer/Core/Checker.h" -#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" +#include "clang/AST/Attr.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/StmtVisitor.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" #include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/SetVector.h" #include "llvm/ADT/SmallString.h" using namespace clang; using namespace ento; namespace { -class IvarInvalidationChecker : - public Checker<check::ASTDecl<ObjCMethodDecl> > { - typedef llvm::DenseSet<const ObjCMethodDecl*> MethodSet; +struct ChecksFilter { + /// Check for missing invalidation method declarations. + DefaultBool check_MissingInvalidationMethod; + /// Check that all ivars are invalidated. + DefaultBool check_InstanceVariableInvalidation; +}; + +class IvarInvalidationCheckerImpl { + + typedef llvm::SmallSetVector<const ObjCMethodDecl*, 2> MethodSet; typedef llvm::DenseMap<const ObjCMethodDecl*, const ObjCIvarDecl*> MethToIvarMapTy; typedef llvm::DenseMap<const ObjCPropertyDecl*, @@ -47,14 +62,14 @@ class IvarInvalidationChecker : const ObjCPropertyDecl*> IvarToPropMapTy; - struct IvarInfo { + struct InvalidationInfo { /// Has the ivar been invalidated? bool IsInvalidated; /// The methods which can be used to invalidate the ivar. MethodSet InvalidationMethods; - IvarInfo() : IsInvalidated(false) {} + InvalidationInfo() : IsInvalidated(false) {} void addInvalidationMethod(const ObjCMethodDecl *MD) { InvalidationMethods.insert(MD); } @@ -63,11 +78,7 @@ class IvarInvalidationChecker : return !InvalidationMethods.empty(); } - void markInvalidated() { - IsInvalidated = true; - } - - bool markInvalidated(const ObjCMethodDecl *MD) { + bool hasMethod(const ObjCMethodDecl *MD) { if (IsInvalidated) return true; for (MethodSet::iterator I = InvalidationMethods.begin(), @@ -79,13 +90,9 @@ class IvarInvalidationChecker : } return false; } - - bool isInvalidated() const { - return IsInvalidated; - } }; - typedef llvm::DenseMap<const ObjCIvarDecl*, IvarInfo> IvarSet; + typedef llvm::DenseMap<const ObjCIvarDecl*, InvalidationInfo> IvarSet; /// Statement visitor, which walks the method body and flags the ivars /// referenced in it (either directly or via property). @@ -168,12 +175,16 @@ class IvarInvalidationChecker : /// Check if the any of the methods inside the interface are annotated with /// the invalidation annotation, update the IvarInfo accordingly. + /// \param LookForPartial is set when we are searching for partial + /// invalidators. static void containsInvalidationMethod(const ObjCContainerDecl *D, - IvarInfo &Out); + InvalidationInfo &Out, + bool LookForPartial); /// Check if ivar should be tracked and add to TrackedIvars if positive. /// Returns true if ivar should be tracked. - static bool trackIvar(const ObjCIvarDecl *Iv, IvarSet &TrackedIvars); + static bool trackIvar(const ObjCIvarDecl *Iv, IvarSet &TrackedIvars, + const ObjCIvarDecl **FirstIvarDecl); /// Given the property declaration, and the list of tracked ivars, finds /// the ivar backing the property when possible. Returns '0' when no such @@ -181,54 +192,90 @@ class IvarInvalidationChecker : static const ObjCIvarDecl *findPropertyBackingIvar( const ObjCPropertyDecl *Prop, const ObjCInterfaceDecl *InterfaceD, - IvarSet &TrackedIvars); + IvarSet &TrackedIvars, + const ObjCIvarDecl **FirstIvarDecl); + + /// Print ivar name or the property if the given ivar backs a property. + static void printIvar(llvm::raw_svector_ostream &os, + const ObjCIvarDecl *IvarDecl, + const IvarToPropMapTy &IvarToPopertyMap); + + void reportNoInvalidationMethod(const ObjCIvarDecl *FirstIvarDecl, + const IvarToPropMapTy &IvarToPopertyMap, + const ObjCInterfaceDecl *InterfaceD, + bool MissingDeclaration) const; + void reportIvarNeedsInvalidation(const ObjCIvarDecl *IvarD, + const IvarToPropMapTy &IvarToPopertyMap, + const ObjCMethodDecl *MethodD) const; + + AnalysisManager& Mgr; + BugReporter &BR; + /// Filter on the checks performed. + const ChecksFilter &Filter; public: - void checkASTDecl(const ObjCMethodDecl *D, AnalysisManager& Mgr, - BugReporter &BR) const; + IvarInvalidationCheckerImpl(AnalysisManager& InMgr, + BugReporter &InBR, + const ChecksFilter &InFilter) : + Mgr (InMgr), BR(InBR), Filter(InFilter) {} - // TODO: We are currently ignoring the ivars coming from class extensions. + void visit(const ObjCImplementationDecl *D) const; }; -static bool isInvalidationMethod(const ObjCMethodDecl *M) { +static bool isInvalidationMethod(const ObjCMethodDecl *M, bool LookForPartial) { for (specific_attr_iterator<AnnotateAttr> AI = M->specific_attr_begin<AnnotateAttr>(), AE = M->specific_attr_end<AnnotateAttr>(); AI != AE; ++AI) { const AnnotateAttr *Ann = *AI; - if (Ann->getAnnotation() == "objc_instance_variable_invalidator") + if (!LookForPartial && + Ann->getAnnotation() == "objc_instance_variable_invalidator") + return true; + if (LookForPartial && + Ann->getAnnotation() == "objc_instance_variable_invalidator_partial") return true; } return false; } -void IvarInvalidationChecker::containsInvalidationMethod( - const ObjCContainerDecl *D, IvarInfo &OutInfo) { - - // TODO: Cache the results. +void IvarInvalidationCheckerImpl::containsInvalidationMethod( + const ObjCContainerDecl *D, InvalidationInfo &OutInfo, bool Partial) { if (!D) return; + assert(!isa<ObjCImplementationDecl>(D)); + // TODO: Cache the results. + // Check all methods. for (ObjCContainerDecl::method_iterator I = D->meth_begin(), E = D->meth_end(); I != E; ++I) { const ObjCMethodDecl *MDI = *I; - if (isInvalidationMethod(MDI)) + if (isInvalidationMethod(MDI, Partial)) OutInfo.addInvalidationMethod( cast<ObjCMethodDecl>(MDI->getCanonicalDecl())); } // If interface, check all parent protocols and super. - // TODO: Visit all categories in case the invalidation method is declared in - // a category. - if (const ObjCInterfaceDecl *InterfaceD = dyn_cast<ObjCInterfaceDecl>(D)) { + if (const ObjCInterfaceDecl *InterfD = dyn_cast<ObjCInterfaceDecl>(D)) { + + // Visit all protocols. for (ObjCInterfaceDecl::protocol_iterator - I = InterfaceD->protocol_begin(), - E = InterfaceD->protocol_end(); I != E; ++I) { - containsInvalidationMethod(*I, OutInfo); + I = InterfD->protocol_begin(), + E = InterfD->protocol_end(); I != E; ++I) { + containsInvalidationMethod((*I)->getDefinition(), OutInfo, Partial); + } + + // Visit all categories in case the invalidation method is declared in + // a category. + for (ObjCInterfaceDecl::visible_extensions_iterator + Ext = InterfD->visible_extensions_begin(), + ExtEnd = InterfD->visible_extensions_end(); + Ext != ExtEnd; ++Ext) { + containsInvalidationMethod(*Ext, OutInfo, Partial); } - containsInvalidationMethod(InterfaceD->getSuperClass(), OutInfo); + + containsInvalidationMethod(InterfD->getSuperClass(), OutInfo, Partial); return; } @@ -237,45 +284,52 @@ void IvarInvalidationChecker::containsInvalidationMethod( for (ObjCInterfaceDecl::protocol_iterator I = ProtD->protocol_begin(), E = ProtD->protocol_end(); I != E; ++I) { - containsInvalidationMethod(*I, OutInfo); + containsInvalidationMethod((*I)->getDefinition(), OutInfo, Partial); } return; } - llvm_unreachable("One of the casts above should have succeeded."); + return; } -bool IvarInvalidationChecker::trackIvar(const ObjCIvarDecl *Iv, - IvarSet &TrackedIvars) { +bool IvarInvalidationCheckerImpl::trackIvar(const ObjCIvarDecl *Iv, + IvarSet &TrackedIvars, + const ObjCIvarDecl **FirstIvarDecl) { QualType IvQTy = Iv->getType(); const ObjCObjectPointerType *IvTy = IvQTy->getAs<ObjCObjectPointerType>(); if (!IvTy) return false; const ObjCInterfaceDecl *IvInterf = IvTy->getInterfaceDecl(); - IvarInfo Info; - containsInvalidationMethod(IvInterf, Info); + InvalidationInfo Info; + containsInvalidationMethod(IvInterf, Info, /*LookForPartial*/ false); if (Info.needsInvalidation()) { - TrackedIvars[cast<ObjCIvarDecl>(Iv->getCanonicalDecl())] = Info; + const ObjCIvarDecl *I = cast<ObjCIvarDecl>(Iv->getCanonicalDecl()); + TrackedIvars[I] = Info; + if (!*FirstIvarDecl) + *FirstIvarDecl = I; return true; } return false; } -const ObjCIvarDecl *IvarInvalidationChecker::findPropertyBackingIvar( +const ObjCIvarDecl *IvarInvalidationCheckerImpl::findPropertyBackingIvar( const ObjCPropertyDecl *Prop, const ObjCInterfaceDecl *InterfaceD, - IvarSet &TrackedIvars) { + IvarSet &TrackedIvars, + const ObjCIvarDecl **FirstIvarDecl) { const ObjCIvarDecl *IvarD = 0; // Lookup for the synthesized case. IvarD = Prop->getPropertyIvarDecl(); - if (IvarD) { + // We only track the ivars/properties that are defined in the current + // class (not the parent). + if (IvarD && IvarD->getContainingInterface() == InterfaceD) { if (TrackedIvars.count(IvarD)) { return IvarD; } // If the ivar is synthesized we still want to track it. - if (trackIvar(IvarD, TrackedIvars)) + if (trackIvar(IvarD, TrackedIvars, FirstIvarDecl)) return IvarD; } @@ -304,22 +358,35 @@ const ObjCIvarDecl *IvarInvalidationChecker::findPropertyBackingIvar( return 0; } -void IvarInvalidationChecker::checkASTDecl(const ObjCMethodDecl *D, - AnalysisManager& Mgr, - BugReporter &BR) const { - // We are only interested in checking the cleanup methods. - if (!D->hasBody() || !isInvalidationMethod(D)) - return; +void IvarInvalidationCheckerImpl::printIvar(llvm::raw_svector_ostream &os, + const ObjCIvarDecl *IvarDecl, + const IvarToPropMapTy &IvarToPopertyMap) { + if (IvarDecl->getSynthesize()) { + const ObjCPropertyDecl *PD = IvarToPopertyMap.lookup(IvarDecl); + assert(PD &&"Do we synthesize ivars for something other than properties?"); + os << "Property "<< PD->getName() << " "; + } else { + os << "Instance variable "<< IvarDecl->getName() << " "; + } +} +// Check that the invalidatable interfaces with ivars/properties implement the +// invalidation methods. +void IvarInvalidationCheckerImpl:: +visit(const ObjCImplementationDecl *ImplD) const { // Collect all ivars that need cleanup. IvarSet Ivars; - const ObjCInterfaceDecl *InterfaceD = D->getClassInterface(); + // Record the first Ivar needing invalidation; used in reporting when only + // one ivar is sufficient. Cannot grab the first on the Ivars set to ensure + // deterministic output. + const ObjCIvarDecl *FirstIvarDecl = 0; + const ObjCInterfaceDecl *InterfaceD = ImplD->getClassInterface(); // Collect ivars declared in this class, its extensions and its implementation ObjCInterfaceDecl *IDecl = const_cast<ObjCInterfaceDecl *>(InterfaceD); for (const ObjCIvarDecl *Iv = IDecl->all_declared_ivar_begin(); Iv; Iv= Iv->getNextIvar()) - trackIvar(Iv, Ivars); + trackIvar(Iv, Ivars, &FirstIvarDecl); // Construct Property/Property Accessor to Ivar maps to assist checking if an // ivar which is backing a property has been reset. @@ -329,16 +396,17 @@ void IvarInvalidationChecker::checkASTDecl(const ObjCMethodDecl *D, IvarToPropMapTy IvarToPopertyMap; ObjCInterfaceDecl::PropertyMap PropMap; - InterfaceD->collectPropertiesToImplement(PropMap); + ObjCInterfaceDecl::PropertyDeclOrder PropOrder; + InterfaceD->collectPropertiesToImplement(PropMap, PropOrder); for (ObjCInterfaceDecl::PropertyMap::iterator I = PropMap.begin(), E = PropMap.end(); I != E; ++I) { const ObjCPropertyDecl *PD = I->second; - const ObjCIvarDecl *ID = findPropertyBackingIvar(PD, InterfaceD, Ivars); - if (!ID) { + const ObjCIvarDecl *ID = findPropertyBackingIvar(PD, InterfaceD, Ivars, + &FirstIvarDecl); + if (!ID) continue; - } // Store the mappings. PD = cast<ObjCPropertyDecl>(PD->getCanonicalDecl()); @@ -359,66 +427,159 @@ void IvarInvalidationChecker::checkASTDecl(const ObjCMethodDecl *D, } } + // If no ivars need invalidation, there is nothing to check here. + if (Ivars.empty()) + return; + + // Find all partial invalidation methods. + InvalidationInfo PartialInfo; + containsInvalidationMethod(InterfaceD, PartialInfo, /*LookForPartial*/ true); + + // Remove ivars invalidated by the partial invalidation methods. They do not + // need to be invalidated in the regular invalidation methods. + for (MethodSet::iterator + I = PartialInfo.InvalidationMethods.begin(), + E = PartialInfo.InvalidationMethods.end(); I != E; ++I) { + const ObjCMethodDecl *InterfD = *I; + + // Get the corresponding method in the @implementation. + const ObjCMethodDecl *D = ImplD->getMethod(InterfD->getSelector(), + InterfD->isInstanceMethod()); + if (D && D->hasBody()) { + bool CalledAnotherInvalidationMethod = false; + // The MethodCrowler is going to remove the invalidated ivars. + MethodCrawler(Ivars, + CalledAnotherInvalidationMethod, + PropSetterToIvarMap, + PropGetterToIvarMap, + PropertyToIvarMap, + BR.getContext()).VisitStmt(D->getBody()); + // If another invalidation method was called, trust that full invalidation + // has occurred. + if (CalledAnotherInvalidationMethod) + Ivars.clear(); + } + } - // Check which ivars have been invalidated in the method body. - bool CalledAnotherInvalidationMethod = false; - MethodCrawler(Ivars, - CalledAnotherInvalidationMethod, - PropSetterToIvarMap, - PropGetterToIvarMap, - PropertyToIvarMap, - BR.getContext()).VisitStmt(D->getBody()); + // If all ivars have been invalidated by partial invalidators, there is + // nothing to check here. + if (Ivars.empty()) + return; - if (CalledAnotherInvalidationMethod) + // Find all invalidation methods in this @interface declaration and parents. + InvalidationInfo Info; + containsInvalidationMethod(InterfaceD, Info, /*LookForPartial*/ false); + + // Report an error in case none of the invalidation methods are declared. + if (!Info.needsInvalidation()) { + if (Filter.check_MissingInvalidationMethod) + reportNoInvalidationMethod(FirstIvarDecl, IvarToPopertyMap, InterfaceD, + /*MissingDeclaration*/ true); + // If there are no invalidation methods, there is no ivar validation work + // to be done. return; + } - // Warn on the ivars that were not accessed by the method. - for (IvarSet::const_iterator I = Ivars.begin(), E = Ivars.end(); I != E; ++I){ - if (!I->second.isInvalidated()) { - const ObjCIvarDecl *IvarDecl = I->first; - - PathDiagnosticLocation IvarDecLocation = - PathDiagnosticLocation::createEnd(D->getBody(), BR.getSourceManager(), - Mgr.getAnalysisDeclContext(D)); - - SmallString<128> sbuf; - llvm::raw_svector_ostream os(sbuf); - - // Construct the warning message. - if (IvarDecl->getSynthesize()) { - const ObjCPropertyDecl *PD = IvarToPopertyMap[IvarDecl]; - assert(PD && - "Do we synthesize ivars for something other than properties?"); - os << "Property "<< PD->getName() << - " needs to be invalidated or set to nil"; - } else { - os << "Instance variable "<< IvarDecl->getName() - << " needs to be invalidated or set to nil"; - } + // Only check if Ivars are invalidated when InstanceVariableInvalidation + // has been requested. + if (!Filter.check_InstanceVariableInvalidation) + return; - BR.EmitBasicReport(D, - "Incomplete invalidation", - categories::CoreFoundationObjectiveC, os.str(), - IvarDecLocation); + // Check that all ivars are invalidated by the invalidation methods. + bool AtImplementationContainsAtLeastOneInvalidationMethod = false; + for (MethodSet::iterator I = Info.InvalidationMethods.begin(), + E = Info.InvalidationMethods.end(); I != E; ++I) { + const ObjCMethodDecl *InterfD = *I; + + // Get the corresponding method in the @implementation. + const ObjCMethodDecl *D = ImplD->getMethod(InterfD->getSelector(), + InterfD->isInstanceMethod()); + if (D && D->hasBody()) { + AtImplementationContainsAtLeastOneInvalidationMethod = true; + + // Get a copy of ivars needing invalidation. + IvarSet IvarsI = Ivars; + + bool CalledAnotherInvalidationMethod = false; + MethodCrawler(IvarsI, + CalledAnotherInvalidationMethod, + PropSetterToIvarMap, + PropGetterToIvarMap, + PropertyToIvarMap, + BR.getContext()).VisitStmt(D->getBody()); + // If another invalidation method was called, trust that full invalidation + // has occurred. + if (CalledAnotherInvalidationMethod) + continue; + + // Warn on the ivars that were not invalidated by the method. + for (IvarSet::const_iterator + I = IvarsI.begin(), E = IvarsI.end(); I != E; ++I) + reportIvarNeedsInvalidation(I->first, IvarToPopertyMap, D); } } + + // Report an error in case none of the invalidation methods are implemented. + if (!AtImplementationContainsAtLeastOneInvalidationMethod) + reportNoInvalidationMethod(FirstIvarDecl, IvarToPopertyMap, InterfaceD, + /*MissingDeclaration*/ false); } -void IvarInvalidationChecker::MethodCrawler::markInvalidated( +void IvarInvalidationCheckerImpl:: +reportNoInvalidationMethod(const ObjCIvarDecl *FirstIvarDecl, + const IvarToPropMapTy &IvarToPopertyMap, + const ObjCInterfaceDecl *InterfaceD, + bool MissingDeclaration) const { + SmallString<128> sbuf; + llvm::raw_svector_ostream os(sbuf); + assert(FirstIvarDecl); + printIvar(os, FirstIvarDecl, IvarToPopertyMap); + os << "needs to be invalidated; "; + if (MissingDeclaration) + os << "no invalidation method is declared for "; + else + os << "no invalidation method is defined in the @implementation for "; + os << InterfaceD->getName(); + + PathDiagnosticLocation IvarDecLocation = + PathDiagnosticLocation::createBegin(FirstIvarDecl, BR.getSourceManager()); + + BR.EmitBasicReport(FirstIvarDecl, "Incomplete invalidation", + categories::CoreFoundationObjectiveC, os.str(), + IvarDecLocation); +} + +void IvarInvalidationCheckerImpl:: +reportIvarNeedsInvalidation(const ObjCIvarDecl *IvarD, + const IvarToPropMapTy &IvarToPopertyMap, + const ObjCMethodDecl *MethodD) const { + SmallString<128> sbuf; + llvm::raw_svector_ostream os(sbuf); + printIvar(os, IvarD, IvarToPopertyMap); + os << "needs to be invalidated or set to nil"; + PathDiagnosticLocation MethodDecLocation = + PathDiagnosticLocation::createEnd(MethodD->getBody(), + BR.getSourceManager(), + Mgr.getAnalysisDeclContext(MethodD)); + BR.EmitBasicReport(MethodD, "Incomplete invalidation", + categories::CoreFoundationObjectiveC, os.str(), + MethodDecLocation); +} + +void IvarInvalidationCheckerImpl::MethodCrawler::markInvalidated( const ObjCIvarDecl *Iv) { IvarSet::iterator I = IVars.find(Iv); if (I != IVars.end()) { // If InvalidationMethod is present, we are processing the message send and // should ensure we are invalidating with the appropriate method, // otherwise, we are processing setting to 'nil'. - if (InvalidationMethod) - I->second.markInvalidated(InvalidationMethod); - else - I->second.markInvalidated(); + if (!InvalidationMethod || + (InvalidationMethod && I->second.hasMethod(InvalidationMethod))) + IVars.erase(I); } } -const Expr *IvarInvalidationChecker::MethodCrawler::peel(const Expr *E) const { +const Expr *IvarInvalidationCheckerImpl::MethodCrawler::peel(const Expr *E) const { E = E->IgnoreParenCasts(); if (const PseudoObjectExpr *POE = dyn_cast<PseudoObjectExpr>(E)) E = POE->getSyntacticForm()->IgnoreParenCasts(); @@ -427,13 +588,13 @@ const Expr *IvarInvalidationChecker::MethodCrawler::peel(const Expr *E) const { return E; } -void IvarInvalidationChecker::MethodCrawler::checkObjCIvarRefExpr( +void IvarInvalidationCheckerImpl::MethodCrawler::checkObjCIvarRefExpr( const ObjCIvarRefExpr *IvarRef) { if (const Decl *D = IvarRef->getDecl()) markInvalidated(cast<ObjCIvarDecl>(D->getCanonicalDecl())); } -void IvarInvalidationChecker::MethodCrawler::checkObjCMessageExpr( +void IvarInvalidationCheckerImpl::MethodCrawler::checkObjCMessageExpr( const ObjCMessageExpr *ME) { const ObjCMethodDecl *MD = ME->getMethodDecl(); if (MD) { @@ -444,7 +605,7 @@ void IvarInvalidationChecker::MethodCrawler::checkObjCMessageExpr( } } -void IvarInvalidationChecker::MethodCrawler::checkObjCPropertyRefExpr( +void IvarInvalidationCheckerImpl::MethodCrawler::checkObjCPropertyRefExpr( const ObjCPropertyRefExpr *PA) { if (PA->isExplicitProperty()) { @@ -470,14 +631,14 @@ void IvarInvalidationChecker::MethodCrawler::checkObjCPropertyRefExpr( } } -bool IvarInvalidationChecker::MethodCrawler::isZero(const Expr *E) const { +bool IvarInvalidationCheckerImpl::MethodCrawler::isZero(const Expr *E) const { E = peel(E); return (E->isNullPointerConstant(Ctx, Expr::NPC_ValueDependentIsNotNull) != Expr::NPCK_NotNull); } -void IvarInvalidationChecker::MethodCrawler::check(const Expr *E) { +void IvarInvalidationCheckerImpl::MethodCrawler::check(const Expr *E) { E = peel(E); if (const ObjCIvarRefExpr *IvarRef = dyn_cast<ObjCIvarRefExpr>(E)) { @@ -496,28 +657,36 @@ void IvarInvalidationChecker::MethodCrawler::check(const Expr *E) { } } -void IvarInvalidationChecker::MethodCrawler::VisitBinaryOperator( +void IvarInvalidationCheckerImpl::MethodCrawler::VisitBinaryOperator( const BinaryOperator *BO) { VisitStmt(BO); - if (BO->getOpcode() != BO_Assign) + // Do we assign/compare against zero? If yes, check the variable we are + // assigning to. + BinaryOperatorKind Opcode = BO->getOpcode(); + if (Opcode != BO_Assign && + Opcode != BO_EQ && + Opcode != BO_NE) return; - // Do we assign zero? - if (!isZero(BO->getRHS())) - return; + if (isZero(BO->getRHS())) { + check(BO->getLHS()); + return; + } - // Check the variable we are assigning to. - check(BO->getLHS()); + if (Opcode != BO_Assign && isZero(BO->getLHS())) { + check(BO->getRHS()); + return; + } } -void IvarInvalidationChecker::MethodCrawler::VisitObjCMessageExpr( - const ObjCMessageExpr *ME) { +void IvarInvalidationCheckerImpl::MethodCrawler::VisitObjCMessageExpr( + const ObjCMessageExpr *ME) { const ObjCMethodDecl *MD = ME->getMethodDecl(); const Expr *Receiver = ME->getInstanceReceiver(); // Stop if we are calling '[self invalidate]'. - if (Receiver && isInvalidationMethod(MD)) + if (Receiver && isInvalidationMethod(MD, /*LookForPartial*/ false)) if (Receiver->isObjCSelfExpr()) { CalledAnotherInvalidationMethod = true; return; @@ -544,7 +713,27 @@ void IvarInvalidationChecker::MethodCrawler::VisitObjCMessageExpr( } } -// Register the checker. -void ento::registerIvarInvalidationChecker(CheckerManager &mgr) { - mgr.registerChecker<IvarInvalidationChecker>(); +// Register the checkers. +namespace { + +class IvarInvalidationChecker : + public Checker<check::ASTDecl<ObjCImplementationDecl> > { +public: + ChecksFilter Filter; +public: + void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager& Mgr, + BugReporter &BR) const { + IvarInvalidationCheckerImpl Walker(Mgr, BR, Filter); + Walker.visit(D); + } +}; +} + +#define REGISTER_CHECKER(name) \ +void ento::register##name(CheckerManager &mgr) {\ + mgr.registerChecker<IvarInvalidationChecker>()->Filter.check_##name = true;\ } + +REGISTER_CHECKER(InstanceVariableInvalidation) +REGISTER_CHECKER(MissingInvalidationMethod) + diff --git a/lib/StaticAnalyzer/Checkers/LLVMConventionsChecker.cpp b/lib/StaticAnalyzer/Checkers/LLVMConventionsChecker.cpp index 757a4ce28817..02a7cc34e4d4 100644 --- a/lib/StaticAnalyzer/Checkers/LLVMConventionsChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/LLVMConventionsChecker.cpp @@ -13,11 +13,12 @@ //===----------------------------------------------------------------------===// #include "ClangSACheckers.h" -#include "clang/StaticAnalyzer/Core/Checker.h" -#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" #include "clang/AST/DeclTemplate.h" #include "clang/AST/StmtVisitor.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" +#include "clang/StaticAnalyzer/Core/Checker.h" #include "llvm/ADT/SmallString.h" +#include "llvm/Support/raw_ostream.h" using namespace clang; using namespace ento; diff --git a/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp b/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp index 76f20b6e2e51..f1f06c798cde 100644 --- a/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp @@ -13,22 +13,21 @@ //===----------------------------------------------------------------------===// #include "ClangSACheckers.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" -#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" #include "llvm/ADT/SmallString.h" +#include "llvm/Support/raw_ostream.h" using namespace clang; using namespace ento; namespace { class MacOSKeychainAPIChecker : public Checker<check::PreStmt<CallExpr>, - check::PreStmt<ReturnStmt>, check::PostStmt<CallExpr>, - check::EndPath, check::DeadSymbols> { mutable OwningPtr<BugType> BT; @@ -56,14 +55,12 @@ public: }; void checkPreStmt(const CallExpr *S, CheckerContext &C) const; - void checkPreStmt(const ReturnStmt *S, CheckerContext &C) const; void checkPostStmt(const CallExpr *S, CheckerContext &C) const; void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const; - void checkEndPath(CheckerContext &C) const; private: typedef std::pair<SymbolRef, const AllocationState*> AllocationPair; - typedef llvm::SmallVector<AllocationPair, 2> AllocationPairVec; + typedef SmallVector<AllocationPair, 2> AllocationPairVec; enum APIKind { /// Denotes functions tracked by this checker. @@ -94,7 +91,8 @@ private: inline void initBugType() const { if (!BT) - BT.reset(new BugType("Improper use of SecKeychain API", "Mac OS API")); + BT.reset(new BugType("Improper use of SecKeychain API", + "API Misuse (Apple)")); } void generateDeallocatorMismatchReport(const AllocationPair &AP, @@ -102,8 +100,8 @@ private: 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; + const ExplodedNode *getAllocationNode(const ExplodedNode *N, SymbolRef Sym, + CheckerContext &C) const; BugReport *generateAllocatedDataNotReleasedReport(const AllocationPair &AP, ExplodedNode *N, @@ -220,7 +218,7 @@ static SymbolRef getAsPointeeSymbol(const Expr *Expr, ProgramStateRef State = C.getState(); SVal ArgV = State->getSVal(Expr, C.getLocationContext()); - if (const loc::MemRegionVal *X = dyn_cast<loc::MemRegionVal>(&ArgV)) { + if (Optional<loc::MemRegionVal> X = ArgV.getAs<loc::MemRegionVal>()) { StoreManager& SM = C.getStoreManager(); SymbolRef sym = SM.getBinding(State->getStore(), *X).getAsLocSymbol(); if (sym) @@ -396,16 +394,18 @@ void MacOSKeychainAPIChecker::checkPreStmt(const CallExpr *CE, return; } // If kCFAllocatorNull, which does not deallocate, we still have to - // find the deallocator. Otherwise, assume that the user had written a - // custom deallocator which does the right thing. - if (DE->getFoundDecl()->getName() != "kCFAllocatorNull") { - State = State->remove<AllocatedData>(ArgSM); - C.addTransition(State); + // find the deallocator. + if (DE->getFoundDecl()->getName() == "kCFAllocatorNull") return; - } } + // In all other cases, assume the user supplied a correct deallocator + // that will free memory so stop tracking. + State = State->remove<AllocatedData>(ArgSM); + C.addTransition(State); + return; } - return; + + llvm_unreachable("We know of no other possible APIs."); } // The call is deallocating a value we previously allocated, so remove it @@ -422,7 +422,7 @@ void MacOSKeychainAPIChecker::checkPreStmt(const CallExpr *CE, // 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) && + if (State->assume(ArgSVal.castAs<DefinedSVal>(), false) && !definitelyDidnotReturnError(AS->Region, State, C.getSValBuilder())) { ExplodedNode *N = C.addTransition(State); if (!N) @@ -486,31 +486,9 @@ void MacOSKeychainAPIChecker::checkPostStmt(const CallExpr *CE, } } -void MacOSKeychainAPIChecker::checkPreStmt(const ReturnStmt *S, - CheckerContext &C) const { - const Expr *retExpr = S->getRetValue(); - 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. - ProgramStateRef state = C.getState(); - SymbolRef sym = state->getSVal(retExpr, LC).getAsLocSymbol(); - if (!sym) - return; - 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, +const ExplodedNode * +MacOSKeychainAPIChecker::getAllocationNode(const ExplodedNode *N, SymbolRef Sym, CheckerContext &C) const { const LocationContext *LeakContext = N->getLocationContext(); @@ -528,12 +506,7 @@ MacOSKeychainAPIChecker::getAllocationSite(const ExplodedNode *N, N = N->pred_empty() ? NULL : *(N->pred_begin()); } - ProgramPoint P = AllocNode->getLocation(); - if (CallExitEnd *Exit = dyn_cast<CallExitEnd>(&P)) - return Exit->getCalleeContext()->getCallSite(); - if (clang::PostStmt *PS = dyn_cast<clang::PostStmt>(&P)) - return PS->getStmt(); - return 0; + return AllocNode; } BugReport *MacOSKeychainAPIChecker:: @@ -551,11 +524,22 @@ BugReport *MacOSKeychainAPIChecker:: // 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)) + const ExplodedNode *AllocNode = getAllocationNode(N, AP.first, C); + const Stmt *AllocStmt = 0; + ProgramPoint P = AllocNode->getLocation(); + if (Optional<CallExitEnd> Exit = P.getAs<CallExitEnd>()) + AllocStmt = Exit->getCalleeContext()->getCallSite(); + else if (Optional<clang::PostStmt> PS = P.getAs<clang::PostStmt>()) + AllocStmt = PS->getStmt(); + + if (AllocStmt) LocUsedForUniqueing = PathDiagnosticLocation::createBegin(AllocStmt, - C.getSourceManager(), N->getLocationContext()); + C.getSourceManager(), + AllocNode->getLocationContext()); + + BugReport *Report = new BugReport(*BT, os.str(), N, LocUsedForUniqueing, + AllocNode->getLocationContext()->getDecl()); - BugReport *Report = new BugReport(*BT, os.str(), N, LocUsedForUniqueing); Report->addVisitor(new SecKeychainBugVisitor(AP.first)); markInteresting(Report, AP); return Report; @@ -604,55 +588,6 @@ void MacOSKeychainAPIChecker::checkDeadSymbols(SymbolReaper &SR, C.addTransition(State, N); } -// TODO: Remove this after we ensure that checkDeadSymbols are always called. -void MacOSKeychainAPIChecker::checkEndPath(CheckerContext &C) const { - ProgramStateRef state = C.getState(); - - // If inside inlined call, skip it. - if (C.getLocationContext()->getParent() != 0) - return; - - AllocatedDataTy AS = state->get<AllocatedData>(); - if (AS.isEmpty()) - return; - - // Anything which has been allocated but not freed (nor escaped) will be - // found here, so report it. - bool Changed = false; - AllocationPairVec Errors; - for (AllocatedDataTy::iterator I = AS.begin(), E = AS.end(); I != E; ++I ) { - Changed = true; - state = state->remove<AllocatedData>(I->first); - // If the allocated symbol is null or if error code was returned at - // allocation, do not report. - ConstraintManager &CMgr = state->getConstraintManager(); - ConditionTruthVal AllocFailed = CMgr.isNull(state, I.getKey()); - if (AllocFailed.isConstrainedTrue() || - definitelyReturnedError(I->second.Region, state, - C.getSValBuilder())) { - continue; - } - Errors.push_back(std::make_pair(I->first, &I->second)); - } - - // If no change, do not generate a new state. - if (!Changed) { - C.addTransition(state); - 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) { - C.emitReport(generateAllocatedDataNotReleasedReport(*I, N, C)); - } - - C.addTransition(state, N); -} - PathDiagnosticPiece *MacOSKeychainAPIChecker::SecKeychainBugVisitor::VisitNode( const ExplodedNode *N, @@ -668,8 +603,8 @@ PathDiagnosticPiece *MacOSKeychainAPIChecker::SecKeychainBugVisitor::VisitNode( // (!ASPrev && AS) ~ We started tracking symbol in node N, it must be the // allocation site. - const CallExpr *CE = cast<CallExpr>(cast<StmtPoint>(N->getLocation()) - .getStmt()); + const CallExpr *CE = + cast<CallExpr>(N->getLocation().castAs<StmtPoint>().getStmt()); const FunctionDecl *funDecl = CE->getDirectCallee(); assert(funDecl && "We do not support indirect function calls as of now."); StringRef funName = funDecl->getName(); diff --git a/lib/StaticAnalyzer/Checkers/MacOSXAPIChecker.cpp b/lib/StaticAnalyzer/Checkers/MacOSXAPIChecker.cpp index 467b8b1d815c..32ebb51226bb 100644 --- a/lib/StaticAnalyzer/Checkers/MacOSXAPIChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/MacOSXAPIChecker.cpp @@ -8,7 +8,7 @@ //===----------------------------------------------------------------------===// // // This defines MacOSXAPIChecker, which is an assortment of checks on calls -// to various, widely used Mac OS X functions. +// to various, widely used Apple APIs. // // FIXME: What's currently in BasicObjCFoundationChecks.cpp should be migrated // to here, using the new Checker interface. @@ -16,12 +16,12 @@ //===----------------------------------------------------------------------===// #include "ClangSACheckers.h" +#include "clang/Basic/TargetInfo.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.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 "clang/Basic/TargetInfo.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/Support/raw_ostream.h" @@ -68,7 +68,7 @@ void MacOSXAPIChecker::CheckDispatchOnce(CheckerContext &C, const CallExpr *CE, if (!BT_dispatchOnce) BT_dispatchOnce.reset(new BugType("Improper use of 'dispatch_once'", - "Mac OS X API")); + "API Misuse (Apple)")); // Handle _dispatch_once. In some versions of the OS X SDK we have the case // that dispatch_once is a macro that wraps a call to _dispatch_once. diff --git a/lib/StaticAnalyzer/Checkers/MallocChecker.cpp b/lib/StaticAnalyzer/Checkers/MallocChecker.cpp index caf70ca3706f..4b0e7661d8da 100644 --- a/lib/StaticAnalyzer/Checkers/MallocChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/MallocChecker.cpp @@ -14,18 +14,19 @@ #include "ClangSACheckers.h" #include "InterCheckerAPI.h" +#include "clang/AST/Attr.h" +#include "clang/Basic/SourceManager.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.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/CallEvent.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.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 "llvm/ADT/SmallString.h" #include "llvm/ADT/StringExtras.h" #include <climits> @@ -34,6 +35,14 @@ using namespace ento; namespace { +// Used to check correspondence between allocators and deallocators. +enum AllocationFamily { + AF_None, + AF_Malloc, + AF_CXXNew, + AF_CXXNewArray +}; + class RefState { enum Kind { // Reference to allocated memory. Allocated, @@ -41,33 +50,55 @@ class RefState { Released, // The responsibility for freeing resources has transfered from // this reference. A relinquished symbol should not be freed. - Relinquished } K; + Relinquished }; + const Stmt *S; + unsigned K : 2; // Kind enum, but stored as a bitfield. + unsigned Family : 30; // Rest of 32-bit word, currently just an allocation + // family. + RefState(Kind k, const Stmt *s, unsigned family) + : S(s), K(k), Family(family) {} public: - RefState(Kind k, const Stmt *s) : K(k), S(s) {} - bool isAllocated() const { return K == Allocated; } bool isReleased() const { return K == Released; } bool isRelinquished() const { return K == Relinquished; } - + AllocationFamily getAllocationFamily() const { + return (AllocationFamily)Family; + } const Stmt *getStmt() const { return S; } bool operator==(const RefState &X) const { - return K == X.K && S == X.S; + return K == X.K && S == X.S && Family == X.Family; } - static RefState getAllocated(const Stmt *s) { - return RefState(Allocated, s); + static RefState getAllocated(unsigned family, const Stmt *s) { + return RefState(Allocated, s, family); + } + static RefState getReleased(unsigned family, const Stmt *s) { + return RefState(Released, s, family); } - static RefState getReleased(const Stmt *s) { return RefState(Released, s); } - static RefState getRelinquished(const Stmt *s) { - return RefState(Relinquished, s); + static RefState getRelinquished(unsigned family, const Stmt *s) { + return RefState(Relinquished, s, family); } void Profile(llvm::FoldingSetNodeID &ID) const { ID.AddInteger(K); ID.AddPointer(S); + ID.AddInteger(Family); + } + + void dump(raw_ostream &OS) const { + static const char *Table[] = { + "Allocated", + "Released", + "Relinquished" + }; + OS << Table[(unsigned) K]; + } + + LLVM_ATTRIBUTE_USED void dump() const { + dump(llvm::errs()); } }; @@ -99,24 +130,27 @@ struct ReallocPair { } }; -typedef std::pair<const Stmt*, const MemRegion*> LeakInfo; +typedef std::pair<const ExplodedNode*, const MemRegion*> LeakInfo; class MallocChecker : public Checker<check::DeadSymbols, - check::EndPath, + check::PointerEscape, + check::ConstPointerEscape, check::PreStmt<ReturnStmt>, check::PreStmt<CallExpr>, check::PostStmt<CallExpr>, + check::PostStmt<CXXNewExpr>, + check::PreStmt<CXXDeleteExpr>, check::PostStmt<BlockExpr>, check::PostObjCMessage, check::Location, - check::Bind, - eval::Assume, - check::RegionChanges> + eval::Assume> { mutable OwningPtr<BugType> BT_DoubleFree; mutable OwningPtr<BugType> BT_Leak; mutable OwningPtr<BugType> BT_UseFree; mutable OwningPtr<BugType> BT_BadFree; + mutable OwningPtr<BugType> BT_MismatchedDealloc; + mutable OwningPtr<BugType> BT_OffsetFree; mutable IdentifierInfo *II_malloc, *II_free, *II_realloc, *II_calloc, *II_valloc, *II_reallocf, *II_strndup, *II_strdup; @@ -129,32 +163,33 @@ public: struct ChecksFilter { DefaultBool CMallocPessimistic; DefaultBool CMallocOptimistic; + DefaultBool CNewDeleteChecker; + DefaultBool CMismatchedDeallocatorChecker; }; ChecksFilter Filter; void checkPreStmt(const CallExpr *S, CheckerContext &C) const; void checkPostStmt(const CallExpr *CE, CheckerContext &C) const; + void checkPostStmt(const CXXNewExpr *NE, CheckerContext &C) const; + void checkPreStmt(const CXXDeleteExpr *DE, CheckerContext &C) const; void checkPostObjCMessage(const ObjCMethodCall &Call, CheckerContext &C) const; void checkPostStmt(const BlockExpr *BE, CheckerContext &C) const; void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const; - void checkEndPath(CheckerContext &C) const; void checkPreStmt(const ReturnStmt *S, CheckerContext &C) const; 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 CallEvent *Call) const; - bool wantsRegionChangeUpdate(ProgramStateRef state) const { - return true; - } + + ProgramStateRef checkPointerEscape(ProgramStateRef State, + const InvalidatedSymbols &Escaped, + const CallEvent *Call, + PointerEscapeKind Kind) const; + ProgramStateRef checkConstPointerEscape(ProgramStateRef State, + const InvalidatedSymbols &Escaped, + const CallEvent *Call, + PointerEscapeKind Kind) const; void printState(raw_ostream &Out, ProgramStateRef State, const char *NL, const char *Sep) const; @@ -162,31 +197,52 @@ public: private: void initIdentifierInfo(ASTContext &C) const; + /// \brief Determine family of a deallocation expression. + AllocationFamily getAllocationFamily(CheckerContext &C, const Stmt *S) const; + + /// \brief Print names of allocators and deallocators. + /// + /// \returns true on success. + bool printAllocDeallocName(raw_ostream &os, CheckerContext &C, + const Expr *E) const; + + /// \brief Print expected name of an allocator based on the deallocator's + /// family derived from the DeallocExpr. + void printExpectedAllocName(raw_ostream &os, CheckerContext &C, + const Expr *DeallocExpr) const; + /// \brief Print expected name of a deallocator based on the allocator's + /// family. + void printExpectedDeallocName(raw_ostream &os, AllocationFamily Family) 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; bool isFreeFunction(const FunctionDecl *FD, ASTContext &C) const; bool isAllocationFunction(const FunctionDecl *FD, ASTContext &C) const; - + bool isStandardNewDelete(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, - ProgramStateRef state) { + ProgramStateRef State, + AllocationFamily Family = AF_Malloc) { return MallocMemAux(C, CE, - state->getSVal(SizeEx, C.getLocationContext()), - Init, state); + State->getSVal(SizeEx, C.getLocationContext()), + Init, State, Family); } static ProgramStateRef MallocMemAux(CheckerContext &C, const CallExpr *CE, SVal SizeEx, SVal Init, - ProgramStateRef state); + ProgramStateRef State, + AllocationFamily Family = AF_Malloc); /// Update the RefState to reflect the new memory allocation. - static ProgramStateRef MallocUpdateRefState(CheckerContext &C, - const CallExpr *CE, - ProgramStateRef state); + static ProgramStateRef + MallocUpdateRefState(CheckerContext &C, const Expr *E, ProgramStateRef State, + AllocationFamily Family = AF_Malloc); ProgramStateRef FreeMemAttr(CheckerContext &C, const CallExpr *CE, const OwnershipAttr* Att) const; @@ -209,17 +265,43 @@ private: ///\brief Check if the memory associated with this symbol was released. bool isReleased(SymbolRef Sym, CheckerContext &C) const; - bool checkUseAfterFree(SymbolRef Sym, CheckerContext &C, - const Stmt *S = 0) 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 CallEvent *Call, - ProgramStateRef State) const; + bool checkUseAfterFree(SymbolRef Sym, CheckerContext &C, const Stmt *S) const; + + /// Check if the function is known not to free memory, or if it is + /// "interesting" and should be modeled explicitly. + /// + /// We assume that pointers do not escape through calls to system functions + /// not handled by this checker. + bool doesNotFreeMemOrInteresting(const CallEvent *Call, + ProgramStateRef State) const; + + // Implementation of the checkPointerEscape callabcks. + ProgramStateRef checkPointerEscapeAux(ProgramStateRef State, + const InvalidatedSymbols &Escaped, + const CallEvent *Call, + PointerEscapeKind Kind, + bool(*CheckRefState)(const RefState*)) const; + + // Used to suppress warnings if they are not related to the tracked family + // (derived from AllocDeallocStmt). + bool isTrackedFamily(AllocationFamily Family) const; + bool isTrackedFamily(CheckerContext &C, const Stmt *AllocDeallocStmt) const; + bool isTrackedFamily(CheckerContext &C, SymbolRef Sym) const; 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; + void ReportBadFree(CheckerContext &C, SVal ArgVal, SourceRange Range, + const Expr *DeallocExpr) const; + void ReportMismatchedDealloc(CheckerContext &C, SourceRange Range, + const Expr *DeallocExpr, + const RefState *RS) const; + void ReportOffsetFree(CheckerContext &C, SVal ArgVal, SourceRange Range, + const Expr *DeallocExpr, + const Expr *AllocExpr = 0) const; + void ReportUseAfterFree(CheckerContext &C, SourceRange Range, + SymbolRef Sym) const; + void ReportDoubleFree(CheckerContext &C, SourceRange Range, bool Released, + SymbolRef Sym, SymbolRef PrevSym) const; /// Find the location of the allocation for Sym on the path leading to the /// exploded node N. @@ -264,14 +346,14 @@ private: 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) && + return (Stmt && (isa<CallExpr>(Stmt) || isa<CXXNewExpr>(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) && + return (Stmt && (isa<CallExpr>(Stmt) || isa<CXXDeleteExpr>(Stmt)) && (S && S->isReleased()) && (!SPrev || !SPrev->isReleased())); } @@ -381,6 +463,9 @@ bool MallocChecker::isMemFunction(const FunctionDecl *FD, ASTContext &C) const { if (isAllocationFunction(FD, C)) return true; + if (isStandardNewDelete(FD, C)) + return true; + return false; } @@ -432,6 +517,39 @@ bool MallocChecker::isFreeFunction(const FunctionDecl *FD, ASTContext &C) const return false; } +// Tells if the callee is one of the following: +// 1) A global non-placement new/delete operator function. +// 2) A global placement operator function with the single placement argument +// of type std::nothrow_t. +bool MallocChecker::isStandardNewDelete(const FunctionDecl *FD, + ASTContext &C) const { + if (!FD) + return false; + + OverloadedOperatorKind Kind = FD->getOverloadedOperator(); + if (Kind != OO_New && Kind != OO_Array_New && + Kind != OO_Delete && Kind != OO_Array_Delete) + return false; + + // Skip all operator new/delete methods. + if (isa<CXXMethodDecl>(FD)) + return false; + + // Return true if tested operator is a standard placement nothrow operator. + if (FD->getNumParams() == 2) { + QualType T = FD->getParamDecl(1)->getType(); + if (const IdentifierInfo *II = T.getBaseTypeIdentifier()) + return II->getName().equals("nothrow_t"); + } + + // Skip placement operators. + if (FD->getNumParams() != 1 || FD->isVariadic()) + return false; + + // One of the standard new/new[]/delete/delete[] non-placement operators. + return true; +} + void MallocChecker::checkPostStmt(const CallExpr *CE, CheckerContext &C) const { if (C.wasInlined) return; @@ -464,9 +582,26 @@ void MallocChecker::checkPostStmt(const CallExpr *CE, CheckerContext &C) const { } else if (FunI == II_strndup) { State = MallocUpdateRefState(C, CE, State); } + else if (isStandardNewDelete(FD, C.getASTContext())) { + // Process direct calls to operator new/new[]/delete/delete[] functions + // as distinct from new/new[]/delete/delete[] expressions that are + // processed by the checkPostStmt callbacks for CXXNewExpr and + // CXXDeleteExpr. + OverloadedOperatorKind K = FD->getOverloadedOperator(); + if (K == OO_New) + State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State, + AF_CXXNew); + else if (K == OO_Array_New) + State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State, + AF_CXXNewArray); + else if (K == OO_Delete || K == OO_Array_Delete) + State = FreeMemAux(C, CE, State, 0, false, ReleasedAllocatedMemory); + else + llvm_unreachable("not a new/delete operator"); + } } - if (Filter.CMallocOptimistic) { + if (Filter.CMallocOptimistic || Filter.CMismatchedDeallocatorChecker) { // Check all the attributes, if there are any. // There can be multiple of these attributes. if (FD->hasAttrs()) @@ -488,37 +623,91 @@ void MallocChecker::checkPostStmt(const CallExpr *CE, CheckerContext &C) const { C.addTransition(State); } -static bool isFreeWhenDoneSetToZero(const ObjCMethodCall &Call) { +void MallocChecker::checkPostStmt(const CXXNewExpr *NE, + CheckerContext &C) const { + + if (NE->getNumPlacementArgs()) + for (CXXNewExpr::const_arg_iterator I = NE->placement_arg_begin(), + E = NE->placement_arg_end(); I != E; ++I) + if (SymbolRef Sym = C.getSVal(*I).getAsSymbol()) + checkUseAfterFree(Sym, C, *I); + + if (!isStandardNewDelete(NE->getOperatorNew(), C.getASTContext())) + return; + + ProgramStateRef State = C.getState(); + // The return value from operator new is bound to a specified initialization + // value (if any) and we don't want to loose this value. So we call + // MallocUpdateRefState() instead of MallocMemAux() which breakes the + // existing binding. + State = MallocUpdateRefState(C, NE, State, NE->isArray() ? AF_CXXNewArray + : AF_CXXNew); + C.addTransition(State); +} + +void MallocChecker::checkPreStmt(const CXXDeleteExpr *DE, + CheckerContext &C) const { + + if (!Filter.CNewDeleteChecker) + if (SymbolRef Sym = C.getSVal(DE->getArgument()).getAsSymbol()) + checkUseAfterFree(Sym, C, DE->getArgument()); + + if (!isStandardNewDelete(DE->getOperatorDelete(), C.getASTContext())) + return; + + ProgramStateRef State = C.getState(); + bool ReleasedAllocated; + State = FreeMemAux(C, DE->getArgument(), DE, State, + /*Hold*/false, ReleasedAllocated); + + C.addTransition(State); +} + +static bool isKnownDeallocObjCMethodName(const ObjCMethodCall &Call) { + // If the first selector piece is one of the names below, assume that the + // object takes ownership of the memory, promising to eventually deallocate it + // with free(). + // Ex: [NSData dataWithBytesNoCopy:bytes length:10]; + // (...unless a 'freeWhenDone' parameter is false, but that's checked later.) + StringRef FirstSlot = Call.getSelector().getNameForSlot(0); + if (FirstSlot == "dataWithBytesNoCopy" || + FirstSlot == "initWithBytesNoCopy" || + FirstSlot == "initWithCharactersNoCopy") + return true; + + return false; +} + +static Optional<bool> getFreeWhenDoneArg(const ObjCMethodCall &Call) { Selector S = Call.getSelector(); + + // FIXME: We should not rely on fully-constrained symbols being folded. for (unsigned i = 1; i < S.getNumArgs(); ++i) if (S.getNameForSlot(i).equals("freeWhenDone")) - if (Call.getArgSVal(i).isConstant(0)) - return true; + return !Call.getArgSVal(i).isZeroConstant(); - return false; + return None; } void MallocChecker::checkPostObjCMessage(const ObjCMethodCall &Call, CheckerContext &C) const { - // If the first selector is dataWithBytesNoCopy, assume that the memory will - // be released with 'free' by the new object. - // Ex: [NSData dataWithBytesNoCopy:bytes length:10]; - // Unless 'freeWhenDone' param set to 0. - // TODO: Check that the memory was allocated with malloc. - bool ReleasedAllocatedMemory = false; - Selector S = Call.getSelector(); - if ((S.getNameForSlot(0) == "dataWithBytesNoCopy" || - S.getNameForSlot(0) == "initWithBytesNoCopy" || - S.getNameForSlot(0) == "initWithCharactersNoCopy") && - !isFreeWhenDoneSetToZero(Call)){ - unsigned int argIdx = 0; - ProgramStateRef State = FreeMemAux(C, Call.getArgExpr(argIdx), - Call.getOriginExpr(), C.getState(), true, - ReleasedAllocatedMemory, - /* RetNullOnFailure*/ true); - - C.addTransition(State); - } + if (C.wasInlined) + return; + + if (!isKnownDeallocObjCMethodName(Call)) + return; + + if (Optional<bool> FreeWhenDone = getFreeWhenDoneArg(Call)) + if (!*FreeWhenDone) + return; + + bool ReleasedAllocatedMemory; + ProgramStateRef State = FreeMemAux(C, Call.getArgExpr(0), + Call.getOriginExpr(), C.getState(), + /*Hold=*/true, ReleasedAllocatedMemory, + /*RetNullOnFailure=*/true); + + C.addTransition(State); } ProgramStateRef MallocChecker::MallocMemReturnsAttr(CheckerContext &C, @@ -537,7 +726,8 @@ ProgramStateRef MallocChecker::MallocMemReturnsAttr(CheckerContext &C, ProgramStateRef MallocChecker::MallocMemAux(CheckerContext &C, const CallExpr *CE, SVal Size, SVal Init, - ProgramStateRef state) { + ProgramStateRef State, + AllocationFamily Family) { // Bind the return value to the symbolic value from the heap region. // TODO: We could rewrite post visit to eval call; 'malloc' does not have @@ -545,52 +735,52 @@ ProgramStateRef MallocChecker::MallocMemAux(CheckerContext &C, unsigned Count = C.blockCount(); SValBuilder &svalBuilder = C.getSValBuilder(); const LocationContext *LCtx = C.getPredecessor()->getLocationContext(); - DefinedSVal RetVal = - cast<DefinedSVal>(svalBuilder.getConjuredHeapSymbolVal(CE, LCtx, Count)); - state = state->BindExpr(CE, C.getLocationContext(), RetVal); + DefinedSVal RetVal = svalBuilder.getConjuredHeapSymbolVal(CE, LCtx, Count) + .castAs<DefinedSVal>(); + State = State->BindExpr(CE, C.getLocationContext(), RetVal); // We expect the malloc functions to return a pointer. - if (!isa<Loc>(RetVal)) + if (!RetVal.getAs<Loc>()) return 0; // Fill the region with the initialization value. - state = state->bindDefault(RetVal, Init); + State = State->bindDefault(RetVal, Init); // Set the region's extent equal to the Size parameter. const SymbolicRegion *R = dyn_cast_or_null<SymbolicRegion>(RetVal.getAsRegion()); if (!R) return 0; - if (isa<DefinedOrUnknownSVal>(Size)) { + if (Optional<DefinedOrUnknownSVal> DefinedSize = + Size.getAs<DefinedOrUnknownSVal>()) { SValBuilder &svalBuilder = C.getSValBuilder(); DefinedOrUnknownSVal Extent = R->getExtent(svalBuilder); - DefinedOrUnknownSVal DefinedSize = cast<DefinedOrUnknownSVal>(Size); DefinedOrUnknownSVal extentMatchesSize = - svalBuilder.evalEQ(state, Extent, DefinedSize); + svalBuilder.evalEQ(State, Extent, *DefinedSize); - state = state->assume(extentMatchesSize, true); - assert(state); + State = State->assume(extentMatchesSize, true); + assert(State); } - return MallocUpdateRefState(C, CE, state); + return MallocUpdateRefState(C, CE, State, Family); } ProgramStateRef MallocChecker::MallocUpdateRefState(CheckerContext &C, - const CallExpr *CE, - ProgramStateRef state) { + const Expr *E, + ProgramStateRef State, + AllocationFamily Family) { // Get the return value. - SVal retVal = state->getSVal(CE, C.getLocationContext()); + SVal retVal = State->getSVal(E, C.getLocationContext()); // We expect the malloc functions to return a pointer. - if (!isa<Loc>(retVal)) + if (!retVal.getAs<Loc>()) return 0; SymbolRef Sym = retVal.getAsLocSymbol(); assert(Sym); // Set the symbol's state to Allocated. - return state->set<RegionState>(Sym, RefState::getAllocated(CE)); - + return State->set<RegionState>(Sym, RefState::getAllocated(Family, E)); } ProgramStateRef MallocChecker::FreeMemAttr(CheckerContext &C, @@ -629,8 +819,8 @@ ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C, /// Checks if the previous call to free on the given symbol failed - if free /// failed, returns true. Also, returns the corresponding return value symbol. -bool didPreviousFreeFail(ProgramStateRef State, - SymbolRef Sym, SymbolRef &RetStatusSymbol) { +static bool didPreviousFreeFail(ProgramStateRef State, + SymbolRef Sym, SymbolRef &RetStatusSymbol) { const SymbolRef *Ret = State->get<FreeReturnValue>(Sym); if (Ret) { assert(*Ret && "We should not store the null return symbol"); @@ -642,6 +832,107 @@ bool didPreviousFreeFail(ProgramStateRef State, return false; } +AllocationFamily MallocChecker::getAllocationFamily(CheckerContext &C, + const Stmt *S) const { + if (!S) + return AF_None; + + if (const CallExpr *CE = dyn_cast<CallExpr>(S)) { + const FunctionDecl *FD = C.getCalleeDecl(CE); + + if (!FD) + FD = dyn_cast<FunctionDecl>(CE->getCalleeDecl()); + + ASTContext &Ctx = C.getASTContext(); + + if (isAllocationFunction(FD, Ctx) || isFreeFunction(FD, Ctx)) + return AF_Malloc; + + if (isStandardNewDelete(FD, Ctx)) { + OverloadedOperatorKind Kind = FD->getOverloadedOperator(); + if (Kind == OO_New || Kind == OO_Delete) + return AF_CXXNew; + else if (Kind == OO_Array_New || Kind == OO_Array_Delete) + return AF_CXXNewArray; + } + + return AF_None; + } + + if (const CXXNewExpr *NE = dyn_cast<CXXNewExpr>(S)) + return NE->isArray() ? AF_CXXNewArray : AF_CXXNew; + + if (const CXXDeleteExpr *DE = dyn_cast<CXXDeleteExpr>(S)) + return DE->isArrayForm() ? AF_CXXNewArray : AF_CXXNew; + + if (isa<ObjCMessageExpr>(S)) + return AF_Malloc; + + return AF_None; +} + +bool MallocChecker::printAllocDeallocName(raw_ostream &os, CheckerContext &C, + const Expr *E) const { + if (const CallExpr *CE = dyn_cast<CallExpr>(E)) { + // FIXME: This doesn't handle indirect calls. + const FunctionDecl *FD = CE->getDirectCallee(); + if (!FD) + return false; + + os << *FD; + if (!FD->isOverloadedOperator()) + os << "()"; + return true; + } + + if (const ObjCMessageExpr *Msg = dyn_cast<ObjCMessageExpr>(E)) { + if (Msg->isInstanceMessage()) + os << "-"; + else + os << "+"; + os << Msg->getSelector().getAsString(); + return true; + } + + if (const CXXNewExpr *NE = dyn_cast<CXXNewExpr>(E)) { + os << "'" + << getOperatorSpelling(NE->getOperatorNew()->getOverloadedOperator()) + << "'"; + return true; + } + + if (const CXXDeleteExpr *DE = dyn_cast<CXXDeleteExpr>(E)) { + os << "'" + << getOperatorSpelling(DE->getOperatorDelete()->getOverloadedOperator()) + << "'"; + return true; + } + + return false; +} + +void MallocChecker::printExpectedAllocName(raw_ostream &os, CheckerContext &C, + const Expr *E) const { + AllocationFamily Family = getAllocationFamily(C, E); + + switch(Family) { + case AF_Malloc: os << "malloc()"; return; + case AF_CXXNew: os << "'new'"; return; + case AF_CXXNewArray: os << "'new[]'"; return; + case AF_None: llvm_unreachable("not a deallocation expression"); + } +} + +void MallocChecker::printExpectedDeallocName(raw_ostream &os, + AllocationFamily Family) const { + switch(Family) { + case AF_Malloc: os << "free()"; return; + case AF_CXXNew: os << "'delete'"; return; + case AF_CXXNewArray: os << "'delete[]'"; return; + case AF_None: llvm_unreachable("suspicious AF_None argument"); + } +} + ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C, const Expr *ArgExpr, const Expr *ParentExpr, @@ -651,12 +942,12 @@ ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C, bool ReturnsNullOnFailure) const { SVal ArgVal = State->getSVal(ArgExpr, C.getLocationContext()); - if (!isa<DefinedOrUnknownSVal>(ArgVal)) + if (!ArgVal.getAs<DefinedOrUnknownSVal>()) return 0; - DefinedOrUnknownSVal location = cast<DefinedOrUnknownSVal>(ArgVal); + DefinedOrUnknownSVal location = ArgVal.castAs<DefinedOrUnknownSVal>(); // Check for null dereferences. - if (!isa<Loc>(location)) + if (!location.getAs<Loc>()) return 0; // The explicit NULL case, no operation is performed. @@ -675,7 +966,7 @@ ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C, // Nonlocs can't be freed, of course. // Non-region locations (labels and fixed addresses) also shouldn't be freed. if (!R) { - ReportBadFree(C, ArgVal, ArgExpr->getSourceRange()); + ReportBadFree(C, ArgVal, ArgExpr->getSourceRange(), ParentExpr); return 0; } @@ -683,13 +974,14 @@ ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C, // Blocks might show up as heap data, but should not be free()d if (isa<BlockDataRegion>(R)) { - ReportBadFree(C, ArgVal, ArgExpr->getSourceRange()); + ReportBadFree(C, ArgVal, ArgExpr->getSourceRange(), ParentExpr); return 0; } const MemSpaceRegion *MS = R->getMemorySpace(); - // Parameters, locals, statics, and globals shouldn't be freed. + // Parameters, locals, statics, globals, and memory returned by alloca() + // shouldn't be freed. if (!(isa<UnknownSpaceRegion>(MS) || isa<HeapSpaceRegion>(MS))) { // FIXME: at the time this code was written, malloc() regions were // represented by conjured symbols, which are all in UnknownSpaceRegion. @@ -699,46 +991,59 @@ ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C, // function, so UnknownSpaceRegion is always a possibility. // False negatives are better than false positives. - ReportBadFree(C, ArgVal, ArgExpr->getSourceRange()); + ReportBadFree(C, ArgVal, ArgExpr->getSourceRange(), ParentExpr); return 0; } - - const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(R); + + const SymbolicRegion *SrBase = dyn_cast<SymbolicRegion>(R->getBaseRegion()); // Various cases could lead to non-symbol values here. // For now, ignore them. - if (!SR) + if (!SrBase) return 0; - SymbolRef Sym = SR->getSymbol(); - const RefState *RS = State->get<RegionState>(Sym); + SymbolRef SymBase = SrBase->getSymbol(); + const RefState *RsBase = State->get<RegionState>(SymBase); SymbolRef PreviousRetStatusSymbol = 0; - // Check double free. - if (RS && - (RS->isReleased() || RS->isRelinquished()) && - !didPreviousFreeFail(State, Sym, PreviousRetStatusSymbol)) { - - if (ExplodedNode *N = C.generateSink()) { - if (!BT_DoubleFree) - BT_DoubleFree.reset( - new BugType("Double free", "Memory Error")); - BugReport *R = new BugReport(*BT_DoubleFree, - (RS->isReleased() ? "Attempt to free released memory" : - "Attempt to free non-owned memory"), N); - R->addRange(ArgExpr->getSourceRange()); - R->markInteresting(Sym); - if (PreviousRetStatusSymbol) - R->markInteresting(PreviousRetStatusSymbol); - R->addVisitor(new MallocBugVisitor(Sym)); - C.emitReport(R); + if (RsBase) { + + bool DeallocMatchesAlloc = + RsBase->getAllocationFamily() == AF_None || + RsBase->getAllocationFamily() == getAllocationFamily(C, ParentExpr); + + // Check if an expected deallocation function matches the real one. + if (!DeallocMatchesAlloc && RsBase->isAllocated()) { + ReportMismatchedDealloc(C, ArgExpr->getSourceRange(), ParentExpr, RsBase); + return 0; + } + + // Check double free. + if (DeallocMatchesAlloc && + (RsBase->isReleased() || RsBase->isRelinquished()) && + !didPreviousFreeFail(State, SymBase, PreviousRetStatusSymbol)) { + ReportDoubleFree(C, ParentExpr->getSourceRange(), RsBase->isReleased(), + SymBase, PreviousRetStatusSymbol); + return 0; + } + + // Check if the memory location being freed is the actual location + // allocated, or an offset. + RegionOffset Offset = R->getAsOffset(); + if (RsBase->isAllocated() && + Offset.isValid() && + !Offset.hasSymbolicOffset() && + Offset.getOffset() != 0) { + const Expr *AllocExpr = cast<Expr>(RsBase->getStmt()); + ReportOffsetFree(C, ArgVal, ArgExpr->getSourceRange(), ParentExpr, + AllocExpr); + return 0; } - return 0; } - ReleasedAllocated = (RS != 0); + ReleasedAllocated = (RsBase != 0); // Clean out the info on previous call to free return info. - State = State->remove<FreeReturnValue>(Sym); + State = State->remove<FreeReturnValue>(SymBase); // Keep track of the return value. If it is NULL, we will know that free // failed. @@ -746,23 +1051,60 @@ ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C, SVal RetVal = C.getSVal(ParentExpr); SymbolRef RetStatusSymbol = RetVal.getAsSymbol(); if (RetStatusSymbol) { - C.getSymbolManager().addSymbolDependency(Sym, RetStatusSymbol); - State = State->set<FreeReturnValue>(Sym, RetStatusSymbol); + C.getSymbolManager().addSymbolDependency(SymBase, RetStatusSymbol); + State = State->set<FreeReturnValue>(SymBase, RetStatusSymbol); } } + AllocationFamily Family = RsBase ? RsBase->getAllocationFamily() : AF_None; // Normal free. if (Hold) - return State->set<RegionState>(Sym, RefState::getRelinquished(ParentExpr)); - return State->set<RegionState>(Sym, RefState::getReleased(ParentExpr)); + return State->set<RegionState>(SymBase, + RefState::getRelinquished(Family, + ParentExpr)); + + return State->set<RegionState>(SymBase, + RefState::getReleased(Family, ParentExpr)); +} + +bool MallocChecker::isTrackedFamily(AllocationFamily Family) const { + switch (Family) { + case AF_Malloc: { + if (!Filter.CMallocOptimistic && !Filter.CMallocPessimistic) + return false; + return true; + } + case AF_CXXNew: + case AF_CXXNewArray: { + if (!Filter.CNewDeleteChecker) + return false; + return true; + } + case AF_None: { + return true; + } + } + llvm_unreachable("unhandled family"); +} + +bool MallocChecker::isTrackedFamily(CheckerContext &C, + const Stmt *AllocDeallocStmt) const { + return isTrackedFamily(getAllocationFamily(C, AllocDeallocStmt)); +} + +bool MallocChecker::isTrackedFamily(CheckerContext &C, SymbolRef Sym) const { + const RefState *RS = C.getState()->get<RegionState>(Sym); + + return RS ? isTrackedFamily(RS->getAllocationFamily()) + : isTrackedFamily(AF_None); } bool MallocChecker::SummarizeValue(raw_ostream &os, SVal V) { - if (nonloc::ConcreteInt *IntVal = dyn_cast<nonloc::ConcreteInt>(&V)) + if (Optional<nonloc::ConcreteInt> IntVal = V.getAs<nonloc::ConcreteInt>()) os << "an integer (" << IntVal->getValue() << ")"; - else if (loc::ConcreteInt *ConstAddr = dyn_cast<loc::ConcreteInt>(&V)) + else if (Optional<loc::ConcreteInt> ConstAddr = V.getAs<loc::ConcreteInt>()) os << "a constant address (" << ConstAddr->getValue() << ")"; - else if (loc::GotoLabel *Label = dyn_cast<loc::GotoLabel>(&V)) + else if (Optional<loc::GotoLabel> Label = V.getAs<loc::GotoLabel>()) os << "the address of the label '" << Label->getLabel()->getName() << "'"; else return false; @@ -844,41 +1186,192 @@ bool MallocChecker::SummarizeRegion(raw_ostream &os, } } -void MallocChecker::ReportBadFree(CheckerContext &C, SVal ArgVal, - SourceRange range) const { +void MallocChecker::ReportBadFree(CheckerContext &C, SVal ArgVal, + SourceRange Range, + const Expr *DeallocExpr) const { + + if (!Filter.CMallocOptimistic && !Filter.CMallocPessimistic && + !Filter.CNewDeleteChecker) + return; + + if (!isTrackedFamily(C, DeallocExpr)) + return; + if (ExplodedNode *N = C.generateSink()) { if (!BT_BadFree) BT_BadFree.reset(new BugType("Bad free", "Memory Error")); SmallString<100> buf; llvm::raw_svector_ostream os(buf); - + const MemRegion *MR = ArgVal.getAsRegion(); - if (MR) { - while (const ElementRegion *ER = dyn_cast<ElementRegion>(MR)) - MR = ER->getSuperRegion(); - - // Special case for alloca() - if (isa<AllocaRegion>(MR)) - os << "Argument to free() was allocated by alloca(), not malloc()"; - else { - os << "Argument to free() is "; - if (SummarizeRegion(os, MR)) - os << ", which is not memory allocated by malloc()"; - else - os << "not memory allocated by malloc()"; - } - } else { - os << "Argument to free() is "; - if (SummarizeValue(os, ArgVal)) - os << ", which is not memory allocated by malloc()"; + while (const ElementRegion *ER = dyn_cast_or_null<ElementRegion>(MR)) + MR = ER->getSuperRegion(); + + if (MR && isa<AllocaRegion>(MR)) + os << "Memory allocated by alloca() should not be deallocated"; + else { + os << "Argument to "; + if (!printAllocDeallocName(os, C, DeallocExpr)) + os << "deallocator"; + + os << " is "; + bool Summarized = MR ? SummarizeRegion(os, MR) + : SummarizeValue(os, ArgVal); + if (Summarized) + os << ", which is not memory allocated by "; else - os << "not memory allocated by malloc()"; + os << "not memory allocated by "; + + printExpectedAllocName(os, C, DeallocExpr); } - + BugReport *R = new BugReport(*BT_BadFree, os.str(), N); R->markInteresting(MR); - R->addRange(range); + R->addRange(Range); + C.emitReport(R); + } +} + +void MallocChecker::ReportMismatchedDealloc(CheckerContext &C, + SourceRange Range, + const Expr *DeallocExpr, + const RefState *RS) const { + + if (!Filter.CMismatchedDeallocatorChecker) + return; + + if (ExplodedNode *N = C.generateSink()) { + if (!BT_MismatchedDealloc) + BT_MismatchedDealloc.reset(new BugType("Bad deallocator", + "Memory Error")); + + SmallString<100> buf; + llvm::raw_svector_ostream os(buf); + + const Expr *AllocExpr = cast<Expr>(RS->getStmt()); + SmallString<20> AllocBuf; + llvm::raw_svector_ostream AllocOs(AllocBuf); + SmallString<20> DeallocBuf; + llvm::raw_svector_ostream DeallocOs(DeallocBuf); + + os << "Memory"; + if (printAllocDeallocName(AllocOs, C, AllocExpr)) + os << " allocated by " << AllocOs.str(); + + os << " should be deallocated by "; + printExpectedDeallocName(os, RS->getAllocationFamily()); + + if (printAllocDeallocName(DeallocOs, C, DeallocExpr)) + os << ", not " << DeallocOs.str(); + + BugReport *R = new BugReport(*BT_MismatchedDealloc, os.str(), N); + R->addRange(Range); + C.emitReport(R); + } +} + +void MallocChecker::ReportOffsetFree(CheckerContext &C, SVal ArgVal, + SourceRange Range, const Expr *DeallocExpr, + const Expr *AllocExpr) const { + + if (!Filter.CMallocOptimistic && !Filter.CMallocPessimistic && + !Filter.CNewDeleteChecker) + return; + + if (!isTrackedFamily(C, AllocExpr)) + return; + + ExplodedNode *N = C.generateSink(); + if (N == NULL) + return; + + if (!BT_OffsetFree) + BT_OffsetFree.reset(new BugType("Offset free", "Memory Error")); + + SmallString<100> buf; + llvm::raw_svector_ostream os(buf); + SmallString<20> AllocNameBuf; + llvm::raw_svector_ostream AllocNameOs(AllocNameBuf); + + const MemRegion *MR = ArgVal.getAsRegion(); + assert(MR && "Only MemRegion based symbols can have offset free errors"); + + RegionOffset Offset = MR->getAsOffset(); + assert((Offset.isValid() && + !Offset.hasSymbolicOffset() && + Offset.getOffset() != 0) && + "Only symbols with a valid offset can have offset free errors"); + + int offsetBytes = Offset.getOffset() / C.getASTContext().getCharWidth(); + + os << "Argument to "; + if (!printAllocDeallocName(os, C, DeallocExpr)) + os << "deallocator"; + os << " is offset by " + << offsetBytes + << " " + << ((abs(offsetBytes) > 1) ? "bytes" : "byte") + << " from the start of "; + if (AllocExpr && printAllocDeallocName(AllocNameOs, C, AllocExpr)) + os << "memory allocated by " << AllocNameOs.str(); + else + os << "allocated memory"; + + BugReport *R = new BugReport(*BT_OffsetFree, os.str(), N); + R->markInteresting(MR->getBaseRegion()); + R->addRange(Range); + C.emitReport(R); +} + +void MallocChecker::ReportUseAfterFree(CheckerContext &C, SourceRange Range, + SymbolRef Sym) const { + + if (!Filter.CMallocOptimistic && !Filter.CMallocPessimistic && + !Filter.CNewDeleteChecker) + return; + + if (!isTrackedFamily(C, Sym)) + return; + + 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); + + R->markInteresting(Sym); + R->addRange(Range); + R->addVisitor(new MallocBugVisitor(Sym)); + C.emitReport(R); + } +} + +void MallocChecker::ReportDoubleFree(CheckerContext &C, SourceRange Range, + bool Released, SymbolRef Sym, + SymbolRef PrevSym) const { + + if (!Filter.CMallocOptimistic && !Filter.CMallocPessimistic && + !Filter.CNewDeleteChecker) + return; + + if (!isTrackedFamily(C, Sym)) + return; + + if (ExplodedNode *N = C.generateSink()) { + if (!BT_DoubleFree) + BT_DoubleFree.reset(new BugType("Double free", "Memory Error")); + + BugReport *R = new BugReport(*BT_DoubleFree, + (Released ? "Attempt to free released memory" + : "Attempt to free non-owned memory"), + N); + R->addRange(Range); + R->markInteresting(Sym); + if (PrevSym) + R->markInteresting(PrevSym); + R->addVisitor(new MallocBugVisitor(Sym)); C.emitReport(R); } } @@ -893,9 +1386,9 @@ ProgramStateRef MallocChecker::ReallocMem(CheckerContext &C, const Expr *arg0Expr = CE->getArg(0); const LocationContext *LCtx = C.getLocationContext(); SVal Arg0Val = state->getSVal(arg0Expr, LCtx); - if (!isa<DefinedOrUnknownSVal>(Arg0Val)) + if (!Arg0Val.getAs<DefinedOrUnknownSVal>()) return 0; - DefinedOrUnknownSVal arg0Val = cast<DefinedOrUnknownSVal>(Arg0Val); + DefinedOrUnknownSVal arg0Val = Arg0Val.castAs<DefinedOrUnknownSVal>(); SValBuilder &svalBuilder = C.getSValBuilder(); @@ -909,9 +1402,9 @@ ProgramStateRef MallocChecker::ReallocMem(CheckerContext &C, // Get the value of the size argument. SVal Arg1ValG = state->getSVal(Arg1, LCtx); - if (!isa<DefinedOrUnknownSVal>(Arg1ValG)) + if (!Arg1ValG.getAs<DefinedOrUnknownSVal>()) return 0; - DefinedOrUnknownSVal Arg1Val = cast<DefinedOrUnknownSVal>(Arg1ValG); + DefinedOrUnknownSVal Arg1Val = Arg1ValG.castAs<DefinedOrUnknownSVal>(); // Compare the size argument to 0. DefinedOrUnknownSVal SizeZero = @@ -1032,18 +1525,19 @@ MallocChecker::getAllocationSite(const ExplodedNode *N, SymbolRef Sym, N = N->pred_empty() ? NULL : *(N->pred_begin()); } - ProgramPoint P = AllocNode->getLocation(); - const Stmt *AllocationStmt = 0; - if (CallExitEnd *Exit = dyn_cast<CallExitEnd>(&P)) - AllocationStmt = Exit->getCalleeContext()->getCallSite(); - else if (StmtPoint *SP = dyn_cast<StmtPoint>(&P)) - AllocationStmt = SP->getStmt(); - - return LeakInfo(AllocationStmt, ReferenceRegion); + return LeakInfo(AllocNode, ReferenceRegion); } void MallocChecker::reportLeak(SymbolRef Sym, ExplodedNode *N, CheckerContext &C) const { + + if (!Filter.CMallocOptimistic && !Filter.CMallocPessimistic && + !Filter.CNewDeleteChecker) + return; + + if (!isTrackedFamily(C, Sym)) + return; + assert(N); if (!BT_Leak) { BT_Leak.reset(new BugType("Memory leak", "Memory Error")); @@ -1059,12 +1553,20 @@ void MallocChecker::reportLeak(SymbolRef Sym, ExplodedNode *N, // 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 ExplodedNode *AllocNode = 0; const MemRegion *Region = 0; - llvm::tie(AllocStmt, Region) = getAllocationSite(N, Sym, C); - if (AllocStmt) - LocUsedForUniqueing = PathDiagnosticLocation::createBegin(AllocStmt, - C.getSourceManager(), N->getLocationContext()); + llvm::tie(AllocNode, Region) = getAllocationSite(N, Sym, C); + + ProgramPoint P = AllocNode->getLocation(); + const Stmt *AllocationStmt = 0; + if (Optional<CallExitEnd> Exit = P.getAs<CallExitEnd>()) + AllocationStmt = Exit->getCalleeContext()->getCallSite(); + else if (Optional<StmtPoint> SP = P.getAs<StmtPoint>()) + AllocationStmt = SP->getStmt(); + if (AllocationStmt) + LocUsedForUniqueing = PathDiagnosticLocation::createBegin(AllocationStmt, + C.getSourceManager(), + AllocNode->getLocationContext()); SmallString<200> buf; llvm::raw_svector_ostream os(buf); @@ -1075,7 +1577,9 @@ void MallocChecker::reportLeak(SymbolRef Sym, ExplodedNode *N, os << '\''; } - BugReport *R = new BugReport(*BT_Leak, os.str(), N, LocUsedForUniqueing); + BugReport *R = new BugReport(*BT_Leak, os.str(), N, + LocUsedForUniqueing, + AllocNode->getLocationContext()->getDecl()); R->markInteresting(Sym); R->addVisitor(new MallocBugVisitor(Sym, true)); C.emitReport(R); @@ -1091,7 +1595,7 @@ void MallocChecker::checkDeadSymbols(SymbolReaper &SymReaper, RegionStateTy RS = state->get<RegionState>(); RegionStateTy::Factory &F = state->get_context<RegionState>(); - llvm::SmallVector<SymbolRef, 2> Errors; + 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()) @@ -1125,7 +1629,7 @@ void MallocChecker::checkDeadSymbols(SymbolReaper &SymReaper, if (!Errors.empty()) { static SimpleProgramPointTag Tag("MallocChecker : DeadSymbolsLeak"); N = C.addTransition(C.getState(), C.getPredecessor(), &Tag); - for (llvm::SmallVector<SymbolRef, 2>::iterator + for (SmallVector<SymbolRef, 2>::iterator I = Errors.begin(), E = Errors.end(); I != E; ++I) { reportLeak(*I, N, C); } @@ -1134,27 +1638,14 @@ void MallocChecker::checkDeadSymbols(SymbolReaper &SymReaper, C.addTransition(state->set<RegionState>(RS), N); } -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 = C.addTransition(state); - if (N) - reportLeak(I->first, N, C); - } - } -} - void MallocChecker::checkPreStmt(const CallExpr *CE, CheckerContext &C) const { // We will check for double free in the post visit. - if (isFreeFunction(C.getCalleeDecl(CE), C.getASTContext())) + if ((Filter.CMallocOptimistic || Filter.CMallocPessimistic) && + isFreeFunction(C.getCalleeDecl(CE), C.getASTContext())) + return; + + if (Filter.CNewDeleteChecker && + isStandardNewDelete(C.getCalleeDecl(CE), C.getASTContext())) return; // Check use after free, when a freed pointer is passed to a call. @@ -1163,7 +1654,7 @@ void MallocChecker::checkPreStmt(const CallExpr *CE, CheckerContext &C) const { E = CE->arg_end(); I != E; ++I) { const Expr *A = *I; if (A->getType().getTypePtr()->isAnyPointerType()) { - SymbolRef Sym = State->getSVal(A, C.getLocationContext()).getAsSymbol(); + SymbolRef Sym = C.getSVal(A).getAsSymbol(); if (!Sym) continue; if (checkUseAfterFree(Sym, C, A)) @@ -1193,15 +1684,7 @@ void MallocChecker::checkPreStmt(const ReturnStmt *S, CheckerContext &C) const { // Check if we are returning freed memory. if (Sym) - if (checkUseAfterFree(Sym, C, E)) - return; - - // If this function body is not inlined, stop tracking any returned symbols. - if (C.getLocationContext()->getParent() == 0) { - State = - State->scanReachableSymbols<StopTrackingCallback>(RetVal).getState(); - C.addTransition(State); - } + checkUseAfterFree(Sym, C, E); } // TODO: Blocks should be either inlined or should call invalidate regions @@ -1231,7 +1714,7 @@ void MallocChecker::checkPostStmt(const BlockExpr *BE, MemRegionManager &MemMgr = C.getSValBuilder().getRegionManager(); for ( ; I != E; ++I) { - const VarRegion *VR = *I; + const VarRegion *VR = I.getCapturedRegion(); if (VR->getSuperRegion() == R) { VR = MemMgr.getVarRegion(VR->getDecl(), LC); } @@ -1252,21 +1735,12 @@ bool MallocChecker::isReleased(SymbolRef Sym, CheckerContext &C) const { bool MallocChecker::checkUseAfterFree(SymbolRef Sym, CheckerContext &C, const Stmt *S) const { + if (isReleased(Sym, C)) { - 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; - } + ReportUseAfterFree(C, S->getSourceRange(), Sym); + return true; } + return false; } @@ -1278,51 +1752,6 @@ void MallocChecker::checkLocation(SVal l, bool isLoad, const Stmt *S, checkUseAfterFree(Sym, C, S); } -//===----------------------------------------------------------------------===// -// 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). - // Do this only if we know that the store is not supposed to generate the - // same state. - SVal StoredVal = state->getSVal(regionLoc->getRegion()); - if (StoredVal != val) - escapes = (state == (state->bindLoc(*regionLoc, val))); - } - } - - // If our store can represent the binding and we aren't storing to something - // that doesn't have local storage then just return and have the simulation - // state continue as is. - if (!escapes) - return; - - // Otherwise, find all symbols referenced by 'val' that we are tracking - // and stop tracking them. - state = state->scanReachableSymbols<StopTrackingCallback>(val).getState(); - C.addTransition(state); -} - // 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, @@ -1352,7 +1781,7 @@ ProgramStateRef MallocChecker::evalAssume(ProgramStateRef state, if (RS->isReleased()) { if (I.getData().Kind == RPToBeFreedAfterFailure) state = state->set<RegionState>(ReallocSym, - RefState::getAllocated(RS->getStmt())); + RefState::getAllocated(RS->getAllocationFamily(), RS->getStmt())); else if (I.getData().Kind == RPDoNotTrackAfterFailure) state = state->remove<RegionState>(ReallocSym); else @@ -1365,12 +1794,8 @@ ProgramStateRef MallocChecker::evalAssume(ProgramStateRef state, return state; } -// Check if the function is known to us. So, for example, we could -// conservatively assume it can free/reallocate its pointer arguments. -// (We assume that the pointers cannot escape through calls to system -// functions not handled by this checker.) -bool MallocChecker::doesNotFreeMemory(const CallEvent *Call, - ProgramStateRef State) const { +bool MallocChecker::doesNotFreeMemOrInteresting(const CallEvent *Call, + ProgramStateRef State) const { assert(Call); // For now, assume that any C++ call can free memory. @@ -1387,24 +1812,23 @@ bool MallocChecker::doesNotFreeMemory(const CallEvent *Call, if (!Call->isInSystemHeader() || Call->hasNonZeroCallbackArg()) return false; - Selector S = Msg->getSelector(); - - // Whitelist the ObjC methods 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 it's a method we know about, handle it explicitly post-call. + // This should happen before the "freeWhenDone" check below. + if (isKnownDeallocObjCMethodName(*Msg)) + return true; - // If the first selector ends with NoCopy, assume that the ownership is - // transferred as well. - // Ex: [NSData dataWithBytesNoCopy:bytes length:10]; - StringRef FirstSlot = S.getNameForSlot(0); + // If there's a "freeWhenDone" parameter, but the method isn't one we know + // about, we can't be sure that the object will use free() to deallocate the + // memory, so we can't model it explicitly. The best we can do is use it to + // decide whether the pointer escapes. + if (Optional<bool> FreeWhenDone = getFreeWhenDoneArg(*Msg)) + return !*FreeWhenDone; + + // If the first selector piece ends with "NoCopy", and there is no + // "freeWhenDone" parameter set to zero, we know ownership is being + // transferred. Again, though, we can't be sure that the object will use + // free() to deallocate the memory, so we can't model it explicitly. + StringRef FirstSlot = Msg->getSelector().getNameForSlot(0); if (FirstSlot.endswith("NoCopy")) return false; @@ -1509,41 +1933,50 @@ bool MallocChecker::doesNotFreeMemory(const CallEvent *Call, return true; } -// 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 CallEvent *Call) const { - if (!invalidated || invalidated->empty()) - return State; - llvm::SmallPtrSet<SymbolRef, 8> WhitelistedSymbols; +static bool retTrue(const RefState *RS) { + return true; +} - // If it's a call which might free or reallocate memory, we assume that all - // regions (explicit and implicit) escaped. +static bool checkIfNewOrNewArrayFamily(const RefState *RS) { + return (RS->getAllocationFamily() == AF_CXXNewArray || + RS->getAllocationFamily() == AF_CXXNew); +} - // 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()); - } +ProgramStateRef MallocChecker::checkPointerEscape(ProgramStateRef State, + const InvalidatedSymbols &Escaped, + const CallEvent *Call, + PointerEscapeKind Kind) const { + return checkPointerEscapeAux(State, Escaped, Call, Kind, &retTrue); +} + +ProgramStateRef MallocChecker::checkConstPointerEscape(ProgramStateRef State, + const InvalidatedSymbols &Escaped, + const CallEvent *Call, + PointerEscapeKind Kind) const { + return checkPointerEscapeAux(State, Escaped, Call, Kind, + &checkIfNewOrNewArrayFamily); +} + +ProgramStateRef MallocChecker::checkPointerEscapeAux(ProgramStateRef State, + const InvalidatedSymbols &Escaped, + const CallEvent *Call, + PointerEscapeKind Kind, + bool(*CheckRefState)(const RefState*)) const { + // If we know that the call does not free memory, or we want to process the + // call later, keep tracking the top level arguments. + if ((Kind == PSK_DirectEscapeOnCall || + Kind == PSK_IndirectEscapeOnCall) && + doesNotFreeMemOrInteresting(Call, State)) { + return State; } - for (StoreManager::InvalidatedSymbols::const_iterator I=invalidated->begin(), - E = invalidated->end(); I!=E; ++I) { + for (InvalidatedSymbols::const_iterator I = Escaped.begin(), + E = Escaped.end(); + I != E; ++I) { SymbolRef sym = *I; - if (WhitelistedSymbols.count(sym)) - continue; - // The symbol escaped. Note, we assume that if the symbol is released, - // passing it out will result in a use after free. We also keep tracking - // relinquished symbols. + if (const RefState *RS = State->get<RegionState>(sym)) { - if (RS->isAllocated()) + if (RS->isAllocated() && CheckRefState(RS)) State = State->remove<RegionState>(sym); } } @@ -1584,16 +2017,16 @@ MallocChecker::MallocBugVisitor::VisitNode(const ExplodedNode *N, // Retrieve the associated statement. ProgramPoint ProgLoc = N->getLocation(); - if (StmtPoint *SP = dyn_cast<StmtPoint>(&ProgLoc)) + if (Optional<StmtPoint> SP = ProgLoc.getAs<StmtPoint>()) { S = SP->getStmt(); - else if (CallExitEnd *Exit = dyn_cast<CallExitEnd>(&ProgLoc)) + } else if (Optional<CallExitEnd> Exit = ProgLoc.getAs<CallExitEnd>()) { S = Exit->getCalleeContext()->getCallSite(); - // If an assumption was made on a branch, it should be caught - // here by looking at the state transition. - else if (BlockEdge *Edge = dyn_cast<BlockEdge>(&ProgLoc)) { - const CFGBlock *srcBlk = Edge->getSrc(); - S = srcBlk->getTerminator(); + } else if (Optional<BlockEdge> Edge = ProgLoc.getAs<BlockEdge>()) { + // If an assumption was made on a branch, it should be caught + // here by looking at the state transition. + S = Edge->getSrc()->getTerminator(); } + if (!S) return 0; @@ -1658,8 +2091,15 @@ void MallocChecker::printState(raw_ostream &Out, ProgramStateRef State, RegionStateTy RS = State->get<RegionState>(); - if (!RS.isEmpty()) - Out << "Has Malloc data" << NL; + if (!RS.isEmpty()) { + Out << Sep << "MallocChecker:" << NL; + for (RegionStateTy::iterator I = RS.begin(), E = RS.end(); I != E; ++I) { + I.getKey()->dumpToStream(Out); + Out << " : "; + I.getData().dump(Out); + Out << NL; + } + } } #define REGISTER_CHECKER(name) \ @@ -1670,3 +2110,5 @@ void ento::register##name(CheckerManager &mgr) {\ REGISTER_CHECKER(MallocPessimistic) REGISTER_CHECKER(MallocOptimistic) +REGISTER_CHECKER(NewDeleteChecker) +REGISTER_CHECKER(MismatchedDeallocatorChecker) diff --git a/lib/StaticAnalyzer/Checkers/MallocOverflowSecurityChecker.cpp b/lib/StaticAnalyzer/Checkers/MallocOverflowSecurityChecker.cpp index daec4180a0c2..34425e314062 100644 --- a/lib/StaticAnalyzer/Checkers/MallocOverflowSecurityChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/MallocOverflowSecurityChecker.cpp @@ -20,9 +20,9 @@ #include "ClangSACheckers.h" #include "clang/AST/EvaluatedExprVisitor.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" -#include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" #include "llvm/ADT/SmallVector.h" using namespace clang; @@ -44,18 +44,18 @@ public: BugReporter &BR) const; void CheckMallocArgument( - llvm::SmallVectorImpl<MallocOverflowCheck> &PossibleMallocOverflows, + SmallVectorImpl<MallocOverflowCheck> &PossibleMallocOverflows, const Expr *TheArgument, ASTContext &Context) const; void OutputPossibleOverflows( - llvm::SmallVectorImpl<MallocOverflowCheck> &PossibleMallocOverflows, + SmallVectorImpl<MallocOverflowCheck> &PossibleMallocOverflows, const Decl *D, BugReporter &BR, AnalysisManager &mgr) const; }; } // end anonymous namespace void MallocOverflowSecurityChecker::CheckMallocArgument( - llvm::SmallVectorImpl<MallocOverflowCheck> &PossibleMallocOverflows, + SmallVectorImpl<MallocOverflowCheck> &PossibleMallocOverflows, const Expr *TheArgument, ASTContext &Context) const { @@ -111,7 +111,7 @@ namespace { class CheckOverflowOps : public EvaluatedExprVisitor<CheckOverflowOps> { public: - typedef llvm::SmallVectorImpl<MallocOverflowCheck> theVecType; + typedef SmallVectorImpl<MallocOverflowCheck> theVecType; private: theVecType &toScanFor; @@ -197,7 +197,7 @@ private: // detect the most blatent cases of overflow and educate the // programmer. void MallocOverflowSecurityChecker::OutputPossibleOverflows( - llvm::SmallVectorImpl<MallocOverflowCheck> &PossibleMallocOverflows, + SmallVectorImpl<MallocOverflowCheck> &PossibleMallocOverflows, const Decl *D, BugReporter &BR, AnalysisManager &mgr) const { // By far the most common case: nothing to check. if (PossibleMallocOverflows.empty()) @@ -230,13 +230,13 @@ void MallocOverflowSecurityChecker::checkASTCodeBody(const Decl *D, return; // A list of variables referenced in possibly overflowing malloc operands. - llvm::SmallVector<MallocOverflowCheck, 2> PossibleMallocOverflows; + SmallVector<MallocOverflowCheck, 2> PossibleMallocOverflows; for (CFG::iterator it = cfg->begin(), ei = cfg->end(); it != ei; ++it) { CFGBlock *block = *it; for (CFGBlock::iterator bi = block->begin(), be = block->end(); bi != be; ++bi) { - if (const CFGStmt *CS = bi->getAs<CFGStmt>()) { + if (Optional<CFGStmt> CS = bi->getAs<CFGStmt>()) { if (const CallExpr *TheCall = dyn_cast<CallExpr>(CS->getStmt())) { // Get the callee. const FunctionDecl *FD = TheCall->getDirectCallee(); diff --git a/lib/StaticAnalyzer/Checkers/MallocSizeofChecker.cpp b/lib/StaticAnalyzer/Checkers/MallocSizeofChecker.cpp index fb40f222b846..ce7d4ccf7a0a 100644 --- a/lib/StaticAnalyzer/Checkers/MallocSizeofChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/MallocSizeofChecker.cpp @@ -14,13 +14,14 @@ //===----------------------------------------------------------------------===// #include "ClangSACheckers.h" +#include "clang/AST/StmtVisitor.h" +#include "clang/AST/TypeLoc.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" +#include "llvm/Support/raw_ostream.h" using namespace clang; using namespace ento; @@ -225,7 +226,7 @@ public: OS << " is converted to a pointer of type '" << PointeeType.getAsString() << "', which is incompatible with " << "sizeof operand type '" << SizeofType.getAsString() << "'"; - llvm::SmallVector<SourceRange, 4> Ranges; + SmallVector<SourceRange, 4> Ranges; Ranges.push_back(i->AllocCall->getCallee()->getSourceRange()); Ranges.push_back(SFinder.Sizeofs[0]->getSourceRange()); if (TSI) diff --git a/lib/StaticAnalyzer/Checkers/NSAutoreleasePoolChecker.cpp b/lib/StaticAnalyzer/Checkers/NSAutoreleasePoolChecker.cpp index 3331bc8a9a8d..fc28e1fb7f49 100644 --- a/lib/StaticAnalyzer/Checkers/NSAutoreleasePoolChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/NSAutoreleasePoolChecker.cpp @@ -16,15 +16,15 @@ //===----------------------------------------------------------------------===// #include "ClangSACheckers.h" -#include "clang/StaticAnalyzer/Core/Checker.h" -#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclObjC.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" -#include "clang/AST/DeclObjC.h" -#include "clang/AST/Decl.h" using namespace clang; using namespace ento; diff --git a/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp b/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp index 7a66ec3a934f..9f01522eadbd 100644 --- a/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp @@ -16,14 +16,15 @@ //===----------------------------------------------------------------------===// #include "ClangSACheckers.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclObjC.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.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/AST/DeclObjC.h" -#include "clang/AST/Decl.h" -#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/raw_ostream.h" using namespace clang; using namespace ento; @@ -185,7 +186,7 @@ static void setFlag(ProgramStateRef state, SVal val, CheckerContext &C) { static QualType parameterTypeFromSVal(SVal val, CheckerContext &C) { const StackFrameContext * SFC = C.getLocationContext()->getCurrentStackFrame(); - if (const loc::MemRegionVal* X = dyn_cast<loc::MemRegionVal>(&val)) { + if (Optional<loc::MemRegionVal> X = val.getAs<loc::MemRegionVal>()) { const MemRegion* R = X->getRegion(); if (const VarRegion *VR = R->getAs<VarRegion>()) if (const StackArgumentsSpaceRegion * @@ -202,7 +203,7 @@ void NSOrCFErrorDerefChecker::checkLocation(SVal loc, bool isLoad, CheckerContext &C) const { if (!isLoad) return; - if (loc.isUndef() || !isa<Loc>(loc)) + if (loc.isUndef() || !loc.getAs<Loc>()) return; ASTContext &Ctx = C.getASTContext(); @@ -224,12 +225,12 @@ void NSOrCFErrorDerefChecker::checkLocation(SVal loc, bool isLoad, CFErrorII = &Ctx.Idents.get("CFErrorRef"); if (ShouldCheckNSError && IsNSError(parmT, NSErrorII)) { - setFlag<NSErrorOut>(state, state->getSVal(cast<Loc>(loc)), C); + setFlag<NSErrorOut>(state, state->getSVal(loc.castAs<Loc>()), C); return; } if (ShouldCheckCFError && IsCFError(parmT, CFErrorII)) { - setFlag<CFErrorOut>(state, state->getSVal(cast<Loc>(loc)), C); + setFlag<CFErrorOut>(state, state->getSVal(loc.castAs<Loc>()), C); return; } } @@ -251,18 +252,15 @@ void NSOrCFErrorDerefChecker::checkEvent(ImplicitNullDerefEvent event) const { return; // Storing to possible null NSError/CFErrorRef out parameter. + SmallString<128> Buf; + llvm::raw_svector_ostream os(Buf); - // Emit an error. - std::string err; - llvm::raw_string_ostream os(err); - os << "Potential null dereference. According to coding standards "; - - if (isNSError) - os << "in 'Creating and Returning NSError Objects' the parameter '"; - else - os << "documented in CoreFoundation/CFError.h the parameter '"; + os << "Potential null dereference. According to coding standards "; + os << (isNSError + ? "in 'Creating and Returning NSError Objects' the parameter" + : "documented in CoreFoundation/CFError.h the parameter"); - os << "' may be null."; + os << " may be null"; BugType *bug = 0; if (isNSError) diff --git a/lib/StaticAnalyzer/Checkers/NoReturnFunctionChecker.cpp b/lib/StaticAnalyzer/Checkers/NoReturnFunctionChecker.cpp index efb707294d56..0009e1b7cf49 100644 --- a/lib/StaticAnalyzer/Checkers/NoReturnFunctionChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/NoReturnFunctionChecker.cpp @@ -13,6 +13,7 @@ //===----------------------------------------------------------------------===// #include "ClangSACheckers.h" +#include "clang/AST/Attr.h" #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" @@ -47,7 +48,7 @@ void NoReturnFunctionChecker::checkPostStmt(const CallExpr *CE, if (!FD) return; - if (FD->getAttr<AnalyzerNoReturnAttr>()) + if (FD->getAttr<AnalyzerNoReturnAttr>() || FD->isNoReturn()) BuildSinks = true; else if (const IdentifierInfo *II = FD->getIdentifier()) { // HACK: Some functions are not marked noreturn, and don't return. @@ -100,6 +101,15 @@ static bool END_WITH_NULL isMultiArgSelector(const Selector *Sel, ...) { void NoReturnFunctionChecker::checkPostObjCMessage(const ObjCMethodCall &Msg, CheckerContext &C) const { + // Check if the method is annotated with analyzer_noreturn. + if (const ObjCMethodDecl *MD = Msg.getDecl()) { + MD = MD->getCanonicalDecl(); + if (MD->hasAttr<AnalyzerNoReturnAttr>()) { + C.generateSink(); + return; + } + } + // HACK: This entire check is to handle two messages in the Cocoa frameworks: // -[NSAssertionHandler // handleFailureInMethod:object:file:lineNumber:description:] diff --git a/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp b/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp new file mode 100644 index 000000000000..273a7a38824a --- /dev/null +++ b/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp @@ -0,0 +1,193 @@ +//===--- NonNullParamChecker.cpp - Undefined arguments 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 NonNullParamChecker, which checks for arguments expected not to +// be null due to: +// - the corresponding parameters being declared to have nonnull attribute +// - the corresponding parameters being references; since the call would form +// a reference to a null pointer +// +//===----------------------------------------------------------------------===// + +#include "ClangSACheckers.h" +#include "clang/AST/Attr.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" + +using namespace clang; +using namespace ento; + +namespace { +class NonNullParamChecker + : public Checker< check::PreCall > { + mutable OwningPtr<BugType> BTAttrNonNull; + mutable OwningPtr<BugType> BTNullRefArg; +public: + + void checkPreCall(const CallEvent &Call, CheckerContext &C) const; + + BugReport *genReportNullAttrNonNull(const ExplodedNode *ErrorN, + const Expr *ArgE) const; + BugReport *genReportReferenceToNullPointer(const ExplodedNode *ErrorN, + const Expr *ArgE) const; +}; +} // end anonymous namespace + +void NonNullParamChecker::checkPreCall(const CallEvent &Call, + CheckerContext &C) const { + const Decl *FD = Call.getDecl(); + if (!FD) + return; + + const NonNullAttr *Att = FD->getAttr<NonNullAttr>(); + + ProgramStateRef state = C.getState(); + + CallEvent::param_type_iterator TyI = Call.param_type_begin(), + TyE = Call.param_type_end(); + + for (unsigned idx = 0, count = Call.getNumArgs(); idx != count; ++idx){ + + // Check if the parameter is a reference. We want to report when reference + // to a null pointer is passed as a paramter. + bool haveRefTypeParam = false; + if (TyI != TyE) { + haveRefTypeParam = (*TyI)->isReferenceType(); + TyI++; + } + + bool haveAttrNonNull = Att && Att->isNonNull(idx); + + if (!haveRefTypeParam && !haveAttrNonNull) + continue; + + // If the value is unknown or undefined, we can't perform this check. + const Expr *ArgE = Call.getArgExpr(idx); + SVal V = Call.getArgSVal(idx); + Optional<DefinedSVal> DV = V.getAs<DefinedSVal>(); + if (!DV) + continue; + + // Process the case when the argument is not a location. + assert(!haveRefTypeParam || DV->getAs<Loc>()); + + if (haveAttrNonNull && !DV->getAs<Loc>()) { + // If the argument is a union type, we want to handle a potential + // transparent_union GCC extension. + if (!ArgE) + continue; + + QualType T = ArgE->getType(); + const RecordType *UT = T->getAsUnionType(); + if (!UT || !UT->getDecl()->hasAttr<TransparentUnionAttr>()) + continue; + + if (Optional<nonloc::CompoundVal> CSV = + DV->getAs<nonloc::CompoundVal>()) { + nonloc::CompoundVal::iterator CSV_I = CSV->begin(); + assert(CSV_I != CSV->end()); + V = *CSV_I; + DV = V.getAs<DefinedSVal>(); + assert(++CSV_I == CSV->end()); + if (!DV) + continue; + // Retrieve the corresponding expression. + if (const CompoundLiteralExpr *CE = dyn_cast<CompoundLiteralExpr>(ArgE)) + if (const InitListExpr *IE = + dyn_cast<InitListExpr>(CE->getInitializer())) + ArgE = dyn_cast<Expr>(*(IE->begin())); + + } else { + // FIXME: Handle LazyCompoundVals? + continue; + } + } + + ConstraintManager &CM = C.getConstraintManager(); + ProgramStateRef stateNotNull, stateNull; + llvm::tie(stateNotNull, stateNull) = CM.assumeDual(state, *DV); + + if (stateNull && !stateNotNull) { + // Generate an error node. Check for a null node in case + // we cache out. + if (ExplodedNode *errorNode = C.generateSink(stateNull)) { + + BugReport *R = 0; + if (haveAttrNonNull) + R = genReportNullAttrNonNull(errorNode, ArgE); + else if (haveRefTypeParam) + R = genReportReferenceToNullPointer(errorNode, ArgE); + + // Highlight the range of the argument that was null. + R->addRange(Call.getArgSourceRange(idx)); + + // Emit the bug report. + C.emitReport(R); + } + + // Always return. Either we cached out or we just emitted an error. + return; + } + + // If a pointer value passed the check we should assume that it is + // indeed not null from this point forward. + assert(stateNotNull); + state = stateNotNull; + } + + // If we reach here all of the arguments passed the nonnull check. + // If 'state' has been updated generated a new node. + C.addTransition(state); +} + +BugReport *NonNullParamChecker::genReportNullAttrNonNull( + const ExplodedNode *ErrorNode, const Expr *ArgE) const { + // Lazily allocate the BugType object if it hasn't already been + // created. Ownership is transferred to the BugReporter object once + // the BugReport is passed to 'EmitWarning'. + if (!BTAttrNonNull) + BTAttrNonNull.reset(new BugType( + "Argument with 'nonnull' attribute passed null", + "API")); + + BugReport *R = new BugReport(*BTAttrNonNull, + "Null pointer passed as an argument to a 'nonnull' parameter", + ErrorNode); + if (ArgE) + bugreporter::trackNullOrUndefValue(ErrorNode, ArgE, *R); + + return R; +} + +BugReport *NonNullParamChecker::genReportReferenceToNullPointer( + const ExplodedNode *ErrorNode, const Expr *ArgE) const { + if (!BTNullRefArg) + BTNullRefArg.reset(new BuiltinBug("Dereference of null pointer")); + + BugReport *R = new BugReport(*BTNullRefArg, + "Forming reference to null pointer", + ErrorNode); + if (ArgE) { + const Expr *ArgEDeref = bugreporter::getDerefExpr(ArgE); + if (ArgEDeref == 0) + ArgEDeref = ArgE; + bugreporter::trackNullOrUndefValue(ErrorNode, + ArgEDeref, + *R); + } + return R; + +} + +void ento::registerNonNullParamChecker(CheckerManager &mgr) { + mgr.registerChecker<NonNullParamChecker>(); +} diff --git a/lib/StaticAnalyzer/Checkers/ObjCAtSyncChecker.cpp b/lib/StaticAnalyzer/Checkers/ObjCAtSyncChecker.cpp index 9d84f52f934e..4018a66ecf57 100644 --- a/lib/StaticAnalyzer/Checkers/ObjCAtSyncChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/ObjCAtSyncChecker.cpp @@ -14,10 +14,10 @@ #include "ClangSACheckers.h" #include "clang/AST/StmtObjC.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.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/ExprEngine.h" using namespace clang; @@ -42,7 +42,7 @@ void ObjCAtSyncChecker::checkPreStmt(const ObjCAtSynchronizedStmt *S, SVal V = state->getSVal(Ex, C.getLocationContext()); // Uninitialized value used for the mutex? - if (isa<UndefinedVal>(V)) { + if (V.getAs<UndefinedVal>()) { if (ExplodedNode *N = C.generateSink()) { if (!BT_undef) BT_undef.reset(new BuiltinBug("Uninitialized value used as mutex " @@ -60,7 +60,7 @@ void ObjCAtSyncChecker::checkPreStmt(const ObjCAtSynchronizedStmt *S, // Check for null mutexes. ProgramStateRef notNullState, nullState; - llvm::tie(notNullState, nullState) = state->assume(cast<DefinedSVal>(V)); + llvm::tie(notNullState, nullState) = state->assume(V.castAs<DefinedSVal>()); if (nullState) { if (!notNullState) { diff --git a/lib/StaticAnalyzer/Checkers/ObjCContainersASTChecker.cpp b/lib/StaticAnalyzer/Checkers/ObjCContainersASTChecker.cpp index 63a84805e73e..4a0309de044e 100644 --- a/lib/StaticAnalyzer/Checkers/ObjCContainersASTChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/ObjCContainersASTChecker.cpp @@ -12,11 +12,11 @@ // //===----------------------------------------------------------------------===// #include "ClangSACheckers.h" -#include "clang/Analysis/AnalysisContext.h" #include "clang/AST/StmtVisitor.h" +#include "clang/Analysis/AnalysisContext.h" #include "clang/Basic/TargetInfo.h" -#include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" +#include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" #include "llvm/ADT/SmallString.h" #include "llvm/Support/raw_ostream.h" diff --git a/lib/StaticAnalyzer/Checkers/ObjCContainersChecker.cpp b/lib/StaticAnalyzer/Checkers/ObjCContainersChecker.cpp index 999c994cb1c6..b9e96ee99fc6 100644 --- a/lib/StaticAnalyzer/Checkers/ObjCContainersChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/ObjCContainersChecker.cpp @@ -17,12 +17,12 @@ //===----------------------------------------------------------------------===// #include "ClangSACheckers.h" +#include "clang/AST/ParentMap.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.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/AST/ParentMap.h" using namespace clang; using namespace ento; @@ -72,7 +72,8 @@ void ObjCContainersChecker::addSizeInfo(const Expr *Array, const Expr *Size, if (!ArraySym) return; - C.addTransition(State->set<ArraySizeMap>(ArraySym, cast<DefinedSVal>(SizeV))); + C.addTransition( + State->set<ArraySizeMap>(ArraySym, SizeV.castAs<DefinedSVal>())); return; } @@ -125,7 +126,7 @@ void ObjCContainersChecker::checkPreStmt(const CallExpr *CE, SVal IdxVal = State->getSVal(IdxExpr, C.getLocationContext()); if (IdxVal.isUnknownOrUndef()) return; - DefinedSVal Idx = cast<DefinedSVal>(IdxVal); + DefinedSVal Idx = IdxVal.castAs<DefinedSVal>(); // Now, check if 'Idx in [0, Size-1]'. const QualType T = IdxExpr->getType(); diff --git a/lib/StaticAnalyzer/Checkers/ObjCMissingSuperCallChecker.cpp b/lib/StaticAnalyzer/Checkers/ObjCMissingSuperCallChecker.cpp index e906e8aa3016..789b9f4cc19c 100644 --- a/lib/StaticAnalyzer/Checkers/ObjCMissingSuperCallChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/ObjCMissingSuperCallChecker.cpp @@ -14,30 +14,26 @@ //===----------------------------------------------------------------------===// #include "ClangSACheckers.h" -#include "clang/StaticAnalyzer/Core/Checker.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" -#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" -#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" -#include "clang/AST/ExprObjC.h" -#include "clang/AST/Expr.h" #include "clang/AST/DeclObjC.h" +#include "clang/AST/Expr.h" +#include "clang/AST/ExprObjC.h" #include "clang/AST/RecursiveASTVisitor.h" -#include "llvm/ADT/SmallString.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" +#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" #include "llvm/ADT/SmallSet.h" +#include "llvm/ADT/SmallString.h" #include "llvm/Support/raw_ostream.h" using namespace clang; using namespace ento; -static bool isUIViewControllerSubclass(ASTContext &Ctx, - const ObjCImplementationDecl *D) { - IdentifierInfo *ViewControllerII = &Ctx.Idents.get("UIViewController"); - const ObjCInterfaceDecl *ID = D->getClassInterface(); - - for ( ; ID; ID = ID->getSuperClass()) - if (ID->getIdentifier() == ViewControllerII) - return true; - return false; +namespace { +struct SelectorDescriptor { + const char *SelectorName; + unsigned ArgumentCount; +}; } //===----------------------------------------------------------------------===// @@ -71,9 +67,102 @@ namespace { class ObjCSuperCallChecker : public Checker< check::ASTDecl<ObjCImplementationDecl> > { public: + ObjCSuperCallChecker() : IsInitialized(false) {} + void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager &Mgr, BugReporter &BR) const; +private: + bool isCheckableClass(const ObjCImplementationDecl *D, + StringRef &SuperclassName) const; + void initializeSelectors(ASTContext &Ctx) const; + void fillSelectors(ASTContext &Ctx, ArrayRef<SelectorDescriptor> Sel, + StringRef ClassName) const; + mutable llvm::StringMap<llvm::SmallSet<Selector, 16> > SelectorsForClass; + mutable bool IsInitialized; }; + +} + +/// \brief Determine whether the given class has a superclass that we want +/// to check. The name of the found superclass is stored in SuperclassName. +/// +/// \param D The declaration to check for superclasses. +/// \param[out] SuperclassName On return, the found superclass name. +bool ObjCSuperCallChecker::isCheckableClass(const ObjCImplementationDecl *D, + StringRef &SuperclassName) const { + const ObjCInterfaceDecl *ID = D->getClassInterface(); + for ( ; ID ; ID = ID->getSuperClass()) + { + SuperclassName = ID->getIdentifier()->getName(); + if (SelectorsForClass.count(SuperclassName)) + return true; + } + return false; +} + +void ObjCSuperCallChecker::fillSelectors(ASTContext &Ctx, + ArrayRef<SelectorDescriptor> Sel, + StringRef ClassName) const { + llvm::SmallSet<Selector, 16> &ClassSelectors = SelectorsForClass[ClassName]; + // Fill the Selectors SmallSet with all selectors we want to check. + for (ArrayRef<SelectorDescriptor>::iterator I = Sel.begin(), E = Sel.end(); + I != E; ++I) { + SelectorDescriptor Descriptor = *I; + assert(Descriptor.ArgumentCount <= 1); // No multi-argument selectors yet. + + // Get the selector. + IdentifierInfo *II = &Ctx.Idents.get(Descriptor.SelectorName); + + Selector Sel = Ctx.Selectors.getSelector(Descriptor.ArgumentCount, &II); + ClassSelectors.insert(Sel); + } +} + +void ObjCSuperCallChecker::initializeSelectors(ASTContext &Ctx) const { + + { // Initialize selectors for: UIViewController + const SelectorDescriptor Selectors[] = { + { "addChildViewController", 1 }, + { "viewDidAppear", 1 }, + { "viewDidDisappear", 1 }, + { "viewWillAppear", 1 }, + { "viewWillDisappear", 1 }, + { "removeFromParentViewController", 0 }, + { "didReceiveMemoryWarning", 0 }, + { "viewDidUnload", 0 }, + { "viewDidLoad", 0 }, + { "viewWillUnload", 0 }, + { "updateViewConstraints", 0 }, + { "encodeRestorableStateWithCoder", 1 }, + { "restoreStateWithCoder", 1 }}; + + fillSelectors(Ctx, Selectors, "UIViewController"); + } + + { // Initialize selectors for: UIResponder + const SelectorDescriptor Selectors[] = { + { "resignFirstResponder", 0 }}; + + fillSelectors(Ctx, Selectors, "UIResponder"); + } + + { // Initialize selectors for: NSResponder + const SelectorDescriptor Selectors[] = { + { "encodeRestorableStateWithCoder", 1 }, + { "restoreStateWithCoder", 1 }}; + + fillSelectors(Ctx, Selectors, "NSResponder"); + } + + { // Initialize selectors for: NSDocument + const SelectorDescriptor Selectors[] = { + { "encodeRestorableStateWithCoder", 1 }, + { "restoreStateWithCoder", 1 }}; + + fillSelectors(Ctx, Selectors, "NSDocument"); + } + + IsInitialized = true; } void ObjCSuperCallChecker::checkASTDecl(const ObjCImplementationDecl *D, @@ -81,29 +170,15 @@ void ObjCSuperCallChecker::checkASTDecl(const ObjCImplementationDecl *D, BugReporter &BR) const { ASTContext &Ctx = BR.getContext(); - if (!isUIViewControllerSubclass(Ctx, D)) - return; - - const char *SelectorNames[] = - {"addChildViewController", "viewDidAppear", "viewDidDisappear", - "viewWillAppear", "viewWillDisappear", "removeFromParentViewController", - "didReceiveMemoryWarning", "viewDidUnload", "viewWillUnload", - "viewDidLoad"}; - const unsigned SelectorArgumentCounts[] = - {1, 1, 1, 1, 1, 0, 0, 0, 0, 0}; - const size_t SelectorCount = llvm::array_lengthof(SelectorNames); - assert(llvm::array_lengthof(SelectorArgumentCounts) == SelectorCount); + // We need to initialize the selector table once. + if (!IsInitialized) + initializeSelectors(Ctx); - // Fill the Selectors SmallSet with all selectors we want to check. - llvm::SmallSet<Selector, 16> Selectors; - for (size_t i = 0; i < SelectorCount; i++) { - unsigned ArgumentCount = SelectorArgumentCounts[i]; - const char *SelectorCString = SelectorNames[i]; + // Find out whether this class has a superclass that we are supposed to check. + StringRef SuperclassName; + if (!isCheckableClass(D, SuperclassName)) + return; - // Get the selector. - IdentifierInfo *II = &Ctx.Idents.get(SelectorCString); - Selectors.insert(Ctx.Selectors.getSelector(ArgumentCount, &II)); - } // Iterate over all instance methods. for (ObjCImplementationDecl::instmeth_iterator I = D->instmeth_begin(), @@ -111,7 +186,7 @@ void ObjCSuperCallChecker::checkASTDecl(const ObjCImplementationDecl *D, I != E; ++I) { Selector S = (*I)->getSelector(); // Find out whether this is a selector that we want to check. - if (!Selectors.count(S)) + if (!SelectorsForClass[SuperclassName].count(S)) continue; ObjCMethodDecl *MD = *I; @@ -130,12 +205,12 @@ void ObjCSuperCallChecker::checkASTDecl(const ObjCImplementationDecl *D, Mgr.getAnalysisDeclContext(D)); const char *Name = "Missing call to superclass"; - SmallString<256> Buf; + SmallString<320> Buf; llvm::raw_svector_ostream os(Buf); os << "The '" << S.getAsString() - << "' instance method in UIViewController subclass '" << *D - << "' is missing a [super " << S.getAsString() << "] call"; + << "' instance method in " << SuperclassName.str() << " subclass '" + << *D << "' is missing a [super " << S.getAsString() << "] call"; BR.EmitBasicReport(MD, Name, categories::CoreFoundationObjectiveC, os.str(), DLoc); @@ -161,15 +236,6 @@ void ento::registerObjCSuperCallChecker(CheckerManager &Mgr) { improvements like being able to allow for the super-call to be done in a called method would be good too. -*** trivial cases: -UIResponder subclasses -- resignFirstResponder - -NSResponder subclasses -- cursorUpdate - -*** more difficult cases: - UIDocument subclasses - finishedHandlingError:recovered: (is multi-arg) - finishedHandlingError:recovered: (is multi-arg) diff --git a/lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp b/lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp index 98d2a85ace36..8506e08b2b98 100644 --- a/lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp @@ -37,13 +37,14 @@ //===----------------------------------------------------------------------===// #include "ClangSACheckers.h" +#include "clang/AST/ParentMap.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" -#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" -#include "clang/AST/ParentMap.h" +#include "llvm/Support/raw_ostream.h" using namespace clang; using namespace ento; @@ -122,9 +123,10 @@ static SelfFlagEnum getSelfFlags(SVal val, CheckerContext &C) { static void addSelfFlag(ProgramStateRef state, SVal val, SelfFlagEnum flag, CheckerContext &C) { // We tag the symbol that the SVal wraps. - if (SymbolRef sym = val.getAsSymbol()) + if (SymbolRef sym = val.getAsSymbol()) { state = state->set<SelfFlag>(sym, getSelfFlags(val, state) | flag); - C.addTransition(state); + C.addTransition(state); + } } static bool hasSelfFlag(SVal val, SelfFlagEnum flag, CheckerContext &C) { @@ -253,7 +255,7 @@ void ObjCSelfInitChecker::checkPreCall(const CallEvent &CE, 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); + unsigned selfFlags = getSelfFlags(state->getSVal(argV.castAs<Loc>()), C); C.addTransition(state->set<PreCallSelfFlags>(selfFlags)); return; } else if (hasSelfFlag(argV, SelfFlag_Self, C)) { @@ -284,7 +286,7 @@ void ObjCSelfInitChecker::checkPostCall(const CallEvent &CE, // If the address of 'self' is being passed to the call, assume that the // 'self' after the call will have the same flags. // EX: log(&self) - addSelfFlag(state, state->getSVal(cast<Loc>(argV)), prevFlags, C); + addSelfFlag(state, state->getSVal(argV.castAs<Loc>()), prevFlags, C); return; } else if (hasSelfFlag(argV, SelfFlag_Self, C)) { // If 'self' is passed to the call by value, assume that the function @@ -302,11 +304,16 @@ void ObjCSelfInitChecker::checkPostCall(const CallEvent &CE, void ObjCSelfInitChecker::checkLocation(SVal location, bool isLoad, const Stmt *S, CheckerContext &C) const { + if (!shouldRunOnFunctionOrMethod(dyn_cast<NamedDecl>( + C.getCurrentAnalysisDeclContext()->getDecl()))) + return; + // Tag the result of a load from 'self' so that we can easily know that the // value is the object that 'self' points to. ProgramStateRef state = C.getState(); if (isSelfVar(location, C)) - addSelfFlag(state, state->getSVal(cast<Loc>(location)), SelfFlag_Self, C); + addSelfFlag(state, state->getSVal(location.castAs<Loc>()), SelfFlag_Self, + C); } @@ -411,10 +418,10 @@ static bool isSelfVar(SVal location, CheckerContext &C) { AnalysisDeclContext *analCtx = C.getCurrentAnalysisDeclContext(); if (!analCtx->getSelfDecl()) return false; - if (!isa<loc::MemRegionVal>(location)) + if (!location.getAs<loc::MemRegionVal>()) return false; - loc::MemRegionVal MRV = cast<loc::MemRegionVal>(location); + loc::MemRegionVal MRV = location.castAs<loc::MemRegionVal>(); if (const DeclRegion *DR = dyn_cast<DeclRegion>(MRV.stripCasts())) return (DR->getDecl() == analCtx->getSelfDecl()); diff --git a/lib/StaticAnalyzer/Checkers/ObjCUnusedIVarsChecker.cpp b/lib/StaticAnalyzer/Checkers/ObjCUnusedIVarsChecker.cpp index 582269c33279..c66c7d019350 100644 --- a/lib/StaticAnalyzer/Checkers/ObjCUnusedIVarsChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/ObjCUnusedIVarsChecker.cpp @@ -14,14 +14,15 @@ //===----------------------------------------------------------------------===// #include "ClangSACheckers.h" -#include "clang/StaticAnalyzer/Core/Checker.h" -#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" -#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" -#include "clang/AST/ExprObjC.h" -#include "clang/AST/Expr.h" +#include "clang/AST/Attr.h" #include "clang/AST/DeclObjC.h" +#include "clang/AST/Expr.h" +#include "clang/AST/ExprObjC.h" #include "clang/Basic/LangOptions.h" #include "clang/Basic/SourceManager.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" +#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" +#include "clang/StaticAnalyzer/Core/Checker.h" using namespace clang; using namespace ento; @@ -88,10 +89,11 @@ static void Scan(IvarUsageMap& M, const ObjCContainerDecl *D) { Scan(M, *I); // Scan the associated categories as well. - for (const ObjCCategoryDecl *CD = - ID->getClassInterface()->getCategoryList(); CD ; - CD = CD->getNextClassCategory()) { - if (const ObjCCategoryImplDecl *CID = CD->getImplementation()) + for (ObjCInterfaceDecl::visible_categories_iterator + Cat = ID->getClassInterface()->visible_categories_begin(), + CatEnd = ID->getClassInterface()->visible_categories_end(); + Cat != CatEnd; ++Cat) { + if (const ObjCCategoryImplDecl *CID = Cat->getImplementation()) Scan(M, CID); } } diff --git a/lib/StaticAnalyzer/Checkers/PointerArithChecker.cpp b/lib/StaticAnalyzer/Checkers/PointerArithChecker.cpp index b5d9959b8531..bcbfacdb1774 100644 --- a/lib/StaticAnalyzer/Checkers/PointerArithChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/PointerArithChecker.cpp @@ -13,10 +13,10 @@ //===----------------------------------------------------------------------===// #include "ClangSACheckers.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.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; diff --git a/lib/StaticAnalyzer/Checkers/PointerSubChecker.cpp b/lib/StaticAnalyzer/Checkers/PointerSubChecker.cpp index 47da87f0bcc6..07c82d461941 100644 --- a/lib/StaticAnalyzer/Checkers/PointerSubChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/PointerSubChecker.cpp @@ -14,10 +14,10 @@ //===----------------------------------------------------------------------===// #include "ClangSACheckers.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.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; diff --git a/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp b/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp index d9b638469525..ffb8cf20207b 100644 --- a/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp @@ -13,10 +13,10 @@ //===----------------------------------------------------------------------===// #include "ClangSACheckers.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.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/ImmutableList.h" @@ -98,7 +98,7 @@ void PthreadLockChecker::AcquireLock(CheckerContext &C, const CallExpr *CE, if (X.isUnknownOrUndef()) return; - DefinedSVal retVal = cast<DefinedSVal>(X); + DefinedSVal retVal = X.castAs<DefinedSVal>(); if (state->contains<LockSet>(lockR)) { if (!BT_doublelock) diff --git a/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp b/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp index 304051c1394c..79409e85bda4 100644 --- a/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp @@ -13,16 +13,17 @@ //===----------------------------------------------------------------------===// #include "ClangSACheckers.h" -#include "clang/AST/DeclObjC.h" +#include "clang/AST/Attr.h" #include "clang/AST/DeclCXX.h" +#include "clang/AST/DeclObjC.h" +#include "clang/AST/ParentMap.h" +#include "clang/Analysis/DomainSpecific/CocoaConventions.h" #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/Checker.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" @@ -31,8 +32,8 @@ #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/SmallString.h" #include "llvm/ADT/StringExtras.h" #include <cstdarg> @@ -49,7 +50,6 @@ using llvm::StrInStrNoCase; enum ArgEffect { DoNothing, Autorelease, Dealloc, DecRef, DecRefMsg, DecRefBridgedTransfered, IncRefMsg, IncRef, MakeCollectable, MayEscape, - NewAutoreleasePool, // Stop tracking the argument - the effect of the call is // unknown. @@ -782,6 +782,10 @@ public: const RetainSummary *getStandardMethodSummary(const ObjCMethodDecl *MD, Selector S, QualType RetTy); + /// Determine if there is a special return effect for this function or method. + Optional<RetEffect> getRetEffectFromAnnotations(QualType RetTy, + const Decl *D); + void updateSummaryFromAnnotations(const RetainSummary *&Summ, const ObjCMethodDecl *MD); @@ -894,7 +898,6 @@ static ArgEffect getStopTrackingHardEquivalent(ArgEffect E) { case IncRefMsg: case MakeCollectable: case MayEscape: - case NewAutoreleasePool: case StopTracking: case StopTrackingHard: return StopTrackingHard; @@ -1134,12 +1137,7 @@ RetainSummaryManager::getFunctionSummary(const FunctionDecl *FD) { if (S) break; - if (RetTy->isPointerType()) { - if (FD->getAttr<CFAuditedTransferAttr>()) { - S = getCFCreateGetRuleSummary(FD); - break; - } - + if (RetTy->isPointerType()) { // For CoreFoundation ('CF') types. if (cocoa::isRefType(RetTy, "CF", FName)) { if (isRetain(FD, FName)) @@ -1170,6 +1168,11 @@ RetainSummaryManager::getFunctionSummary(const FunctionDecl *FD) { break; } + if (FD->getAttr<CFAuditedTransferAttr>()) { + S = getCFCreateGetRuleSummary(FD); + break; + } + break; } @@ -1272,6 +1275,30 @@ RetainSummaryManager::getCFSummaryGetRule(const FunctionDecl *FD) { // Summary creation for Selectors. //===----------------------------------------------------------------------===// +Optional<RetEffect> +RetainSummaryManager::getRetEffectFromAnnotations(QualType RetTy, + const Decl *D) { + if (cocoa::isCocoaObjectRef(RetTy)) { + if (D->getAttr<NSReturnsRetainedAttr>()) + return ObjCAllocRetE; + + if (D->getAttr<NSReturnsNotRetainedAttr>() || + D->getAttr<NSReturnsAutoreleasedAttr>()) + return RetEffect::MakeNotOwned(RetEffect::ObjC); + + } else if (!RetTy->isPointerType()) { + return None; + } + + if (D->getAttr<CFReturnsRetainedAttr>()) + return RetEffect::MakeOwned(RetEffect::CF, true); + + if (D->getAttr<CFReturnsNotRetainedAttr>()) + return RetEffect::MakeNotOwned(RetEffect::CF); + + return None; +} + void RetainSummaryManager::updateSummaryFromAnnotations(const RetainSummary *&Summ, const FunctionDecl *FD) { @@ -1286,39 +1313,15 @@ RetainSummaryManager::updateSummaryFromAnnotations(const RetainSummary *&Summ, for (FunctionDecl::param_const_iterator pi = FD->param_begin(), pe = FD->param_end(); pi != pe; ++pi, ++parm_idx) { const ParmVarDecl *pd = *pi; - if (pd->getAttr<NSConsumedAttr>()) { - if (!GCEnabled) { - Template->addArg(AF, parm_idx, DecRef); - } - } else if (pd->getAttr<CFConsumedAttr>()) { + if (pd->getAttr<NSConsumedAttr>()) + Template->addArg(AF, parm_idx, DecRefMsg); + else if (pd->getAttr<CFConsumedAttr>()) Template->addArg(AF, parm_idx, DecRef); - } } QualType RetTy = FD->getResultType(); - - // Determine if there is a special return effect for this method. - if (cocoa::isCocoaObjectRef(RetTy)) { - if (FD->getAttr<NSReturnsRetainedAttr>()) { - Template->setRetEffect(ObjCAllocRetE); - } - else if (FD->getAttr<CFReturnsRetainedAttr>()) { - Template->setRetEffect(RetEffect::MakeOwned(RetEffect::CF, true)); - } - else if (FD->getAttr<NSReturnsNotRetainedAttr>()) { - Template->setRetEffect(RetEffect::MakeNotOwned(RetEffect::ObjC)); - } - else if (FD->getAttr<CFReturnsNotRetainedAttr>()) { - Template->setRetEffect(RetEffect::MakeNotOwned(RetEffect::CF)); - } - } else if (RetTy->getAs<PointerType>()) { - if (FD->getAttr<CFReturnsRetainedAttr>()) { - Template->setRetEffect(RetEffect::MakeOwned(RetEffect::CF, true)); - } - else if (FD->getAttr<CFReturnsNotRetainedAttr>()) { - Template->setRetEffect(RetEffect::MakeNotOwned(RetEffect::CF)); - } - } + if (Optional<RetEffect> RetE = getRetEffectFromAnnotations(RetTy, FD)) + Template->setRetEffect(*RetE); } void @@ -1329,13 +1332,10 @@ RetainSummaryManager::updateSummaryFromAnnotations(const RetainSummary *&Summ, assert(Summ && "Must have a valid summary to add annotations to"); RetainSummaryTemplate Template(Summ, *this); - bool isTrackedLoc = false; // Effects on the receiver. - if (MD->getAttr<NSConsumesSelfAttr>()) { - if (!GCEnabled) - Template->setReceiverEffect(DecRefMsg); - } + if (MD->getAttr<NSConsumesSelfAttr>()) + Template->setReceiverEffect(DecRefMsg); // Effects on the parameters. unsigned parm_idx = 0; @@ -1343,37 +1343,16 @@ RetainSummaryManager::updateSummaryFromAnnotations(const RetainSummary *&Summ, pi=MD->param_begin(), pe=MD->param_end(); pi != pe; ++pi, ++parm_idx) { const ParmVarDecl *pd = *pi; - if (pd->getAttr<NSConsumedAttr>()) { - if (!GCEnabled) - Template->addArg(AF, parm_idx, DecRef); - } - else if(pd->getAttr<CFConsumedAttr>()) { + if (pd->getAttr<NSConsumedAttr>()) + Template->addArg(AF, parm_idx, DecRefMsg); + else if (pd->getAttr<CFConsumedAttr>()) { Template->addArg(AF, parm_idx, DecRef); } } - // Determine if there is a special return effect for this method. - if (cocoa::isCocoaObjectRef(MD->getResultType())) { - if (MD->getAttr<NSReturnsRetainedAttr>()) { - Template->setRetEffect(ObjCAllocRetE); - return; - } - if (MD->getAttr<NSReturnsNotRetainedAttr>()) { - Template->setRetEffect(RetEffect::MakeNotOwned(RetEffect::ObjC)); - return; - } - - isTrackedLoc = true; - } else { - isTrackedLoc = MD->getResultType()->getAs<PointerType>() != NULL; - } - - if (isTrackedLoc) { - if (MD->getAttr<CFReturnsRetainedAttr>()) - Template->setRetEffect(RetEffect::MakeOwned(RetEffect::CF, true)); - else if (MD->getAttr<CFReturnsNotRetainedAttr>()) - Template->setRetEffect(RetEffect::MakeNotOwned(RetEffect::CF)); - } + QualType RetTy = MD->getResultType(); + if (Optional<RetEffect> RetE = getRetEffectFromAnnotations(RetTy, MD)) + Template->setRetEffect(*RetE); } const RetainSummary * @@ -1567,10 +1546,6 @@ void RetainSummaryManager::InitializeMethodSummaries() { Summ = getPersistentSummary(NoRet, DecRefMsg); addNSObjectMethSummary(GetNullarySelector("release", Ctx), Summ); - // Create the "drain" selector. - Summ = getPersistentSummary(NoRet, isGCEnabled() ? DoNothing : DecRef); - addNSObjectMethSummary(GetNullarySelector("drain", Ctx), Summ); - // Create the -dealloc summary. Summ = getPersistentSummary(NoRet, Dealloc); addNSObjectMethSummary(GetNullarySelector("dealloc", Ctx), Summ); @@ -1579,10 +1554,6 @@ void RetainSummaryManager::InitializeMethodSummaries() { Summ = getPersistentSummary(NoRet, Autorelease); addNSObjectMethSummary(GetNullarySelector("autorelease", Ctx), Summ); - // Specially handle NSAutoreleasePool. - addInstMethSummary("NSAutoreleasePool", "init", - getPersistentSummary(NoRet, NewAutoreleasePool)); - // For NSWindow, allocated objects are (initially) self-owned. // FIXME: For now we opt for false negatives with NSWindow, as these objects // self-own themselves. However, they only do this once they are displayed. @@ -1601,10 +1572,11 @@ void RetainSummaryManager::InitializeMethodSummaries() { // as for NSWindow objects. addClassMethSummary("NSPanel", "alloc", NoTrackYet); - // Don't track allocated autorelease pools yet, as it is okay to prematurely + // Don't track allocated autorelease pools, as it is okay to prematurely // exit a method. addClassMethSummary("NSAutoreleasePool", "alloc", NoTrackYet); addClassMethSummary("NSAutoreleasePool", "allocWithZone", NoTrackYet, false); + addClassMethSummary("NSAutoreleasePool", "new", NoTrackYet); // Create summaries QCRenderer/QCView -createSnapShotImageOfType: addInstMethSummary("QCRenderer", AllocSumm, @@ -1872,7 +1844,7 @@ PathDiagnosticPiece *CFRefReportVisitor::VisitNode(const ExplodedNode *N, BugReport &BR) { // FIXME: We will eventually need to handle non-statement-based events // (__attribute__((cleanup))). - if (!isa<StmtPoint>(N->getLocation())) + if (!N->getLocation().getAs<StmtPoint>()) return NULL; // Check if the type state has changed. @@ -1894,7 +1866,7 @@ PathDiagnosticPiece *CFRefReportVisitor::VisitNode(const ExplodedNode *N, // This is the allocation site since the previous node had no bindings // for this symbol. if (!PrevT) { - const Stmt *S = cast<StmtPoint>(N->getLocation()).getStmt(); + const Stmt *S = N->getLocation().castAs<StmtPoint>().getStmt(); if (isa<ObjCArrayLiteral>(S)) { os << "NSArray literal is an object with a +0 retain count"; @@ -1984,7 +1956,7 @@ PathDiagnosticPiece *CFRefReportVisitor::VisitNode(const ExplodedNode *N, if (const RetainSummary *Summ = SummaryLog.lookup(OrigNode)) { // We only have summaries attached to nodes after evaluating CallExpr and // ObjCMessageExprs. - const Stmt *S = cast<StmtPoint>(N->getLocation()).getStmt(); + const Stmt *S = N->getLocation().castAs<StmtPoint>().getStmt(); if (const CallExpr *CE = dyn_cast<CallExpr>(S)) { // Iterate through the parameter expressions and see if the symbol @@ -2033,7 +2005,7 @@ PathDiagnosticPiece *CFRefReportVisitor::VisitNode(const ExplodedNode *N, // Specially handle CFMakeCollectable and friends. if (contains(AEffects, MakeCollectable)) { // Get the name of the function. - const Stmt *S = cast<StmtPoint>(N->getLocation()).getStmt(); + const Stmt *S = N->getLocation().castAs<StmtPoint>().getStmt(); SVal X = CurrSt->getSValAsScalarOrLoc(cast<CallExpr>(S)->getCallee(), LCtx); const FunctionDecl *FD = X.getAsFunctionDecl(); @@ -2141,7 +2113,7 @@ PathDiagnosticPiece *CFRefReportVisitor::VisitNode(const ExplodedNode *N, if (os.str().empty()) return 0; // We have nothing to say! - const Stmt *S = cast<StmtPoint>(N->getLocation()).getStmt(); + const Stmt *S = N->getLocation().castAs<StmtPoint>().getStmt(); PathDiagnosticLocation Pos(S, BRC.getSourceManager(), N->getLocationContext()); PathDiagnosticPiece *P = new PathDiagnosticEventPiece(Pos, os.str()); @@ -2278,7 +2250,7 @@ CFRefLeakReportVisitor::getEndPath(BugReporterContext &BRC, } } else if (RV->getKind() == RefVal::ErrorGCLeakReturned) { - ObjCMethodDecl &MD = cast<ObjCMethodDecl>(EndN->getCodeDecl()); + const ObjCMethodDecl &MD = cast<ObjCMethodDecl>(EndN->getCodeDecl()); os << " and returned from method '" << MD.getSelector().getAsString() << "' is potentially leaked when using garbage collection. Callers " "of this method do not expect a returned object with a +1 retain " @@ -2318,10 +2290,10 @@ CFRefLeakReport::CFRefLeakReport(CFRefBug &D, const LangOptions &LOpts, // implicit call. (Currently there are no such allocations in Cocoa, though.) const Stmt *AllocStmt; ProgramPoint P = AllocNode->getLocation(); - if (CallExitEnd *Exit = dyn_cast<CallExitEnd>(&P)) + if (Optional<CallExitEnd> Exit = P.getAs<CallExitEnd>()) AllocStmt = Exit->getCalleeContext()->getCallSite(); else - AllocStmt = cast<PostStmt>(P).getStmt(); + AllocStmt = P.castAs<PostStmt>().getStmt(); assert(AllocStmt && "All allocations must come from explicit calls"); Location = PathDiagnosticLocation::createBegin(AllocStmt, SMgr, n->getLocationContext()); @@ -2349,7 +2321,7 @@ class RetainCountChecker : public Checker< check::Bind, check::DeadSymbols, check::EndAnalysis, - check::EndPath, + check::EndFunction, check::PostStmt<BlockExpr>, check::PostStmt<CastExpr>, check::PostStmt<ObjCArrayLiteral>, @@ -2511,7 +2483,7 @@ public: ProgramStateRef checkRegionChanges(ProgramStateRef state, - const StoreManager::InvalidatedSymbols *invalidated, + const InvalidatedSymbols *invalidated, ArrayRef<const MemRegion *> ExplicitRegions, ArrayRef<const MemRegion *> Regions, const CallEvent *Call) const; @@ -2526,7 +2498,7 @@ public: SymbolRef Sym, ProgramStateRef state) const; void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const; - void checkEndPath(CheckerContext &C) const; + void checkEndFunction(CheckerContext &C) const; ProgramStateRef updateSymbol(ProgramStateRef state, SymbolRef sym, RefVal V, ArgEffect E, RefVal::Kind &hasErr, @@ -2544,7 +2516,7 @@ public: SymbolRef sid, RefVal V, SmallVectorImpl<SymbolRef> &Leaked) const; - std::pair<ExplodedNode *, ProgramStateRef > + ProgramStateRef handleAutoreleaseCounts(ProgramStateRef state, ExplodedNode *Pred, const ProgramPointTag *Tag, CheckerContext &Ctx, SymbolRef Sym, RefVal V) const; @@ -2601,7 +2573,7 @@ void RetainCountChecker::checkPostStmt(const BlockExpr *BE, MemRegionManager &MemMgr = C.getSValBuilder().getRegionManager(); for ( ; I != E; ++I) { - const VarRegion *VR = *I; + const VarRegion *VR = I.getCapturedRegion(); if (VR->getSuperRegion() == R) { VR = MemMgr.getVarRegion(VR->getDecl(), LC); } @@ -2940,9 +2912,6 @@ RetainCountChecker::updateSymbol(ProgramStateRef state, SymbolRef sym, case MakeCollectable: E = C.isObjCGCEnabled() ? DecRef : DoNothing; break; - case NewAutoreleasePool: - E = C.isObjCGCEnabled() ? DoNothing : NewAutoreleasePool; - break; } // Handle all use-after-releases. @@ -2982,10 +2951,6 @@ RetainCountChecker::updateSymbol(ProgramStateRef state, SymbolRef sym, } break; - case NewAutoreleasePool: - assert(!C.isObjCGCEnabled()); - return state; - case MayEscape: if (V.getKind() == RefVal::Owned) { V = V ^ RefVal::NotOwned; @@ -3175,7 +3140,8 @@ bool RetainCountChecker::evalCall(const CallExpr *CE, CheckerContext &C) const { Binding = getRefBinding(state, Sym); // Invalidate the argument region. - state = state->invalidateRegions(ArgRegion, CE, C.blockCount(), LCtx); + state = state->invalidateRegions(ArgRegion, CE, C.blockCount(), LCtx, + /*CausesPointerEscape*/ false); // Restore the refcount status of the argument. if (Binding) @@ -3259,11 +3225,10 @@ void RetainCountChecker::checkPreStmt(const ReturnStmt *S, // Update the autorelease counts. static SimpleProgramPointTag AutoreleaseTag("RetainCountChecker : Autorelease"); - llvm::tie(Pred, state) = handleAutoreleaseCounts(state, Pred, &AutoreleaseTag, - C, Sym, X); + state = handleAutoreleaseCounts(state, Pred, &AutoreleaseTag, C, Sym, X); // Did we cache out? - if (!Pred) + if (!state) return; // Get the updated binding. @@ -3374,7 +3339,7 @@ void RetainCountChecker::checkBind(SVal loc, SVal val, const Stmt *S, // does not understand. ProgramStateRef state = C.getState(); - if (loc::MemRegionVal *regionLoc = dyn_cast<loc::MemRegionVal>(&loc)) { + if (Optional<loc::MemRegionVal> regionLoc = loc.getAs<loc::MemRegionVal>()) { escapes = !regionLoc->getRegion()->hasStackStorage(); if (!escapes) { @@ -3443,7 +3408,7 @@ ProgramStateRef RetainCountChecker::evalAssume(ProgramStateRef state, ProgramStateRef RetainCountChecker::checkRegionChanges(ProgramStateRef state, - const StoreManager::InvalidatedSymbols *invalidated, + const InvalidatedSymbols *invalidated, ArrayRef<const MemRegion *> ExplicitRegions, ArrayRef<const MemRegion *> Regions, const CallEvent *Call) const { @@ -3457,7 +3422,7 @@ RetainCountChecker::checkRegionChanges(ProgramStateRef state, WhitelistedSymbols.insert(SR->getSymbol()); } - for (StoreManager::InvalidatedSymbols::const_iterator I=invalidated->begin(), + for (InvalidatedSymbols::const_iterator I=invalidated->begin(), E = invalidated->end(); I!=E; ++I) { SymbolRef sym = *I; if (WhitelistedSymbols.count(sym)) @@ -3472,8 +3437,8 @@ RetainCountChecker::checkRegionChanges(ProgramStateRef state, // Handle dead symbols and end-of-path. //===----------------------------------------------------------------------===// -std::pair<ExplodedNode *, ProgramStateRef > -RetainCountChecker::handleAutoreleaseCounts(ProgramStateRef state, +ProgramStateRef +RetainCountChecker::handleAutoreleaseCounts(ProgramStateRef state, ExplodedNode *Pred, const ProgramPointTag *Tag, CheckerContext &Ctx, @@ -3482,7 +3447,7 @@ RetainCountChecker::handleAutoreleaseCounts(ProgramStateRef state, // No autorelease counts? Nothing to be done. if (!ACnt) - return std::make_pair(Pred, state); + return state; assert(!Ctx.isObjCGCEnabled() && "Autorelease counts in GC mode?"); unsigned Cnt = V.getCount(); @@ -3500,14 +3465,10 @@ RetainCountChecker::handleAutoreleaseCounts(ProgramStateRef state, else V = V ^ RefVal::NotOwned; } else { - V.setCount(Cnt - ACnt); + V.setCount(V.getCount() - ACnt); V.setAutoreleaseCount(0); } - state = setRefBinding(state, Sym, V); - ExplodedNode *N = Ctx.addTransition(state, Pred, Tag); - if (N == 0) - state = 0; - return std::make_pair(N, state); + return setRefBinding(state, Sym, V); } // Woah! More autorelease counts then retain counts left. @@ -3534,7 +3495,7 @@ RetainCountChecker::handleAutoreleaseCounts(ProgramStateRef state, Ctx.emitReport(report); } - return std::make_pair((ExplodedNode *)0, (ProgramStateRef )0); + return 0; } ProgramStateRef @@ -3559,9 +3520,6 @@ RetainCountChecker::processLeaks(ProgramStateRef state, SmallVectorImpl<SymbolRef> &Leaked, CheckerContext &Ctx, ExplodedNode *Pred) const { - if (Leaked.empty()) - return Pred; - // Generate an intermediate node representing the leak point. ExplodedNode *N = Ctx.addTransition(state, Pred); @@ -3584,14 +3542,14 @@ RetainCountChecker::processLeaks(ProgramStateRef state, return N; } -void RetainCountChecker::checkEndPath(CheckerContext &Ctx) const { +void RetainCountChecker::checkEndFunction(CheckerContext &Ctx) const { ProgramStateRef state = Ctx.getState(); RefBindingsTy B = state->get<RefBindings>(); ExplodedNode *Pred = Ctx.getPredecessor(); for (RefBindingsTy::iterator I = B.begin(), E = B.end(); I != E; ++I) { - llvm::tie(Pred, state) = handleAutoreleaseCounts(state, Pred, /*Tag=*/0, - Ctx, I->first, I->second); + state = handleAutoreleaseCounts(state, Pred, /*Tag=*/0, Ctx, + I->first, I->second); if (!state) return; } @@ -3631,6 +3589,7 @@ void RetainCountChecker::checkDeadSymbols(SymbolReaper &SymReaper, ProgramStateRef state = C.getState(); RefBindingsTy B = state->get<RefBindings>(); + SmallVector<SymbolRef, 10> Leaked; // Update counts from autorelease pools for (SymbolReaper::dead_iterator I = SymReaper.dead_begin(), @@ -3640,20 +3599,19 @@ void RetainCountChecker::checkDeadSymbols(SymbolReaper &SymReaper, // Use the symbol as the tag. // FIXME: This might not be as unique as we would like. const ProgramPointTag *Tag = getDeadSymbolTag(Sym); - llvm::tie(Pred, state) = handleAutoreleaseCounts(state, Pred, Tag, C, - Sym, *T); + state = handleAutoreleaseCounts(state, Pred, Tag, C, Sym, *T); if (!state) return; + + // Fetch the new reference count from the state, and use it to handle + // this symbol. + state = handleSymbolDeath(state, *I, *getRefBinding(state, Sym), Leaked); } } - B = state->get<RefBindings>(); - SmallVector<SymbolRef, 10> Leaked; - - for (SymbolReaper::dead_iterator I = SymReaper.dead_begin(), - E = SymReaper.dead_end(); I != E; ++I) { - if (const RefVal *T = B.lookup(*I)) - state = handleSymbolDeath(state, *I, *T, Leaked); + if (Leaked.empty()) { + C.addTransition(state); + return; } Pred = processLeaks(state, Leaked, C, Pred); @@ -3663,10 +3621,13 @@ void RetainCountChecker::checkDeadSymbols(SymbolReaper &SymReaper, return; // Now generate a new node that nukes the old bindings. + // The only bindings left at this point are the leaked symbols. RefBindingsTy::Factory &F = state->get_context<RefBindings>(); + B = state->get<RefBindings>(); - for (SymbolReaper::dead_iterator I = SymReaper.dead_begin(), - E = SymReaper.dead_end(); I != E; ++I) + for (SmallVectorImpl<SymbolRef>::iterator I = Leaked.begin(), + E = Leaked.end(); + I != E; ++I) B = F.remove(B, *I); state = state->set<RefBindings>(B); @@ -3678,8 +3639,10 @@ void RetainCountChecker::printState(raw_ostream &Out, ProgramStateRef State, RefBindingsTy B = State->get<RefBindings>(); - if (!B.isEmpty()) - Out << Sep << NL; + if (B.isEmpty()) + return; + + Out << Sep << NL; for (RefBindingsTy::iterator I = B.begin(), E = B.end(); I != E; ++I) { Out << I->first << " : "; diff --git a/lib/StaticAnalyzer/Checkers/ReturnPointerRangeChecker.cpp b/lib/StaticAnalyzer/Checkers/ReturnPointerRangeChecker.cpp index f3560aad8de2..fe253b719b50 100644 --- a/lib/StaticAnalyzer/Checkers/ReturnPointerRangeChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/ReturnPointerRangeChecker.cpp @@ -13,10 +13,10 @@ //===----------------------------------------------------------------------===// #include "ClangSACheckers.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.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/ExprEngine.h" using namespace clang; @@ -46,7 +46,7 @@ void ReturnPointerRangeChecker::checkPreStmt(const ReturnStmt *RS, if (!ER) return; - DefinedOrUnknownSVal Idx = cast<DefinedOrUnknownSVal>(ER->getIndex()); + DefinedOrUnknownSVal Idx = ER->getIndex().castAs<DefinedOrUnknownSVal>(); // Zero index is always in bound, this also passes ElementRegions created for // pointer casts. if (Idx.isZeroConstant()) diff --git a/lib/StaticAnalyzer/Checkers/ReturnUndefChecker.cpp b/lib/StaticAnalyzer/Checkers/ReturnUndefChecker.cpp index 37ec1aa7bea0..7a5d99360108 100644 --- a/lib/StaticAnalyzer/Checkers/ReturnUndefChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/ReturnUndefChecker.cpp @@ -14,19 +14,23 @@ //===----------------------------------------------------------------------===// #include "ClangSACheckers.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" -#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" using namespace clang; using namespace ento; namespace { -class ReturnUndefChecker : - public Checker< check::PreStmt<ReturnStmt> > { - mutable OwningPtr<BuiltinBug> BT; +class ReturnUndefChecker : public Checker< check::PreStmt<ReturnStmt> > { + mutable OwningPtr<BuiltinBug> BT_Undef; + mutable OwningPtr<BuiltinBug> BT_NullReference; + + void emitUndef(CheckerContext &C, const Expr *RetE) const; + void checkReference(CheckerContext &C, const Expr *RetE, + DefinedOrUnknownSVal RetVal) const; public: void checkPreStmt(const ReturnStmt *RS, CheckerContext &C) const; }; @@ -34,43 +38,75 @@ public: void ReturnUndefChecker::checkPreStmt(const ReturnStmt *RS, CheckerContext &C) const { - const Expr *RetE = RS->getRetValue(); if (!RetE) return; - - if (!C.getState()->getSVal(RetE, C.getLocationContext()).isUndef()) - return; - - // "return;" is modeled to evaluate to an UndefinedValue. Allow UndefinedValue - // to be returned in functions returning void to support the following pattern: - // void foo() { - // return; - // } - // void test() { - // return foo(); - // } + SVal RetVal = C.getSVal(RetE); + const StackFrameContext *SFC = C.getStackFrame(); QualType RT = CallEvent::getDeclaredResultType(SFC->getDecl()); - if (!RT.isNull() && RT->isSpecificBuiltinType(BuiltinType::Void)) + + if (RetVal.isUndef()) { + // "return;" is modeled to evaluate to an UndefinedVal. Allow UndefinedVal + // to be returned in functions returning void to support this pattern: + // void foo() { + // return; + // } + // void test() { + // return foo(); + // } + if (RT.isNull() || !RT->isVoidType()) + emitUndef(C, RetE); return; + } - ExplodedNode *N = C.generateSink(); + if (RT.isNull()) + return; + + if (RT->isReferenceType()) { + checkReference(C, RetE, RetVal.castAs<DefinedOrUnknownSVal>()); + return; + } +} +static void emitBug(CheckerContext &C, BuiltinBug &BT, const Expr *RetE, + const Expr *TrackingE = 0) { + ExplodedNode *N = C.generateSink(); if (!N) return; - - if (!BT) - BT.reset(new BuiltinBug("Garbage return value", - "Undefined or garbage value returned to caller")); - - BugReport *report = - new BugReport(*BT, BT->getDescription(), N); - - report->addRange(RetE->getSourceRange()); - bugreporter::trackNullOrUndefValue(N, RetE, *report); - - C.emitReport(report); + + BugReport *Report = new BugReport(BT, BT.getDescription(), N); + + Report->addRange(RetE->getSourceRange()); + bugreporter::trackNullOrUndefValue(N, TrackingE ? TrackingE : RetE, *Report); + + C.emitReport(Report); +} + +void ReturnUndefChecker::emitUndef(CheckerContext &C, const Expr *RetE) const { + if (!BT_Undef) + BT_Undef.reset(new BuiltinBug("Garbage return value", + "Undefined or garbage value " + "returned to caller")); + emitBug(C, *BT_Undef, RetE); +} + +void ReturnUndefChecker::checkReference(CheckerContext &C, const Expr *RetE, + DefinedOrUnknownSVal RetVal) const { + ProgramStateRef StNonNull, StNull; + llvm::tie(StNonNull, StNull) = C.getState()->assume(RetVal); + + if (StNonNull) { + // Going forward, assume the location is non-null. + C.addTransition(StNonNull); + return; + } + + // The return value is known to be null. Emit a bug report. + if (!BT_NullReference) + BT_NullReference.reset(new BuiltinBug("Returning null reference")); + + emitBug(C, *BT_NullReference, RetE, bugreporter::getDerefExpr(RetE)); } void ento::registerReturnUndefChecker(CheckerManager &mgr) { diff --git a/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp b/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp index ee055adf6e4d..1ccf339bacc1 100644 --- a/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp @@ -16,8 +16,8 @@ //===----------------------------------------------------------------------===// #include "ClangSACheckers.h" -#include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" @@ -25,7 +25,7 @@ using namespace clang; using namespace ento; namespace { -typedef llvm::SmallVector<SymbolRef, 2> SymbolVector; +typedef SmallVector<SymbolRef, 2> SymbolVector; struct StreamState { private: @@ -50,8 +50,7 @@ public: class SimpleStreamChecker : public Checker<check::PostCall, check::PreCall, check::DeadSymbols, - check::Bind, - check::RegionChanges> { + check::PointerEscape> { mutable IdentifierInfo *IIfopen, *IIfclose; @@ -80,20 +79,11 @@ public: void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const; - /// Deal with symbol escape as a byproduct of a bind. - void checkBind(SVal location, SVal val, const Stmt*S, - CheckerContext &C) const; - - /// Deal with symbol escape as a byproduct of a region change. - ProgramStateRef - checkRegionChanges(ProgramStateRef state, - const StoreManager::InvalidatedSymbols *invalidated, - ArrayRef<const MemRegion *> ExplicitRegions, - ArrayRef<const MemRegion *> Regions, - const CallEvent *Call) const; - bool wantsRegionChangeUpdate(ProgramStateRef state) const { - return true; - } + /// Stop tracking addresses which escape. + ProgramStateRef checkPointerEscape(ProgramStateRef State, + const InvalidatedSymbols &Escaped, + const CallEvent *Call, + PointerEscapeKind Kind) const; }; } // end anonymous namespace @@ -237,7 +227,7 @@ void SimpleStreamChecker::reportLeaks(SymbolVector LeakedStreams, ExplodedNode *ErrNode) const { // Attach bug reports to the leak node. // TODO: Identify the leaked file descriptor. - for (llvm::SmallVector<SymbolRef, 2>::iterator + for (SmallVector<SymbolRef, 2>::iterator I = LeakedStreams.begin(), E = LeakedStreams.end(); I != E; ++I) { BugReport *R = new BugReport(*LeakBugType, "Opened file is never closed; potential resource leak", ErrNode); @@ -246,45 +236,6 @@ void SimpleStreamChecker::reportLeaks(SymbolVector LeakedStreams, } } -// Check various ways a symbol can be invalidated. -// 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 SimpleStreamChecker::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). Do this only if we know that the store is not supposed - // to generate the same state. - SVal StoredVal = state->getSVal(regionLoc->getRegion()); - if (StoredVal != val) - escapes = (state == (state->bindLoc(*regionLoc, val))); - } - } - - // If our store can represent the binding and we aren't storing to something - // that doesn't have local storage then just return the state and - // 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); -} - bool SimpleStreamChecker::guaranteedNotToCloseFile(const CallEvent &Call) const{ // If it's not in a system header, assume it might close a file. if (!Call.isInSystemHeader()) @@ -300,38 +251,28 @@ bool SimpleStreamChecker::guaranteedNotToCloseFile(const CallEvent &Call) const{ return true; } -// If the symbol we are tracking is invalidated, do not track the symbol as +// If the pointer we are tracking escaped, do not track the symbol as // we cannot reason about it anymore. ProgramStateRef -SimpleStreamChecker::checkRegionChanges(ProgramStateRef State, - const StoreManager::InvalidatedSymbols *invalidated, - ArrayRef<const MemRegion *> ExplicitRegions, - ArrayRef<const MemRegion *> Regions, - const CallEvent *Call) const { - - if (!invalidated || invalidated->empty()) +SimpleStreamChecker::checkPointerEscape(ProgramStateRef State, + const InvalidatedSymbols &Escaped, + const CallEvent *Call, + PointerEscapeKind Kind) const { + // If we know that the call cannot close a file, there is nothing to do. + if ((Kind == PSK_DirectEscapeOnCall || + Kind == PSK_IndirectEscapeOnCall) && + guaranteedNotToCloseFile(*Call)) { return State; - - // If it's a call which might close the file, we assume that all regions - // (explicit and implicit) escaped. Otherwise, whitelist explicit pointers - // (the parameters to the call); we still can track them. - llvm::SmallPtrSet<SymbolRef, 8> WhitelistedSymbols; - if (!Call || guaranteedNotToCloseFile(*Call)) { - 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; + for (InvalidatedSymbols::const_iterator I = Escaped.begin(), + E = Escaped.end(); + I != E; ++I) { + SymbolRef Sym = *I; + // The symbol escaped. Optimistically, assume that the corresponding file // handle will be closed somewhere else. - State = State->remove<StreamMap>(sym); + State = State->remove<StreamMap>(Sym); } return State; } diff --git a/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp b/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp index 0c2f26683745..4fd778ef58ca 100644 --- a/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp @@ -13,38 +13,40 @@ //===----------------------------------------------------------------------===// #include "ClangSACheckers.h" +#include "clang/AST/ExprCXX.h" +#include "clang/Basic/SourceManager.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.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/ProgramState.h" -#include "clang/Basic/SourceManager.h" #include "llvm/ADT/SmallString.h" +#include "llvm/Support/raw_ostream.h" using namespace clang; using namespace ento; namespace { class StackAddrEscapeChecker : public Checker< check::PreStmt<ReturnStmt>, - check::EndPath > { + check::EndFunction > { mutable OwningPtr<BuiltinBug> BT_stackleak; mutable OwningPtr<BuiltinBug> BT_returnstack; public: void checkPreStmt(const ReturnStmt *RS, CheckerContext &C) const; - void checkEndPath(CheckerContext &Ctx) const; + void checkEndFunction(CheckerContext &Ctx) const; private: void EmitStackError(CheckerContext &C, const MemRegion *R, const Expr *RetE) const; - static SourceRange GenName(raw_ostream &os, const MemRegion *R, - SourceManager &SM); + static SourceRange genName(raw_ostream &os, const MemRegion *R, + ASTContext &Ctx); }; } -SourceRange StackAddrEscapeChecker::GenName(raw_ostream &os, - const MemRegion *R, - SourceManager &SM) { +SourceRange StackAddrEscapeChecker::genName(raw_ostream &os, const MemRegion *R, + ASTContext &Ctx) { // Get the base region, stripping away fields and elements. R = R->getBaseRegion(); + SourceManager &SM = Ctx.getSourceManager(); SourceRange range; os << "Address of "; @@ -77,8 +79,10 @@ SourceRange StackAddrEscapeChecker::GenName(raw_ostream &os, range = VR->getDecl()->getSourceRange(); } else if (const CXXTempObjectRegion *TOR = dyn_cast<CXXTempObjectRegion>(R)) { - os << "stack memory associated with temporary object of type '" - << TOR->getValueType().getAsString() << '\''; + QualType Ty = TOR->getValueType().getLocalUnqualifiedType(); + os << "stack memory associated with temporary object of type '"; + Ty.print(os, Ctx.getPrintingPolicy()); + os << "'"; range = TOR->getExpr()->getSourceRange(); } else { @@ -102,7 +106,7 @@ void StackAddrEscapeChecker::EmitStackError(CheckerContext &C, const MemRegion * // Generate a report for this bug. SmallString<512> buf; llvm::raw_svector_ostream os(buf); - SourceRange range = GenName(os, R, C.getSourceManager()); + SourceRange range = genName(os, R, C.getASTContext()); os << " returned to caller"; BugReport *report = new BugReport(*BT_returnstack, os.str(), N); report->addRange(RetE->getSourceRange()); @@ -155,7 +159,7 @@ void StackAddrEscapeChecker::checkPreStmt(const ReturnStmt *RS, EmitStackError(C, R, RetE); } -void StackAddrEscapeChecker::checkEndPath(CheckerContext &Ctx) const { +void StackAddrEscapeChecker::checkEndFunction(CheckerContext &Ctx) const { ProgramStateRef state = Ctx.getState(); // Iterate over all bindings to global variables and see if it contains @@ -222,8 +226,7 @@ void StackAddrEscapeChecker::checkEndPath(CheckerContext &Ctx) const { // Generate a report for this bug. SmallString<512> buf; llvm::raw_svector_ostream os(buf); - SourceRange range = GenName(os, cb.V[i].second, - Ctx.getSourceManager()); + SourceRange range = genName(os, cb.V[i].second, Ctx.getASTContext()); os << " is still referred to by the global variable '"; const VarRegion *VR = cast<VarRegion>(cb.V[i].first->getBaseRegion()); os << *VR->getDecl() diff --git a/lib/StaticAnalyzer/Checkers/StreamChecker.cpp b/lib/StaticAnalyzer/Checkers/StreamChecker.cpp index c06ba7c304e6..ffdf2d54b4ce 100644 --- a/lib/StaticAnalyzer/Checkers/StreamChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/StreamChecker.cpp @@ -12,10 +12,10 @@ //===----------------------------------------------------------------------===// #include "ClangSACheckers.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.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/ProgramState.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h" @@ -57,9 +57,7 @@ struct StreamState { }; class StreamChecker : public Checker<eval::Call, - check::DeadSymbols, - check::EndPath, - check::PreStmt<ReturnStmt> > { + check::DeadSymbols > { mutable IdentifierInfo *II_fopen, *II_tmpfile, *II_fclose, *II_fread, *II_fwrite, *II_fseek, *II_ftell, *II_rewind, *II_fgetpos, *II_fsetpos, @@ -75,8 +73,6 @@ public: bool evalCall(const CallExpr *CE, CheckerContext &C) const; void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const; - void checkEndPath(CheckerContext &Ctx) const; - void checkPreStmt(const ReturnStmt *S, CheckerContext &C) const; private: void Fopen(CheckerContext &C, const CallExpr *CE) const; @@ -214,9 +210,8 @@ void StreamChecker::OpenFileAux(CheckerContext &C, const CallExpr *CE) const { ProgramStateRef state = C.getState(); SValBuilder &svalBuilder = C.getSValBuilder(); const LocationContext *LCtx = C.getPredecessor()->getLocationContext(); - DefinedSVal RetVal = - cast<DefinedSVal>(svalBuilder.conjureSymbolVal(0, CE, LCtx, - C.blockCount())); + DefinedSVal RetVal = svalBuilder.conjureSymbolVal(0, CE, LCtx, C.blockCount()) + .castAs<DefinedSVal>(); state = state->BindExpr(CE, C.getLocationContext(), RetVal); ConstraintManager &CM = C.getConstraintManager(); @@ -264,7 +259,7 @@ void StreamChecker::Fseek(CheckerContext &C, const CallExpr *CE) const { return; // Check the legality of the 'whence' argument of 'fseek'. SVal Whence = state->getSVal(CE->getArg(2), C.getLocationContext()); - const nonloc::ConcreteInt *CI = dyn_cast<nonloc::ConcreteInt>(&Whence); + Optional<nonloc::ConcreteInt> CI = Whence.getAs<nonloc::ConcreteInt>(); if (!CI) return; @@ -342,7 +337,7 @@ void StreamChecker::Fileno(CheckerContext &C, const CallExpr *CE) const { ProgramStateRef StreamChecker::CheckNullStream(SVal SV, ProgramStateRef state, CheckerContext &C) const { - const DefinedSVal *DV = dyn_cast<DefinedSVal>(&SV); + Optional<DefinedSVal> DV = SV.getAs<DefinedSVal>(); if (!DV) return 0; @@ -405,9 +400,8 @@ void StreamChecker::checkDeadSymbols(SymbolReaper &SymReaper, SymbolRef Sym = *I; ProgramStateRef state = C.getState(); const StreamState *SS = state->get<StreamMap>(Sym); - // TODO: Shouldn't we have a continue here? if (!SS) - return; + continue; if (SS->isOpened()) { ExplodedNode *N = C.generateSink(); @@ -423,47 +417,6 @@ void StreamChecker::checkDeadSymbols(SymbolReaper &SymReaper, } } -void StreamChecker::checkEndPath(CheckerContext &Ctx) const { - ProgramStateRef state = Ctx.getState(); - StreamMapTy M = state->get<StreamMap>(); - - for (StreamMapTy::iterator I = M.begin(), E = M.end(); I != E; ++I) { - StreamState SS = I->second; - if (SS.isOpened()) { - 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); - Ctx.emitReport(R); - } - } - } -} - -void StreamChecker::checkPreStmt(const ReturnStmt *S, CheckerContext &C) const { - const Expr *RetE = S->getRetValue(); - if (!RetE) - return; - - ProgramStateRef state = C.getState(); - SymbolRef Sym = state->getSVal(RetE, C.getLocationContext()).getAsSymbol(); - - if (!Sym) - return; - - const StreamState *SS = state->get<StreamMap>(Sym); - if(!SS) - return; - - if (SS->isOpened()) - state = state->set<StreamMap>(Sym, StreamState::getEscaped(S)); - - C.addTransition(state); -} - void ento::registerStreamChecker(CheckerManager &mgr) { mgr.registerChecker<StreamChecker>(); } diff --git a/lib/StaticAnalyzer/Checkers/TaintTesterChecker.cpp b/lib/StaticAnalyzer/Checkers/TaintTesterChecker.cpp index 382be8475bb8..264f7f9fdb92 100644 --- a/lib/StaticAnalyzer/Checkers/TaintTesterChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/TaintTesterChecker.cpp @@ -11,10 +11,10 @@ // //===----------------------------------------------------------------------===// #include "ClangSACheckers.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.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; diff --git a/lib/StaticAnalyzer/Checkers/TraversalChecker.cpp b/lib/StaticAnalyzer/Checkers/TraversalChecker.cpp index b97cd6c66b93..57c9ed4ce289 100644 --- a/lib/StaticAnalyzer/Checkers/TraversalChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/TraversalChecker.cpp @@ -18,16 +18,17 @@ #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "llvm/Support/raw_ostream.h" using namespace clang; using namespace ento; namespace { class TraversalDumper : public Checker< check::BranchCondition, - check::EndPath > { + check::EndFunction > { public: void checkBranchCondition(const Stmt *Condition, CheckerContext &C) const; - void checkEndPath(CheckerContext &C) const; + void checkEndFunction(CheckerContext &C) const; }; } @@ -49,8 +50,8 @@ void TraversalDumper::checkBranchCondition(const Stmt *Condition, << Parent->getStmtClassName() << "\n"; } -void TraversalDumper::checkEndPath(CheckerContext &C) const { - llvm::outs() << "--END PATH--\n"; +void TraversalDumper::checkEndFunction(CheckerContext &C) const { + llvm::outs() << "--END FUNCTION--\n"; } void ento::registerTraversalDumper(CheckerManager &mgr) { @@ -60,9 +61,11 @@ void ento::registerTraversalDumper(CheckerManager &mgr) { //------------------------------------------------------------------------------ namespace { -class CallDumper : public Checker< check::PreCall > { +class CallDumper : public Checker< check::PreCall, + check::PostCall > { public: void checkPreCall(const CallEvent &Call, CheckerContext &C) const; + void checkPostCall(const CallEvent &Call, CheckerContext &C) const; }; } @@ -79,6 +82,26 @@ void CallDumper::checkPreCall(const CallEvent &Call, CheckerContext &C) const { Call.dump(llvm::outs()); } +void CallDumper::checkPostCall(const CallEvent &Call, CheckerContext &C) const { + const Expr *CallE = Call.getOriginExpr(); + if (!CallE) + return; + + unsigned Indentation = 0; + for (const LocationContext *LC = C.getLocationContext()->getParent(); + LC != 0; LC = LC->getParent()) + ++Indentation; + + // It is mildly evil to print directly to llvm::outs() rather than emitting + // warnings, but this ensures things do not get filtered out by the rest of + // the static analyzer machinery. + llvm::outs().indent(Indentation); + if (Call.getResultType()->isVoidType()) + llvm::outs() << "Returning void\n"; + else + llvm::outs() << "Returning " << C.getSVal(CallE) << "\n"; +} + void ento::registerCallDumper(CheckerManager &mgr) { mgr.registerChecker<CallDumper>(); } diff --git a/lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp b/lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp index 70e141e574cd..8235e68937af 100644 --- a/lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp @@ -13,10 +13,10 @@ //===----------------------------------------------------------------------===// #include "ClangSACheckers.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.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; @@ -90,7 +90,7 @@ void UndefBranchChecker::checkBranchCondition(const Stmt *Condition, ProgramPoint P = PrevN->getLocation(); ProgramStateRef St = N->getState(); - if (PostStmt *PS = dyn_cast<PostStmt>(&P)) + if (Optional<PostStmt> PS = P.getAs<PostStmt>()) if (PS->getStmt() == Ex) St = PrevN->getState(); diff --git a/lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp b/lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp index 30ccffaab055..93812f714856 100644 --- a/lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp @@ -12,11 +12,12 @@ //===----------------------------------------------------------------------===// #include "ClangSACheckers.h" +#include "clang/AST/Attr.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.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/ExprEngine.h" -#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "llvm/ADT/SmallString.h" #include "llvm/Support/raw_ostream.h" @@ -67,18 +68,15 @@ UndefCapturedBlockVarChecker::checkPostStmt(const BlockExpr *BE, for (; I != E; ++I) { // This VarRegion is the region associated with the block; we need // the one associated with the encompassing context. - const VarRegion *VR = *I; + const VarRegion *VR = I.getCapturedRegion(); const VarDecl *VD = VR->getDecl(); if (VD->getAttr<BlocksAttr>() || !VD->hasLocalStorage()) continue; // Get the VarRegion associated with VD in the local stack frame. - const LocationContext *LC = C.getLocationContext(); - VR = C.getSValBuilder().getRegionManager().getVarRegion(VD, LC); - SVal VRVal = state->getSVal(VR); - - if (VRVal.isUndef()) + if (Optional<UndefinedVal> V = + state->getSVal(I.getOriginalRegion()).getAs<UndefinedVal>()) { if (ExplodedNode *N = C.generateSink()) { if (!BT) BT.reset(new BuiltinBug("uninitialized variable captured by block")); @@ -93,11 +91,13 @@ UndefCapturedBlockVarChecker::checkPostStmt(const BlockExpr *BE, BugReport *R = new BugReport(*BT, os.str(), N); if (const Expr *Ex = FindBlockDeclRefExpr(BE->getBody(), VD)) R->addRange(Ex->getSourceRange()); - R->addVisitor(new FindLastStoreBRVisitor(VRVal, VR)); + R->addVisitor(new FindLastStoreBRVisitor(*V, VR, + /*EnableNullFPSuppression*/false)); R->disablePathPruning(); // need location of block C.emitReport(R); } + } } } diff --git a/lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp b/lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp index 415bab57287e..673356319833 100644 --- a/lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp @@ -13,12 +13,13 @@ //===----------------------------------------------------------------------===// #include "ClangSACheckers.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.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/ExprEngine.h" #include "llvm/ADT/SmallString.h" +#include "llvm/Support/raw_ostream.h" using namespace clang; using namespace ento; diff --git a/lib/StaticAnalyzer/Checkers/UndefinedArraySubscriptChecker.cpp b/lib/StaticAnalyzer/Checkers/UndefinedArraySubscriptChecker.cpp index b3a83e8e9179..176ee480826c 100644 --- a/lib/StaticAnalyzer/Checkers/UndefinedArraySubscriptChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/UndefinedArraySubscriptChecker.cpp @@ -13,10 +13,10 @@ //===----------------------------------------------------------------------===// #include "ClangSACheckers.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.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; @@ -34,18 +34,28 @@ public: void UndefinedArraySubscriptChecker::checkPreStmt(const ArraySubscriptExpr *A, CheckerContext &C) const { - if (C.getState()->getSVal(A->getIdx(), C.getLocationContext()).isUndef()) { - if (ExplodedNode *N = C.generateSink()) { - if (!BT) - BT.reset(new BuiltinBug("Array subscript is undefined")); - - // Generate a report for this bug. - BugReport *R = new BugReport(*BT, BT->getName(), N); - R->addRange(A->getIdx()->getSourceRange()); - bugreporter::trackNullOrUndefValue(N, A->getIdx(), *R); - C.emitReport(R); - } - } + const Expr *Index = A->getIdx(); + if (!C.getSVal(Index).isUndef()) + return; + + // Sema generates anonymous array variables for copying array struct fields. + // Don't warn if we're in an implicitly-generated constructor. + const Decl *D = C.getLocationContext()->getDecl(); + if (const CXXConstructorDecl *Ctor = dyn_cast<CXXConstructorDecl>(D)) + if (Ctor->isImplicitlyDefined()) + return; + + ExplodedNode *N = C.generateSink(); + if (!N) + return; + if (!BT) + BT.reset(new BuiltinBug("Array subscript is undefined")); + + // Generate a report for this bug. + BugReport *R = new BugReport(*BT, BT->getName(), N); + R->addRange(A->getIdx()->getSourceRange()); + bugreporter::trackNullOrUndefValue(N, A->getIdx(), *R); + C.emitReport(R); } void ento::registerUndefinedArraySubscriptChecker(CheckerManager &mgr) { diff --git a/lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp b/lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp index 410010a335c3..e04f49c3746d 100644 --- a/lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp @@ -13,10 +13,10 @@ //===----------------------------------------------------------------------===// #include "ClangSACheckers.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.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; diff --git a/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp b/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp index 171e15b85ae7..4ea07e29ebbb 100644 --- a/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp @@ -13,20 +13,20 @@ //===----------------------------------------------------------------------===// #include "ClangSACheckers.h" +#include "clang/Basic/TargetInfo.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.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/Basic/TargetInfo.h" #include "llvm/ADT/Optional.h" -#include "llvm/ADT/SmallString.h" #include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringSwitch.h" +#include "llvm/Support/raw_ostream.h" #include <fcntl.h> using namespace clang; using namespace ento; -using llvm::Optional; namespace { class UnixAPIChecker : public Checker< check::PreStmt<CallExpr> > { @@ -102,21 +102,20 @@ 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, C.getLocationContext()); - if (!isa<NonLoc>(V)) { + if (!V.getAs<NonLoc>()) { // The case where 'V' can be a location can only be due to a bad header, // so in this case bail out. return; } - NonLoc oflags = cast<NonLoc>(V); - NonLoc ocreateFlag = - cast<NonLoc>(C.getSValBuilder().makeIntVal(Val_O_CREAT.getValue(), - oflagsEx->getType())); + NonLoc oflags = V.castAs<NonLoc>(); + NonLoc ocreateFlag = C.getSValBuilder() + .makeIntVal(Val_O_CREAT.getValue(), oflagsEx->getType()).castAs<NonLoc>(); SVal maskedFlagsUC = C.getSValBuilder().evalBinOpNN(state, BO_And, oflags, ocreateFlag, oflagsEx->getType()); if (maskedFlagsUC.isUnknownOrUndef()) return; - DefinedSVal maskedFlags = cast<DefinedSVal>(maskedFlagsUC); + DefinedSVal maskedFlags = maskedFlagsUC.castAs<DefinedSVal>(); // Check if maskedFlags is non-zero. ProgramStateRef trueState, falseState; @@ -201,7 +200,7 @@ static bool IsZeroByteAllocation(ProgramStateRef state, ProgramStateRef *trueState, ProgramStateRef *falseState) { llvm::tie(*trueState, *falseState) = - state->assume(cast<DefinedSVal>(argVal)); + state->assume(argVal.castAs<DefinedSVal>()); return (*falseState && !*trueState); } diff --git a/lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp b/lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp index 5a13ed0a2e17..91c2ffb5aabf 100644 --- a/lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp @@ -14,16 +14,16 @@ //===----------------------------------------------------------------------===// #include "ClangSACheckers.h" +#include "clang/AST/ParentMap.h" +#include "clang/Basic/Builtins.h" +#include "clang/Basic/SourceManager.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/CheckerContext.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h" -#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" -#include "clang/AST/ParentMap.h" -#include "clang/Basic/Builtins.h" -#include "clang/Basic/SourceManager.h" #include "llvm/ADT/SmallSet.h" // The number of CFGBlock pointers we want to reserve memory for. This is used @@ -76,7 +76,7 @@ void UnreachableCodeChecker::checkEndAnalysis(ExplodedGraph &G, if (!PM) PM = &LC->getParentMap(); - if (const BlockEntrance *BE = dyn_cast<BlockEntrance>(&P)) { + if (Optional<BlockEntrance> BE = P.getAs<BlockEntrance>()) { const CFGBlock *CB = BE->getBlock(); reachable.insert(CB->getBlockID()); } @@ -131,7 +131,7 @@ void UnreachableCodeChecker::checkEndAnalysis(ExplodedGraph &G, bool foundUnreachable = false; for (CFGBlock::const_iterator ci = CB->begin(), ce = CB->end(); ci != ce; ++ci) { - if (const CFGStmt *S = (*ci).getAs<CFGStmt>()) + if (Optional<CFGStmt> S = (*ci).getAs<CFGStmt>()) if (const CallExpr *CE = dyn_cast<CallExpr>(S->getStmt())) { if (CE->isBuiltinCall() == Builtin::BI__builtin_unreachable) { foundUnreachable = true; @@ -189,7 +189,7 @@ void UnreachableCodeChecker::FindUnreachableEntryPoints(const CFGBlock *CB, // Find the Stmt* in a CFGBlock for reporting a warning const Stmt *UnreachableCodeChecker::getUnreachableStmt(const CFGBlock *CB) { for (CFGBlock::const_iterator I = CB->begin(), E = CB->end(); I != E; ++I) { - if (const CFGStmt *S = I->getAs<CFGStmt>()) + if (Optional<CFGStmt> S = I->getAs<CFGStmt>()) return S->getStmt(); } if (const Stmt *S = CB->getTerminator()) diff --git a/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp b/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp index 58f9ec0f9b9b..30aef060690d 100644 --- a/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp @@ -15,13 +15,14 @@ //===----------------------------------------------------------------------===// #include "ClangSACheckers.h" +#include "clang/AST/CharUnits.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.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/AST/CharUnits.h" -#include "llvm/ADT/SmallString.h" #include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/raw_ostream.h" using namespace clang; using namespace ento; @@ -109,7 +110,7 @@ void VLASizeChecker::checkPreStmt(const DeclStmt *DS, CheckerContext &C) const { } // Check if the size is zero. - DefinedSVal sizeD = cast<DefinedSVal>(sizeV); + DefinedSVal sizeD = sizeV.castAs<DefinedSVal>(); ProgramStateRef stateNotZero, stateZero; llvm::tie(stateNotZero, stateZero) = state->assume(sizeD); @@ -129,22 +130,22 @@ void VLASizeChecker::checkPreStmt(const DeclStmt *DS, CheckerContext &C) const { // Convert the array length to size_t. SValBuilder &svalBuilder = C.getSValBuilder(); QualType SizeTy = Ctx.getSizeType(); - NonLoc ArrayLength = cast<NonLoc>(svalBuilder.evalCast(sizeD, SizeTy, - SE->getType())); + NonLoc ArrayLength = + svalBuilder.evalCast(sizeD, SizeTy, SE->getType()).castAs<NonLoc>(); // Get the element size. CharUnits EleSize = Ctx.getTypeSizeInChars(VLA->getElementType()); SVal EleSizeVal = svalBuilder.makeIntVal(EleSize.getQuantity(), SizeTy); // Multiply the array length by the element size. - SVal ArraySizeVal = svalBuilder.evalBinOpNN(state, BO_Mul, ArrayLength, - cast<NonLoc>(EleSizeVal), SizeTy); + SVal ArraySizeVal = svalBuilder.evalBinOpNN( + state, BO_Mul, ArrayLength, EleSizeVal.castAs<NonLoc>(), SizeTy); // Finally, assume that the array's extent matches the given size. const LocationContext *LC = C.getLocationContext(); DefinedOrUnknownSVal Extent = state->getRegion(VD, LC)->getExtent(svalBuilder); - DefinedOrUnknownSVal ArraySize = cast<DefinedOrUnknownSVal>(ArraySizeVal); + DefinedOrUnknownSVal ArraySize = ArraySizeVal.castAs<DefinedOrUnknownSVal>(); DefinedOrUnknownSVal sizeIsKnown = svalBuilder.evalEQ(state, Extent, ArraySize); state = state->assume(sizeIsKnown, true); diff --git a/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp b/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp index bdc96278f76a..06f01ad75422 100644 --- a/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp @@ -15,11 +15,12 @@ #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 "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" #include "llvm/ADT/SmallString.h" +#include "llvm/Support/SaveAndRestore.h" +#include "llvm/Support/raw_ostream.h" using namespace clang; using namespace ento; diff --git a/lib/StaticAnalyzer/Core/APSIntType.cpp b/lib/StaticAnalyzer/Core/APSIntType.cpp index 884b0faa9ed4..c7e95268213e 100644 --- a/lib/StaticAnalyzer/Core/APSIntType.cpp +++ b/lib/StaticAnalyzer/Core/APSIntType.cpp @@ -13,20 +13,31 @@ using namespace clang; using namespace ento; APSIntType::RangeTestResultKind -APSIntType::testInRange(const llvm::APSInt &Value) const { +APSIntType::testInRange(const llvm::APSInt &Value, + bool AllowSignConversions) const { + // Negative numbers cannot be losslessly converted to unsigned type. - if (IsUnsigned && Value.isSigned() && Value.isNegative()) + if (IsUnsigned && !AllowSignConversions && + Value.isSigned() && Value.isNegative()) return RTR_Below; - // Signed integers can be converted to signed integers of the same width - // or (if positive) unsigned integers with one fewer bit. - // Unsigned integers can be converted to unsigned integers of the same width - // or signed integers with one more bit. unsigned MinBits; - if (Value.isSigned()) - MinBits = Value.getMinSignedBits() - IsUnsigned; - else - MinBits = Value.getActiveBits() + !IsUnsigned; + if (AllowSignConversions) { + if (Value.isSigned() && !IsUnsigned) + MinBits = Value.getMinSignedBits(); + else + MinBits = Value.getActiveBits(); + + } else { + // Signed integers can be converted to signed integers of the same width + // or (if positive) unsigned integers with one fewer bit. + // Unsigned integers can be converted to unsigned integers of the same width + // or signed integers with one more bit. + if (Value.isSigned()) + MinBits = Value.getMinSignedBits() - IsUnsigned; + else + MinBits = Value.getActiveBits() + !IsUnsigned; + } if (MinBits <= BitWidth) return RTR_Within; diff --git a/lib/StaticAnalyzer/Core/AnalysisManager.cpp b/lib/StaticAnalyzer/Core/AnalysisManager.cpp index 011d4c09a23f..747b73c4164b 100644 --- a/lib/StaticAnalyzer/Core/AnalysisManager.cpp +++ b/lib/StaticAnalyzer/Core/AnalysisManager.cpp @@ -25,7 +25,8 @@ AnalysisManager::AnalysisManager(ASTContext &ctx, DiagnosticsEngine &diags, /*AddImplicitDtors=*/true, /*AddInitializers=*/true, Options.includeTemporaryDtorsInCFG(), - Options.shouldSynthesizeBodies()), + Options.shouldSynthesizeBodies(), + Options.shouldConditionalizeStaticInitializers()), Ctx(ctx), Diags(diags), LangOpts(lang), diff --git a/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp b/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp index da88589c8696..ae707395fc5a 100644 --- a/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp +++ b/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp @@ -13,23 +13,68 @@ //===----------------------------------------------------------------------===// #include "clang/StaticAnalyzer/Core/AnalyzerOptions.h" -#include "llvm/ADT/StringSwitch.h" #include "llvm/ADT/SmallString.h" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/Support/ErrorHandling.h" #include "llvm/Support/raw_ostream.h" using namespace clang; using namespace llvm; +AnalyzerOptions::UserModeKind AnalyzerOptions::getUserMode() { + if (UserMode == UMK_NotSet) { + StringRef ModeStr(Config.GetOrCreateValue("mode", "deep").getValue()); + UserMode = llvm::StringSwitch<UserModeKind>(ModeStr) + .Case("shallow", UMK_Shallow) + .Case("deep", UMK_Deep) + .Default(UMK_NotSet); + assert(UserMode != UMK_NotSet && "User mode is invalid."); + } + return UserMode; +} + +IPAKind AnalyzerOptions::getIPAMode() { + if (IPAMode == IPAK_NotSet) { + + // Use the User Mode to set the default IPA value. + // Note, we have to add the string to the Config map for the ConfigDumper + // checker to function properly. + const char *DefaultIPA = 0; + UserModeKind HighLevelMode = getUserMode(); + if (HighLevelMode == UMK_Shallow) + DefaultIPA = "inlining"; + else if (HighLevelMode == UMK_Deep) + DefaultIPA = "dynamic-bifurcate"; + assert(DefaultIPA); + + // Lookup the ipa configuration option, use the default from User Mode. + StringRef ModeStr(Config.GetOrCreateValue("ipa", DefaultIPA).getValue()); + IPAKind IPAConfig = llvm::StringSwitch<IPAKind>(ModeStr) + .Case("none", IPAK_None) + .Case("basic-inlining", IPAK_BasicInlining) + .Case("inlining", IPAK_Inlining) + .Case("dynamic", IPAK_DynamicDispatch) + .Case("dynamic-bifurcate", IPAK_DynamicDispatchBifurcate) + .Default(IPAK_NotSet); + assert(IPAConfig != IPAK_NotSet && "IPA Mode is invalid."); + + // Set the member variable. + IPAMode = IPAConfig; + } + + return IPAMode; +} + bool AnalyzerOptions::mayInlineCXXMemberFunction(CXXInlineableMemberKind K) { - if (IPAMode < Inlining) + if (getIPAMode() < IPAK_Inlining) return false; if (!CXXMemberInliningMode) { static const char *ModeKey = "c++-inlining"; StringRef ModeStr(Config.GetOrCreateValue(ModeKey, - "methods").getValue()); + "destructors").getValue()); CXXInlineableMemberKind &MutableMode = const_cast<CXXInlineableMemberKind &>(CXXMemberInliningMode); @@ -64,8 +109,7 @@ bool AnalyzerOptions::getBooleanOption(StringRef Name, bool DefaultVal) { .Default(DefaultVal); } -bool AnalyzerOptions::getBooleanOption(llvm::Optional<bool> &V, - StringRef Name, +bool AnalyzerOptions::getBooleanOption(Optional<bool> &V, StringRef Name, bool DefaultVal) { if (!V.hasValue()) V = getBooleanOption(Name, DefaultVal); @@ -90,14 +134,21 @@ bool AnalyzerOptions::mayInlineTemplateFunctions() { /*Default=*/true); } +bool AnalyzerOptions::mayInlineCXXContainerCtorsAndDtors() { + return getBooleanOption(InlineCXXContainerCtorsAndDtors, + "c++-container-inlining", + /*Default=*/false); +} + + bool AnalyzerOptions::mayInlineObjCMethod() { return getBooleanOption(ObjCInliningMode, "objc-inlining", /* Default = */ true); } -bool AnalyzerOptions::shouldPruneNullReturnPaths() { - return getBooleanOption(PruneNullReturnPaths, +bool AnalyzerOptions::shouldSuppressNullReturnPaths() { + return getBooleanOption(SuppressNullReturnPaths, "suppress-null-return-paths", /* Default = */ true); } @@ -108,8 +159,20 @@ bool AnalyzerOptions::shouldAvoidSuppressingNullArgumentPaths() { /* Default = */ false); } +bool AnalyzerOptions::shouldSuppressInlinedDefensiveChecks() { + return getBooleanOption(SuppressInlinedDefensiveChecks, + "suppress-inlined-defensive-checks", + /* Default = */ true); +} + +bool AnalyzerOptions::shouldSuppressFromCXXStandardLibrary() { + return getBooleanOption(SuppressFromCXXStandardLibrary, + "suppress-c++-stdlib", + /* Default = */ false); +} + int AnalyzerOptions::getOptionAsInteger(StringRef Name, int DefaultVal) { - llvm::SmallString<10> StrBuf; + SmallString<10> StrBuf; llvm::raw_svector_ostream OS(StrBuf); OS << DefaultVal; @@ -127,12 +190,67 @@ unsigned AnalyzerOptions::getAlwaysInlineSize() { return AlwaysInlineSize.getValue(); } +unsigned AnalyzerOptions::getMaxInlinableSize() { + if (!MaxInlinableSize.hasValue()) { + + int DefaultValue = 0; + UserModeKind HighLevelMode = getUserMode(); + switch (HighLevelMode) { + default: + llvm_unreachable("Invalid mode."); + case UMK_Shallow: + DefaultValue = 4; + break; + case UMK_Deep: + DefaultValue = 50; + break; + } + + MaxInlinableSize = getOptionAsInteger("max-inlinable-size", DefaultValue); + } + return MaxInlinableSize.getValue(); +} + unsigned AnalyzerOptions::getGraphTrimInterval() { if (!GraphTrimInterval.hasValue()) GraphTrimInterval = getOptionAsInteger("graph-trim-interval", 1000); return GraphTrimInterval.getValue(); } +unsigned AnalyzerOptions::getMaxTimesInlineLarge() { + if (!MaxTimesInlineLarge.hasValue()) + MaxTimesInlineLarge = getOptionAsInteger("max-times-inline-large", 32); + return MaxTimesInlineLarge.getValue(); +} + +unsigned AnalyzerOptions::getMaxNodesPerTopLevelFunction() { + if (!MaxNodesPerTopLevelFunction.hasValue()) { + int DefaultValue = 0; + UserModeKind HighLevelMode = getUserMode(); + switch (HighLevelMode) { + default: + llvm_unreachable("Invalid mode."); + case UMK_Shallow: + DefaultValue = 75000; + break; + case UMK_Deep: + DefaultValue = 150000; + break; + } + MaxNodesPerTopLevelFunction = getOptionAsInteger("max-nodes", DefaultValue); + } + return MaxNodesPerTopLevelFunction.getValue(); +} + bool AnalyzerOptions::shouldSynthesizeBodies() { return getBooleanOption("faux-bodies", true); } + +bool AnalyzerOptions::shouldPrunePaths() { + return getBooleanOption("prune-paths", true); +} + +bool AnalyzerOptions::shouldConditionalizeStaticInitializers() { + return getBooleanOption("cfg-conditional-static-initializers", true); +} + diff --git a/lib/StaticAnalyzer/Core/BugReporter.cpp b/lib/StaticAnalyzer/Core/BugReporter.cpp index c898d65a5f95..8f8eb3bb8502 100644 --- a/lib/StaticAnalyzer/Core/BugReporter.cpp +++ b/lib/StaticAnalyzer/Core/BugReporter.cpp @@ -12,29 +12,38 @@ // //===----------------------------------------------------------------------===// +#define DEBUG_TYPE "BugReporter" + #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" -#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" -#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" -#include "clang/Basic/SourceManager.h" +#include "clang/Analysis/CFG.h" #include "clang/Analysis/ProgramPoint.h" +#include "clang/Basic/SourceManager.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" -#include "llvm/Support/raw_ostream.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.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 "llvm/ADT/OwningPtr.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/Statistic.h" +#include "llvm/Support/raw_ostream.h" #include <queue> using namespace clang; using namespace ento; +STATISTIC(MaxBugClassSize, + "The maximum number of bug reports in the same equivalence class"); +STATISTIC(MaxValidBugClassSize, + "The maximum number of bug reports in the same equivalence class " + "where at least one report is valid (not suppressed)"); + BugReporterVisitor::~BugReporterVisitor() {} void BugReporterContext::anchor() {} @@ -44,13 +53,13 @@ void BugReporterContext::anchor() {} //===----------------------------------------------------------------------===// static inline const Stmt *GetStmt(const ProgramPoint &P) { - if (const StmtPoint* SP = dyn_cast<StmtPoint>(&P)) + if (Optional<StmtPoint> SP = P.getAs<StmtPoint>()) return SP->getStmt(); - else if (const BlockEdge *BE = dyn_cast<BlockEdge>(&P)) + if (Optional<BlockEdge> BE = P.getAs<BlockEdge>()) return BE->getSrc()->getTerminator(); - else if (const CallEnter *CE = dyn_cast<CallEnter>(&P)) + if (Optional<CallEnter> CE = P.getAs<CallEnter>()) return CE->getCallExpr(); - else if (const CallExitEnd *CEE = dyn_cast<CallExitEnd>(&P)) + if (Optional<CallExitEnd> CEE = P.getAs<CallExitEnd>()) return CEE->getCalleeContext()->getCallSite(); return 0; @@ -191,9 +200,8 @@ static void removeRedundantMsgs(PathPieces &path) { /// 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. -bool BugReporter::RemoveUneededCalls(PathPieces &pieces, BugReport *R, - PathDiagnosticCallPiece *CallWithLoc) { +/// "interesting stuff" which means it shouldn't be pruned from the parent path. +bool BugReporter::RemoveUnneededCalls(PathPieces &pieces, BugReport *R) { bool containsSomethingInteresting = false; const unsigned N = pieces.size(); @@ -203,7 +211,9 @@ bool BugReporter::RemoveUneededCalls(PathPieces &pieces, BugReport *R, IntrusiveRefCntPtr<PathDiagnosticPiece> piece(pieces.front()); pieces.pop_front(); - // Throw away pieces with invalid locations. + // Throw away pieces with invalid locations. Note that we can't throw away + // calls just yet because they might have something interesting inside them. + // If so, their locations will be adjusted as necessary later. if (piece->getKind() != PathDiagnosticPiece::Call && piece->getLocation().asLocation().isInvalid()) continue; @@ -217,25 +227,16 @@ bool BugReporter::RemoveUneededCalls(PathPieces &pieces, BugReport *R, containsSomethingInteresting = true; break; } - // Recursively clean out the subclass. Keep this call around if - // it contains any informative diagnostics. - PathDiagnosticCallPiece *NewCallWithLoc = - call->getLocation().asLocation().isValid() - ? call : CallWithLoc; - - if (!RemoveUneededCalls(call->path, R, NewCallWithLoc)) - continue; - if (NewCallWithLoc == CallWithLoc && CallWithLoc) { - call->callEnter = CallWithLoc->callEnter; - } + if (!RemoveUnneededCalls(call->path, R)) + continue; containsSomethingInteresting = true; break; } case PathDiagnosticPiece::Macro: { PathDiagnosticMacroPiece *macro = cast<PathDiagnosticMacroPiece>(piece); - if (!RemoveUneededCalls(macro->subPieces, R)) + if (!RemoveUnneededCalls(macro->subPieces, R)) continue; containsSomethingInteresting = true; break; @@ -258,36 +259,66 @@ bool BugReporter::RemoveUneededCalls(PathPieces &pieces, BugReport *R, return containsSomethingInteresting; } +/// Recursively scan through a path and make sure that all call pieces have +/// valid locations. Note that all other pieces with invalid locations should +/// have already been pruned out. +static void adjustCallLocations(PathPieces &Pieces, + PathDiagnosticLocation *LastCallLocation = 0) { + for (PathPieces::iterator I = Pieces.begin(), E = Pieces.end(); I != E; ++I) { + PathDiagnosticCallPiece *Call = dyn_cast<PathDiagnosticCallPiece>(*I); + + if (!Call) { + assert((*I)->getLocation().asLocation().isValid()); + continue; + } + + if (LastCallLocation) { + if (!Call->callEnter.asLocation().isValid() || + Call->getCaller()->isImplicit()) + Call->callEnter = *LastCallLocation; + if (!Call->callReturn.asLocation().isValid() || + Call->getCaller()->isImplicit()) + Call->callReturn = *LastCallLocation; + } + + // Recursively clean out the subclass. Keep this call around if + // it contains any informative diagnostics. + PathDiagnosticLocation *ThisCallLocation; + if (Call->callEnterWithin.asLocation().isValid() && + !Call->getCallee()->isImplicit()) + ThisCallLocation = &Call->callEnterWithin; + else + ThisCallLocation = &Call->callEnter; + + assert(ThisCallLocation && "Outermost call has an invalid location"); + adjustCallLocations(Call->path, ThisCallLocation); + } +} + //===----------------------------------------------------------------------===// // PathDiagnosticBuilder and its associated routines and helper objects. //===----------------------------------------------------------------------===// -typedef llvm::DenseMap<const ExplodedNode*, -const ExplodedNode*> NodeBackMap; - namespace { class NodeMapClosure : public BugReport::NodeResolver { - NodeBackMap& M; + InterExplodedGraphMap &M; public: - NodeMapClosure(NodeBackMap *m) : M(*m) {} - ~NodeMapClosure() {} + NodeMapClosure(InterExplodedGraphMap &m) : M(m) {} const ExplodedNode *getOriginalNode(const ExplodedNode *N) { - NodeBackMap::iterator I = M.find(N); - return I == M.end() ? 0 : I->second; + return M.lookup(N); } }; class PathDiagnosticBuilder : public BugReporterContext { BugReport *R; PathDiagnosticConsumer *PDC; - OwningPtr<ParentMap> PM; NodeMapClosure NMC; public: const LocationContext *LC; PathDiagnosticBuilder(GRBugReporter &br, - BugReport *r, NodeBackMap *Backmap, + BugReport *r, InterExplodedGraphMap &Backmap, PathDiagnosticConsumer *pdc) : BugReporterContext(br), R(r), PDC(pdc), NMC(Backmap), LC(r->getErrorNode()->getLocationContext()) @@ -552,7 +583,7 @@ static bool GenerateMinimalPathDiagnostic(PathDiagnostic& PD, ProgramPoint P = N->getLocation(); do { - if (const CallExitEnd *CE = dyn_cast<CallExitEnd>(&P)) { + if (Optional<CallExitEnd> CE = P.getAs<CallExitEnd>()) { PathDiagnosticCallPiece *C = PathDiagnosticCallPiece::construct(N, *CE, SMgr); GRBugReporter& BR = PDB.getBugReporter(); @@ -563,7 +594,7 @@ static bool GenerateMinimalPathDiagnostic(PathDiagnostic& PD, break; } - if (const CallEnter *CE = dyn_cast<CallEnter>(&P)) { + if (Optional<CallEnter> CE = P.getAs<CallEnter>()) { // Flush all locations, and pop the active path. bool VisitedEntireCall = PD.isWithinCall(); PD.popActivePath(); @@ -591,7 +622,7 @@ static bool GenerateMinimalPathDiagnostic(PathDiagnostic& PD, break; } - if (const BlockEdge *BE = dyn_cast<BlockEdge>(&P)) { + if (Optional<BlockEdge> BE = P.getAs<BlockEdge>()) { const CFGBlock *Src = BE->getSrc(); const CFGBlock *Dst = BE->getDst(); const Stmt *T = Src->getTerminator(); @@ -1267,7 +1298,81 @@ static void reversePropagateInterestingSymbols(BugReport &R, } } } - + +//===----------------------------------------------------------------------===// +// Functions for determining if a loop was executed 0 times. +//===----------------------------------------------------------------------===// + +/// Return true if the terminator is a loop and the destination is the +/// false branch. +static bool isLoopJumpPastBody(const Stmt *Term, const BlockEdge *BE) { + switch (Term->getStmtClass()) { + case Stmt::ForStmtClass: + case Stmt::WhileStmtClass: + case Stmt::ObjCForCollectionStmtClass: + break; + default: + // Note that we intentionally do not include do..while here. + return false; + } + + // Did we take the false branch? + const CFGBlock *Src = BE->getSrc(); + assert(Src->succ_size() == 2); + return (*(Src->succ_begin()+1) == BE->getDst()); +} + +static bool isContainedByStmt(ParentMap &PM, const Stmt *S, const Stmt *SubS) { + while (SubS) { + if (SubS == S) + return true; + SubS = PM.getParent(SubS); + } + return false; +} + +static const Stmt *getStmtBeforeCond(ParentMap &PM, const Stmt *Term, + const ExplodedNode *N) { + while (N) { + Optional<StmtPoint> SP = N->getLocation().getAs<StmtPoint>(); + if (SP) { + const Stmt *S = SP->getStmt(); + if (!isContainedByStmt(PM, Term, S)) + return S; + } + N = GetPredecessorNode(N); + } + return 0; +} + +static bool isInLoopBody(ParentMap &PM, const Stmt *S, const Stmt *Term) { + const Stmt *LoopBody = 0; + switch (Term->getStmtClass()) { + case Stmt::ForStmtClass: { + const ForStmt *FS = cast<ForStmt>(Term); + if (isContainedByStmt(PM, FS->getInc(), S)) + return true; + LoopBody = FS->getBody(); + break; + } + case Stmt::ObjCForCollectionStmtClass: { + const ObjCForCollectionStmt *FC = cast<ObjCForCollectionStmt>(Term); + LoopBody = FC->getBody(); + break; + } + case Stmt::WhileStmtClass: + LoopBody = cast<WhileStmt>(Term)->getBody(); + break; + default: + return false; + } + return isContainedByStmt(PM, LoopBody, S); +} + +//===----------------------------------------------------------------------===// +// Top-level logic for generating extensive path diagnostics. +//===----------------------------------------------------------------------===// + static bool GenerateExtensivePathDiagnostic(PathDiagnostic& PD, PathDiagnosticBuilder &PDB, const ExplodedNode *N, @@ -1284,14 +1389,14 @@ static bool GenerateExtensivePathDiagnostic(PathDiagnostic& PD, ProgramPoint P = N->getLocation(); do { - if (const PostStmt *PS = dyn_cast<PostStmt>(&P)) { + if (Optional<PostStmt> PS = P.getAs<PostStmt>()) { if (const Expr *Ex = PS->getStmtAs<Expr>()) reversePropagateIntererstingSymbols(*PDB.getBugReport(), IE, N->getState().getPtr(), Ex, N->getLocationContext()); } - if (const CallExitEnd *CE = dyn_cast<CallExitEnd>(&P)) { + if (Optional<CallExitEnd> CE = P.getAs<CallExitEnd>()) { const Stmt *S = CE->getCalleeContext()->getCallSite(); if (const Expr *Ex = dyn_cast_or_null<Expr>(S)) { reversePropagateIntererstingSymbols(*PDB.getBugReport(), IE, @@ -1315,7 +1420,7 @@ static bool GenerateExtensivePathDiagnostic(PathDiagnostic& PD, // Pop the call hierarchy if we are done walking the contents // of a function call. - if (const CallEnter *CE = dyn_cast<CallEnter>(&P)) { + if (Optional<CallEnter> CE = P.getAs<CallEnter>()) { // Add an edge to the start of the function. const Decl *D = CE->getCalleeContext()->getDecl(); PathDiagnosticLocation pos = @@ -1360,7 +1465,7 @@ static bool GenerateExtensivePathDiagnostic(PathDiagnostic& PD, PDB.LC = N->getLocationContext(); // Block edges. - if (const BlockEdge *BE = dyn_cast<BlockEdge>(&P)) { + if (Optional<BlockEdge> BE = P.getAs<BlockEdge>()) { // Does this represent entering a call? If so, look at propagating // interesting symbols across call boundaries. if (NextNode) { @@ -1397,16 +1502,39 @@ static bool GenerateExtensivePathDiagnostic(PathDiagnostic& PD, EB.addEdge(BL); } } - - if (const Stmt *Term = BE->getSrc()->getTerminator()) + + const CFGBlock *BSrc = BE->getSrc(); + ParentMap &PM = PDB.getParentMap(); + + if (const Stmt *Term = BSrc->getTerminator()) { + // Are we jumping past the loop body without ever executing the + // loop (because the condition was false)? + if (isLoopJumpPastBody(Term, &*BE) && + !isInLoopBody(PM, + getStmtBeforeCond(PM, + BSrc->getTerminatorCondition(), + N), + Term)) { + PathDiagnosticLocation L(Term, SM, PDB.LC); + PathDiagnosticEventPiece *PE = + new PathDiagnosticEventPiece(L, "Loop body executed 0 times"); + PE->setPrunable(true); + + EB.addEdge(PE->getLocation(), true); + PD.getActivePath().push_front(PE); + } + + // In any case, add the terminator as the current statement + // context for control edges. EB.addContext(Term); + } break; } - if (const BlockEntrance *BE = dyn_cast<BlockEntrance>(&P)) { - CFGElement First = BE->getFirstElement(); - if (const CFGStmt *S = First.getAs<CFGStmt>()) { + if (Optional<BlockEntrance> BE = P.getAs<BlockEntrance>()) { + Optional<CFGElement> First = BE->getFirstElement(); + if (Optional<CFGStmt> S = First ? First->getAs<CFGStmt>() : None) { const Stmt *stmt = S->getStmt(); if (IsControlFlowExpr(stmt)) { // Add the proper context for '&&', '||', and '?'. @@ -1502,8 +1630,9 @@ const Decl *BugReport::getDeclWithIssue() const { void BugReport::Profile(llvm::FoldingSetNodeID& hash) const { hash.AddPointer(&BT); hash.AddString(Description); - if (UniqueingLocation.isValid()) { - UniqueingLocation.Profile(hash); + PathDiagnosticLocation UL = getUniqueingLocation(); + if (UL.isValid()) { + UL.Profile(hash); } else if (Location.isValid()) { Location.Profile(hash); } else { @@ -1623,7 +1752,7 @@ const Stmt *BugReport::getStmt() const { ProgramPoint ProgP = ErrorNode->getLocation(); const Stmt *S = NULL; - if (BlockEntrance *BE = dyn_cast<BlockEntrance>(&ProgP)) { + if (Optional<BlockEntrance> BE = ProgP.getAs<BlockEntrance>()) { CFGBlock &Exit = ProgP.getLocationContext()->getCFG()->getExit(); if (BE->getBlock() == &Exit) S = GetPreviousStmt(ErrorNode); @@ -1667,6 +1796,9 @@ PathDiagnosticLocation BugReport::getLocation(const SourceManager &SM) const { if (const BinaryOperator *B = dyn_cast<BinaryOperator>(S)) return PathDiagnosticLocation::createOperatorLoc(B, SM); + if (ErrorNode->getLocation().getAs<PostStmtPurgeDeadSymbols>()) + return PathDiagnosticLocation::createEnd(S, SM, LC); + return PathDiagnosticLocation::createBegin(S, SM, LC); } } else { @@ -1741,141 +1873,174 @@ void BugReporter::FlushReports() { // PathDiagnostics generation. //===----------------------------------------------------------------------===// -static std::pair<std::pair<ExplodedGraph*, NodeBackMap*>, - std::pair<ExplodedNode*, unsigned> > -MakeReportGraph(const ExplodedGraph* G, - SmallVectorImpl<const ExplodedNode*> &nodes) { +namespace { +/// A wrapper around a report graph, which contains only a single path, and its +/// node maps. +class ReportGraph { +public: + InterExplodedGraphMap BackMap; + OwningPtr<ExplodedGraph> Graph; + const ExplodedNode *ErrorNode; + size_t Index; +}; + +/// A wrapper around a trimmed graph and its node maps. +class TrimmedGraph { + InterExplodedGraphMap InverseMap; + + typedef llvm::DenseMap<const ExplodedNode *, unsigned> PriorityMapTy; + PriorityMapTy PriorityMap; + + typedef std::pair<const ExplodedNode *, size_t> NodeIndexPair; + SmallVector<NodeIndexPair, 32> ReportNodes; - // Create the trimmed graph. It will contain the shortest paths from the - // error nodes to the root. In the new graph we should only have one - // error node unless there are two or more error nodes with the same minimum - // path length. - ExplodedGraph* GTrim; - InterExplodedGraphMap* NMap; + OwningPtr<ExplodedGraph> G; - llvm::DenseMap<const void*, const void*> InverseMap; - llvm::tie(GTrim, NMap) = G->Trim(nodes.data(), nodes.data() + nodes.size(), - &InverseMap); + /// A helper class for sorting ExplodedNodes by priority. + template <bool Descending> + class PriorityCompare { + const PriorityMapTy &PriorityMap; - // Create owning pointers for GTrim and NMap just to ensure that they are - // released when this function exists. - OwningPtr<ExplodedGraph> AutoReleaseGTrim(GTrim); - OwningPtr<InterExplodedGraphMap> AutoReleaseNMap(NMap); + public: + PriorityCompare(const PriorityMapTy &M) : PriorityMap(M) {} + + bool operator()(const ExplodedNode *LHS, const ExplodedNode *RHS) const { + PriorityMapTy::const_iterator LI = PriorityMap.find(LHS); + PriorityMapTy::const_iterator RI = PriorityMap.find(RHS); + PriorityMapTy::const_iterator E = PriorityMap.end(); + + if (LI == E) + return Descending; + if (RI == E) + return !Descending; + + return Descending ? LI->second > RI->second + : LI->second < RI->second; + } + + bool operator()(const NodeIndexPair &LHS, const NodeIndexPair &RHS) const { + return (*this)(LHS.first, RHS.first); + } + }; + +public: + TrimmedGraph(const ExplodedGraph *OriginalGraph, + ArrayRef<const ExplodedNode *> Nodes); + + bool popNextReportGraph(ReportGraph &GraphWrapper); +}; +} + +TrimmedGraph::TrimmedGraph(const ExplodedGraph *OriginalGraph, + ArrayRef<const ExplodedNode *> Nodes) { + // The trimmed graph is created in the body of the constructor to ensure + // that the DenseMaps have been initialized already. + InterExplodedGraphMap ForwardMap; + G.reset(OriginalGraph->trim(Nodes, &ForwardMap, &InverseMap)); // 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 + // the node map which maps from nodes in the original graph to nodes // in the new graph. + llvm::SmallPtrSet<const ExplodedNode *, 32> RemainingNodes; - std::queue<const ExplodedNode*> WS; - typedef llvm::DenseMap<const ExplodedNode*, unsigned> IndexMapTy; - IndexMapTy IndexMap; - - for (unsigned nodeIndex = 0 ; nodeIndex < nodes.size(); ++nodeIndex) { - const ExplodedNode *originalNode = nodes[nodeIndex]; - if (const ExplodedNode *N = NMap->getMappedNode(originalNode)) { - WS.push(N); - IndexMap[originalNode] = nodeIndex; + for (unsigned i = 0, count = Nodes.size(); i < count; ++i) { + if (const ExplodedNode *NewNode = ForwardMap.lookup(Nodes[i])) { + ReportNodes.push_back(std::make_pair(NewNode, i)); + RemainingNodes.insert(NewNode); } } - assert(!WS.empty() && "No error node found in the trimmed graph."); - - // Create a new (third!) graph with a single path. This is the graph - // that will be returned to the caller. - ExplodedGraph *GNew = new ExplodedGraph(); + assert(!RemainingNodes.empty() && "No error node found in the trimmed graph"); - // Sometimes the trimmed graph can contain a cycle. Perform a reverse BFS - // to the root node, and then construct a new graph that contains only - // a single path. - llvm::DenseMap<const void*,unsigned> Visited; + // Perform a forward BFS to find all the shortest paths. + std::queue<const ExplodedNode *> WS; - unsigned cnt = 0; - const ExplodedNode *Root = 0; + assert(G->num_roots() == 1); + WS.push(*G->roots_begin()); + unsigned Priority = 0; while (!WS.empty()) { const ExplodedNode *Node = WS.front(); WS.pop(); - if (Visited.find(Node) != Visited.end()) - continue; + PriorityMapTy::iterator PriorityEntry; + bool IsNew; + llvm::tie(PriorityEntry, IsNew) = + PriorityMap.insert(std::make_pair(Node, Priority)); + ++Priority; - Visited[Node] = cnt++; - - if (Node->pred_empty()) { - Root = Node; - break; + if (!IsNew) { + assert(PriorityEntry->second <= Priority); + continue; } - for (ExplodedNode::const_pred_iterator I=Node->pred_begin(), - E=Node->pred_end(); I!=E; ++I) + if (RemainingNodes.erase(Node)) + if (RemainingNodes.empty()) + break; + + for (ExplodedNode::const_pred_iterator I = Node->succ_begin(), + E = Node->succ_end(); + I != E; ++I) WS.push(*I); } - assert(Root); + // Sort the error paths from longest to shortest. + std::sort(ReportNodes.begin(), ReportNodes.end(), + PriorityCompare<true>(PriorityMap)); +} - // Now walk from the root down the BFS path, always taking the successor - // with the lowest number. - ExplodedNode *Last = 0, *First = 0; - NodeBackMap *BM = new NodeBackMap(); - unsigned NodeIndex = 0; +bool TrimmedGraph::popNextReportGraph(ReportGraph &GraphWrapper) { + if (ReportNodes.empty()) + return false; - for ( const ExplodedNode *N = Root ;;) { - // Lookup the number associated with the current node. - llvm::DenseMap<const void*,unsigned>::iterator I = Visited.find(N); - assert(I != Visited.end()); + const ExplodedNode *OrigN; + llvm::tie(OrigN, GraphWrapper.Index) = ReportNodes.pop_back_val(); + assert(PriorityMap.find(OrigN) != PriorityMap.end() && + "error node not accessible from root"); + // Create a new graph with a single path. This is the graph + // that will be returned to the caller. + ExplodedGraph *GNew = new ExplodedGraph(); + GraphWrapper.Graph.reset(GNew); + GraphWrapper.BackMap.clear(); + + // Now walk from the error node up the BFS path, always taking the + // predeccessor with the lowest number. + ExplodedNode *Succ = 0; + while (true) { // Create the equivalent node in the new graph with the same state // and location. - ExplodedNode *NewN = GNew->getNode(N->getLocation(), N->getState()); + ExplodedNode *NewN = GNew->getNode(OrigN->getLocation(), OrigN->getState()); // Store the mapping to the original node. - llvm::DenseMap<const void*, const void*>::iterator IMitr=InverseMap.find(N); + InterExplodedGraphMap::const_iterator IMitr = InverseMap.find(OrigN); assert(IMitr != InverseMap.end() && "No mapping to original node."); - (*BM)[NewN] = (const ExplodedNode*) IMitr->second; + GraphWrapper.BackMap[NewN] = IMitr->second; // Link up the new node with the previous node. - if (Last) - NewN->addPredecessor(Last, *GNew); + if (Succ) + Succ->addPredecessor(NewN, *GNew); + else + GraphWrapper.ErrorNode = NewN; - Last = NewN; + Succ = NewN; // Are we at the final node? - IndexMapTy::iterator IMI = - IndexMap.find((const ExplodedNode*)(IMitr->second)); - if (IMI != IndexMap.end()) { - First = NewN; - NodeIndex = IMI->second; + if (OrigN->pred_empty()) { + GNew->addRoot(NewN); break; } - // Find the next successor node. We choose the node that is marked - // with the lowest DFS number. - ExplodedNode::const_succ_iterator SI = N->succ_begin(); - ExplodedNode::const_succ_iterator SE = N->succ_end(); - N = 0; - - for (unsigned MinVal = 0; SI != SE; ++SI) { - - I = Visited.find(*SI); - - if (I == Visited.end()) - continue; - - if (!N || I->second < MinVal) { - N = *SI; - MinVal = I->second; - } - } - - assert(N); + // Find the next predeccessor node. We choose the node that is marked + // with the lowest BFS number. + OrigN = *std::min_element(OrigN->pred_begin(), OrigN->pred_end(), + PriorityCompare<false>(PriorityMap)); } - assert(First); - - return std::make_pair(std::make_pair(GNew, BM), - std::make_pair(First, NodeIndex)); + return true; } + /// CompactPathDiagnostic - This function postprocesses a PathDiagnostic object /// and collapses PathDiagosticPieces that are expanded by macros. static void CompactPathDiagnostic(PathPieces &path, const SourceManager& SM) { @@ -1978,128 +2143,128 @@ bool GRBugReporter::generatePathDiagnostic(PathDiagnostic& PD, assert(!bugReports.empty()); bool HasValid = false; - SmallVector<const ExplodedNode *, 10> errorNodes; + bool HasInvalid = false; + SmallVector<const ExplodedNode *, 32> errorNodes; for (ArrayRef<BugReport*>::iterator I = bugReports.begin(), E = bugReports.end(); I != E; ++I) { if ((*I)->isValid()) { HasValid = true; errorNodes.push_back((*I)->getErrorNode()); } else { + // Keep the errorNodes list in sync with the bugReports list. + HasInvalid = true; errorNodes.push_back(0); } } - // If all the reports have been marked invalid, we're done. + // If all the reports have been marked invalid by a previous path generation, + // we're done. if (!HasValid) return false; - // Construct a new graph that contains only a single path from the error - // node to a root. - const std::pair<std::pair<ExplodedGraph*, NodeBackMap*>, - std::pair<ExplodedNode*, unsigned> >& - GPair = MakeReportGraph(&getGraph(), errorNodes); - - // Find the BugReport with the original location. - assert(GPair.second.second < bugReports.size()); - BugReport *R = bugReports[GPair.second.second]; - assert(R && "No original report found for sliced graph."); - assert(R->isValid() && "Report selected from trimmed graph marked invalid."); - - OwningPtr<ExplodedGraph> ReportGraph(GPair.first.first); - OwningPtr<NodeBackMap> BackMap(GPair.first.second); - const ExplodedNode *N = GPair.second.first; - - // Start building the path diagnostic... - PathDiagnosticBuilder PDB(*this, R, BackMap.get(), &PC); - - // Register additional node visitors. - R->addVisitor(new NilReceiverBRVisitor()); - R->addVisitor(new ConditionBRVisitor()); - - 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.resetPath(); - originalReportConfigToken = R->getConfigurationChangeToken(); - - // Generate the very last diagnostic piece - the piece is visible before - // the trace is expanded. - if (PDB.getGenerationScheme() != PathDiagnosticConsumer::None) { + typedef PathDiagnosticConsumer::PathGenerationScheme PathGenerationScheme; + PathGenerationScheme ActiveScheme = PC.getGenerationScheme(); + + TrimmedGraph TrimG(&getGraph(), errorNodes); + ReportGraph ErrorGraph; + + while (TrimG.popNextReportGraph(ErrorGraph)) { + // Find the BugReport with the original location. + assert(ErrorGraph.Index < bugReports.size()); + BugReport *R = bugReports[ErrorGraph.Index]; + assert(R && "No original report found for sliced graph."); + assert(R->isValid() && "Report selected by trimmed graph marked invalid."); + + // Start building the path diagnostic... + PathDiagnosticBuilder PDB(*this, R, ErrorGraph.BackMap, &PC); + const ExplodedNode *N = ErrorGraph.ErrorNode; + + // Register additional node visitors. + R->addVisitor(new NilReceiverBRVisitor()); + R->addVisitor(new ConditionBRVisitor()); + R->addVisitor(new LikelyFalsePositiveSuppressionBRVisitor()); + + BugReport::VisitorList visitors; + unsigned origReportConfigToken, 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.resetPath(); + origReportConfigToken = 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) { + I != E; ++I) { if (PathDiagnosticPiece *Piece = (*I)->getEndPath(PDB, N, *R)) { assert (!LastPiece && - "There can only be one final piece in a diagnostic."); + "There can only be one final piece in a diagnostic."); LastPiece = Piece; } } - if (!LastPiece) - LastPiece = BugReporterVisitor::getDefaultEndPath(PDB, N, *R); - if (LastPiece) - PD.setEndOfPath(LastPiece); - else - return false; - } - switch (PDB.getGenerationScheme()) { - case PathDiagnosticConsumer::Extensive: - if (!GenerateExtensivePathDiagnostic(PD, PDB, N, visitors)) { - assert(!R->isValid() && "Failed on valid report"); - // Try again. We'll filter out the bad report when we trim the graph. - // FIXME: It would be more efficient to use the same intermediate - // trimmed graph, and just repeat the shortest-path search. - return generatePathDiagnostic(PD, PC, bugReports); - } - break; - case PathDiagnosticConsumer::Minimal: - if (!GenerateMinimalPathDiagnostic(PD, PDB, N, visitors)) { - assert(!R->isValid() && "Failed on valid report"); - // Try again. We'll filter out the bad report when we trim the graph. - return generatePathDiagnostic(PD, PC, bugReports); + if (ActiveScheme != PathDiagnosticConsumer::None) { + if (!LastPiece) + LastPiece = BugReporterVisitor::getDefaultEndPath(PDB, N, *R); + assert(LastPiece); + PD.setEndOfPath(LastPiece); } - break; - case PathDiagnosticConsumer::None: - if (!GenerateVisitorsOnlyPathDiagnostic(PD, PDB, N, visitors)) { - assert(!R->isValid() && "Failed on valid report"); - // Try again. We'll filter out the bad report when we trim the graph. - return generatePathDiagnostic(PD, PC, bugReports); + + switch (ActiveScheme) { + case PathDiagnosticConsumer::Extensive: + GenerateExtensivePathDiagnostic(PD, PDB, N, visitors); + break; + case PathDiagnosticConsumer::Minimal: + GenerateMinimalPathDiagnostic(PD, PDB, N, visitors); + break; + case PathDiagnosticConsumer::None: + GenerateVisitorsOnlyPathDiagnostic(PD, PDB, N, visitors); + break; } - break; - } - // Clean up the visitors we used. - llvm::DeleteContainerPointers(visitors); + // Clean up the visitors we used. + llvm::DeleteContainerPointers(visitors); - // Did anything change while generating this path? - finalReportConfigToken = R->getConfigurationChangeToken(); - } while(finalReportConfigToken != originalReportConfigToken); + // Did anything change while generating this path? + finalReportConfigToken = R->getConfigurationChangeToken(); + } while (finalReportConfigToken != origReportConfigToken); - // Finally, prune the diagnostic path of uninteresting stuff. - if (!PD.path.empty()) { - // Remove messages that are basically the same. - removeRedundantMsgs(PD.getMutablePieces()); + if (!R->isValid()) + continue; - if (R->shouldPrunePath()) { - bool hasSomethingInteresting = RemoveUneededCalls(PD.getMutablePieces(), - R); - assert(hasSomethingInteresting); - (void) hasSomethingInteresting; + // Finally, prune the diagnostic path of uninteresting stuff. + if (!PD.path.empty()) { + // Remove messages that are basically the same. + removeRedundantMsgs(PD.getMutablePieces()); + + if (R->shouldPrunePath() && + getEngine().getAnalysisManager().options.shouldPrunePaths()) { + bool stillHasNotes = RemoveUnneededCalls(PD.getMutablePieces(), R); + assert(stillHasNotes); + (void)stillHasNotes; + } + + adjustCallLocations(PD.getMutablePieces()); } + + // We found a report and didn't suppress it. + return true; } - return true; + // We suppressed all the reports in this equivalence class. + assert(!HasInvalid && "Inconsistent suppression"); + (void)HasInvalid; + return false; } void BugReporter::Register(BugType *BT) { @@ -2265,7 +2430,12 @@ void BugReporter::FlushReport(BugReport *exampleReport, exampleReport->getBugType().getName(), exampleReport->getDescription(), exampleReport->getShortDescription(/*Fallback=*/false), - BT.getCategory())); + BT.getCategory(), + exampleReport->getUniqueingLocation(), + exampleReport->getUniqueingDecl())); + + MaxBugClassSize = std::max(bugReports.size(), + static_cast<size_t>(MaxBugClassSize)); // Generate the full path diagnostic, using the generation scheme // specified by the PathDiagnosticConsumer. Note that we have to generate @@ -2275,6 +2445,9 @@ void BugReporter::FlushReport(BugReport *exampleReport, if (!generatePathDiagnostic(*D.get(), PD, bugReports)) return; + MaxValidBugClassSize = std::max(bugReports.size(), + static_cast<size_t>(MaxValidBugClassSize)); + // If the path is empty, generate a single step path with the location // of the issue. if (D->path.empty()) { diff --git a/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp b/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp index 328e8a650df1..f600362da94b 100644 --- a/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp +++ b/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp @@ -12,22 +12,23 @@ // //===----------------------------------------------------------------------===// #include "clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitor.h" - #include "clang/AST/Expr.h" #include "clang/AST/ExprObjC.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" #include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringExtras.h" +#include "llvm/Support/raw_ostream.h" using namespace clang; using namespace ento; +using llvm::FoldingSetNodeID; + //===----------------------------------------------------------------------===// // Utility functions. //===----------------------------------------------------------------------===// @@ -39,37 +40,33 @@ bool bugreporter::isDeclRefExprToReference(const Expr *E) { return false; } -const Stmt *bugreporter::GetDerefExpr(const ExplodedNode *N) { +const Expr *bugreporter::getDerefExpr(const Stmt *S) { // Pattern match for a few useful cases (do something smarter later): // a[0], p->f, *p - const PostStmt *Loc = N->getLocationAs<PostStmt>(); - if (!Loc) - return 0; - - const Expr *S = dyn_cast<Expr>(Loc->getStmt()); - if (!S) + const Expr *E = dyn_cast<Expr>(S); + if (!E) return 0; - S = S->IgnoreParenCasts(); + E = E->IgnoreParenCasts(); while (true) { - if (const BinaryOperator *B = dyn_cast<BinaryOperator>(S)) { + if (const BinaryOperator *B = dyn_cast<BinaryOperator>(E)) { assert(B->isAssignmentOp()); - S = B->getLHS()->IgnoreParenCasts(); + E = B->getLHS()->IgnoreParenCasts(); continue; } - else if (const UnaryOperator *U = dyn_cast<UnaryOperator>(S)) { + else if (const UnaryOperator *U = dyn_cast<UnaryOperator>(E)) { if (U->getOpcode() == UO_Deref) return U->getSubExpr()->IgnoreParenCasts(); } - else if (const MemberExpr *ME = dyn_cast<MemberExpr>(S)) { + else if (const MemberExpr *ME = dyn_cast<MemberExpr>(E)) { if (ME->isArrow() || isDeclRefExprToReference(ME->getBase())) { return ME->getBase()->IgnoreParenCasts(); } } - else if (const ObjCIvarRefExpr *IvarRef = dyn_cast<ObjCIvarRefExpr>(S)) { + else if (const ObjCIvarRefExpr *IvarRef = dyn_cast<ObjCIvarRefExpr>(E)) { return IvarRef->getBase()->IgnoreParenCasts(); } - else if (const ArraySubscriptExpr *AE = dyn_cast<ArraySubscriptExpr>(S)) { + else if (const ArraySubscriptExpr *AE = dyn_cast<ArraySubscriptExpr>(E)) { return AE->getBase(); } break; @@ -137,13 +134,15 @@ class ReturnVisitor : public BugReporterVisitorImpl<ReturnVisitor> { const StackFrameContext *StackFrame; enum { Initial, - MaybeSuppress, + MaybeUnsuppress, Satisfied } Mode; + bool EnableNullFPSuppression; + public: - ReturnVisitor(const StackFrameContext *Frame) - : StackFrame(Frame), Mode(Initial) {} + ReturnVisitor(const StackFrameContext *Frame, bool Suppressed) + : StackFrame(Frame), Mode(Initial), EnableNullFPSuppression(Suppressed) {} static void *getTag() { static int Tag = 0; @@ -153,6 +152,7 @@ public: virtual void Profile(llvm::FoldingSetNodeID &ID) const { ID.AddPointer(ReturnVisitor::getTag()); ID.AddPointer(StackFrame); + ID.AddBoolean(EnableNullFPSuppression); } /// Adds a ReturnVisitor if the given statement represents a call that was @@ -163,16 +163,17 @@ public: /// the statement is a call that was inlined, we add the visitor to the /// bug report, so it can print a note later. static void addVisitorIfNecessary(const ExplodedNode *Node, const Stmt *S, - BugReport &BR) { + BugReport &BR, + bool InEnableNullFPSuppression) { if (!CallEvent::isCallStmt(S)) return; // First, find when we processed the statement. do { - if (const CallExitEnd *CEE = Node->getLocationAs<CallExitEnd>()) + if (Optional<CallExitEnd> CEE = Node->getLocationAs<CallExitEnd>()) if (CEE->getCalleeContext()->getCallSite() == S) break; - if (const StmtPoint *SP = Node->getLocationAs<StmtPoint>()) + if (Optional<StmtPoint> SP = Node->getLocationAs<StmtPoint>()) if (SP->getStmt() == S) break; @@ -180,19 +181,41 @@ public: } while (Node); // Next, step over any post-statement checks. - while (Node && isa<PostStmt>(Node->getLocation())) + while (Node && Node->getLocation().getAs<PostStmt>()) Node = Node->getFirstPred(); + if (!Node) + return; // Finally, see if we inlined the call. - if (Node) { - if (const CallExitEnd *CEE = Node->getLocationAs<CallExitEnd>()) { - const StackFrameContext *CalleeContext = CEE->getCalleeContext(); - if (CalleeContext->getCallSite() == S) { - BR.markInteresting(CalleeContext); - BR.addVisitor(new ReturnVisitor(CalleeContext)); - } - } - } + Optional<CallExitEnd> CEE = Node->getLocationAs<CallExitEnd>(); + if (!CEE) + return; + + const StackFrameContext *CalleeContext = CEE->getCalleeContext(); + if (CalleeContext->getCallSite() != S) + return; + + // Check the return value. + ProgramStateRef State = Node->getState(); + SVal RetVal = State->getSVal(S, Node->getLocationContext()); + + // Handle cases where a reference is returned and then immediately used. + if (cast<Expr>(S)->isGLValue()) + if (Optional<Loc> LValue = RetVal.getAs<Loc>()) + RetVal = State->getSVal(*LValue); + + // See if the return value is NULL. If so, suppress the report. + SubEngine *Eng = State->getStateManager().getOwningEngine(); + assert(Eng && "Cannot file a bug report without an owning engine"); + AnalyzerOptions &Options = Eng->getAnalysisManager().options; + + bool EnableNullFPSuppression = false; + if (InEnableNullFPSuppression && Options.shouldSuppressNullReturnPaths()) + if (Optional<Loc> RetLoc = RetVal.getAs<Loc>()) + EnableNullFPSuppression = State->isNull(*RetLoc).isConstrainedTrue(); + + BR.markInteresting(CalleeContext); + BR.addVisitor(new ReturnVisitor(CalleeContext, EnableNullFPSuppression)); } /// Returns true if any counter-suppression heuristics are enabled for @@ -209,7 +232,7 @@ public: if (N->getLocationContext() != StackFrame) return 0; - const StmtPoint *SP = N->getLocationAs<StmtPoint>(); + Optional<StmtPoint> SP = N->getLocationAs<StmtPoint>(); if (!SP) return 0; @@ -229,35 +252,49 @@ public: const Expr *RetE = Ret->getRetValue(); assert(RetE && "Tracking a return value for a void function"); + + // Handle cases where a reference is returned and then immediately used. + Optional<Loc> LValue; + if (RetE->isGLValue()) { + if ((LValue = V.getAs<Loc>())) { + SVal RValue = State->getRawSVal(*LValue, RetE->getType()); + if (RValue.getAs<DefinedSVal>()) + V = RValue; + } + } + + // Ignore aggregate rvalues. + if (V.getAs<nonloc::LazyCompoundVal>() || + V.getAs<nonloc::CompoundVal>()) + return 0; + RetE = RetE->IgnoreParenCasts(); // If we can't prove the return value is 0, just mark it interesting, and // make sure to track it into any further inner functions. - if (State->assume(cast<DefinedSVal>(V), true)) { + if (!State->isNull(V).isConstrainedTrue()) { BR.markInteresting(V); - ReturnVisitor::addVisitorIfNecessary(N, RetE, BR); + ReturnVisitor::addVisitorIfNecessary(N, RetE, BR, + EnableNullFPSuppression); return 0; } // If we're returning 0, we should track where that 0 came from. - bugreporter::trackNullOrUndefValue(N, RetE, BR); + bugreporter::trackNullOrUndefValue(N, RetE, BR, /*IsArg*/ false, + EnableNullFPSuppression); // Build an appropriate message based on the return value. SmallString<64> Msg; llvm::raw_svector_ostream Out(Msg); - if (isa<Loc>(V)) { - // If we are pruning null-return paths as unlikely error paths, mark the - // report invalid. We still want to emit a path note, however, in case + if (V.getAs<Loc>()) { + // If we have counter-suppression enabled, make sure we keep visiting + // future nodes. We want to emit a path note as well, in case // the report is resurrected as valid later on. ExprEngine &Eng = BRC.getBugReporter().getEngine(); AnalyzerOptions &Options = Eng.getAnalysisManager().options; - if (Options.shouldPruneNullReturnPaths()) { - if (hasCounterSuppression(Options)) - Mode = MaybeSuppress; - else - BR.markInvalid(ReturnVisitor::getTag(), StackFrame); - } + if (EnableNullFPSuppression && hasCounterSuppression(Options)) + Mode = MaybeUnsuppress; if (RetE->getType()->isObjCObjectPointerType()) Out << "Returning nil"; @@ -267,21 +304,37 @@ public: Out << "Returning zero"; } - // FIXME: We should have a more generalized location printing mechanism. - if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(RetE)) - if (const DeclaratorDecl *DD = dyn_cast<DeclaratorDecl>(DR->getDecl())) - Out << " (loaded from '" << *DD << "')"; + if (LValue) { + if (const MemRegion *MR = LValue->getAsRegion()) { + if (MR->canPrintPretty()) { + Out << " (reference to '"; + MR->printPretty(Out); + Out << "')"; + } + } + } else { + // FIXME: We should have a more generalized location printing mechanism. + if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(RetE)) + if (const DeclaratorDecl *DD = dyn_cast<DeclaratorDecl>(DR->getDecl())) + Out << " (loaded from '" << *DD << "')"; + } PathDiagnosticLocation L(Ret, BRC.getSourceManager(), StackFrame); return new PathDiagnosticEventPiece(L, Out.str()); } - PathDiagnosticPiece *visitNodeMaybeSuppress(const ExplodedNode *N, - const ExplodedNode *PrevN, - BugReporterContext &BRC, - BugReport &BR) { + PathDiagnosticPiece *visitNodeMaybeUnsuppress(const ExplodedNode *N, + const ExplodedNode *PrevN, + BugReporterContext &BRC, + BugReport &BR) { +#ifndef NDEBUG + ExprEngine &Eng = BRC.getBugReporter().getEngine(); + AnalyzerOptions &Options = Eng.getAnalysisManager().options; + assert(hasCounterSuppression(Options)); +#endif + // Are we at the entry node for this call? - const CallEnter *CE = N->getLocationAs<CallEnter>(); + Optional<CallEnter> CE = N->getLocationAs<CallEnter>(); if (!CE) return 0; @@ -290,41 +343,36 @@ public: Mode = Satisfied; - ExprEngine &Eng = BRC.getBugReporter().getEngine(); - AnalyzerOptions &Options = Eng.getAnalysisManager().options; - if (Options.shouldAvoidSuppressingNullArgumentPaths()) { - // Don't automatically suppress a report if one of the arguments is - // known to be a null pointer. Instead, start tracking /that/ null - // value back to its origin. - ProgramStateManager &StateMgr = BRC.getStateManager(); - CallEventManager &CallMgr = StateMgr.getCallEventManager(); - - ProgramStateRef State = N->getState(); - CallEventRef<> Call = CallMgr.getCaller(StackFrame, State); - for (unsigned I = 0, E = Call->getNumArgs(); I != E; ++I) { - SVal ArgV = Call->getArgSVal(I); - if (!isa<Loc>(ArgV)) - continue; + // Don't automatically suppress a report if one of the arguments is + // known to be a null pointer. Instead, start tracking /that/ null + // value back to its origin. + ProgramStateManager &StateMgr = BRC.getStateManager(); + CallEventManager &CallMgr = StateMgr.getCallEventManager(); - const Expr *ArgE = Call->getArgExpr(I); - if (!ArgE) - continue; - - // Is it possible for this argument to be non-null? - if (State->assume(cast<Loc>(ArgV), true)) - continue; - - if (bugreporter::trackNullOrUndefValue(N, ArgE, BR, /*IsArg=*/true)) - return 0; - - // If we /can't/ track the null pointer, we should err on the side of - // false negatives, and continue towards marking this report invalid. - // (We will still look at the other arguments, though.) - } + ProgramStateRef State = N->getState(); + CallEventRef<> Call = CallMgr.getCaller(StackFrame, State); + for (unsigned I = 0, E = Call->getNumArgs(); I != E; ++I) { + Optional<Loc> ArgV = Call->getArgSVal(I).getAs<Loc>(); + if (!ArgV) + continue; + + const Expr *ArgE = Call->getArgExpr(I); + if (!ArgE) + continue; + + // Is it possible for this argument to be non-null? + if (!State->isNull(*ArgV).isConstrainedTrue()) + continue; + + if (bugreporter::trackNullOrUndefValue(N, ArgE, BR, /*IsArg=*/true, + EnableNullFPSuppression)) + BR.removeInvalidation(ReturnVisitor::getTag(), StackFrame); + + // If we /can't/ track the null pointer, we should err on the side of + // false negatives, and continue towards marking this report invalid. + // (We will still look at the other arguments, though.) } - // There is no reason not to suppress this report; go ahead and do it. - BR.markInvalid(ReturnVisitor::getTag(), StackFrame); return 0; } @@ -335,14 +383,22 @@ public: switch (Mode) { case Initial: return visitNodeInitial(N, PrevN, BRC, BR); - case MaybeSuppress: - return visitNodeMaybeSuppress(N, PrevN, BRC, BR); + case MaybeUnsuppress: + return visitNodeMaybeUnsuppress(N, PrevN, BRC, BR); case Satisfied: return 0; } llvm_unreachable("Invalid visit mode!"); } + + PathDiagnosticPiece *getEndPath(BugReporterContext &BRC, + const ExplodedNode *N, + BugReport &BR) { + if (EnableNullFPSuppression) + BR.markInvalid(ReturnVisitor::getTag(), StackFrame); + return 0; + } }; } // end anonymous namespace @@ -352,6 +408,7 @@ void FindLastStoreBRVisitor ::Profile(llvm::FoldingSetNodeID &ID) const { ID.AddPointer(&tag); ID.AddPointer(R); ID.Add(V); + ID.AddBoolean(EnableNullFPSuppression); } PathDiagnosticPiece *FindLastStoreBRVisitor::VisitNode(const ExplodedNode *Succ, @@ -359,7 +416,7 @@ PathDiagnosticPiece *FindLastStoreBRVisitor::VisitNode(const ExplodedNode *Succ, BugReporterContext &BRC, BugReport &BR) { - if (satisfied) + if (Satisfied) return NULL; const ExplodedNode *StoreSite = 0; @@ -368,7 +425,7 @@ PathDiagnosticPiece *FindLastStoreBRVisitor::VisitNode(const ExplodedNode *Succ, // First see if we reached the declaration of the region. if (const VarRegion *VR = dyn_cast<VarRegion>(R)) { - if (const PostStmt *P = Pred->getLocationAs<PostStmt>()) { + if (Optional<PostStmt> P = Pred->getLocationAs<PostStmt>()) { if (const DeclStmt *DS = P->getStmtAs<DeclStmt>()) { if (DS->getSingleDecl() == VR->getDecl()) { StoreSite = Pred; @@ -378,19 +435,36 @@ PathDiagnosticPiece *FindLastStoreBRVisitor::VisitNode(const ExplodedNode *Succ, } } - // Otherwise, check that Succ has this binding and Pred does not, i.e. this is - // where the binding first occurred. + // If this is a post initializer expression, initializing the region, we + // should track the initializer expression. + if (Optional<PostInitializer> PIP = Pred->getLocationAs<PostInitializer>()) { + const MemRegion *FieldReg = (const MemRegion *)PIP->getLocationValue(); + if (FieldReg && FieldReg == R) { + StoreSite = Pred; + InitE = PIP->getInitializer()->getInit(); + } + } + + // Otherwise, see if this is the store site: + // (1) Succ has this binding and Pred does not, i.e. this is + // where the binding first occurred. + // (2) Succ has this binding and is a PostStore node for this region, i.e. + // the same binding was re-assigned here. if (!StoreSite) { if (Succ->getState()->getSVal(R) != V) return NULL; - if (Pred->getState()->getSVal(R) == V) - return NULL; + + if (Pred->getState()->getSVal(R) == V) { + Optional<PostStore> PS = Succ->getLocationAs<PostStore>(); + if (!PS || PS->getLocationValue() != R) + return NULL; + } StoreSite = Succ; // If this is an assignment expression, we can track the value // being assigned. - if (const PostStmt *P = Succ->getLocationAs<PostStmt>()) + if (Optional<PostStmt> P = Succ->getLocationAs<PostStmt>()) if (const BinaryOperator *BO = P->getStmtAs<BinaryOperator>()) if (BO->isAssignmentOp()) InitE = BO->getRHS(); @@ -399,34 +473,41 @@ PathDiagnosticPiece *FindLastStoreBRVisitor::VisitNode(const ExplodedNode *Succ, // FIXME: Handle CXXThisRegion as well. (This is not a priority because // 'this' should never be NULL, but this visitor isn't just for NULL and // UndefinedVal.) - if (const CallEnter *CE = Succ->getLocationAs<CallEnter>()) { - const VarRegion *VR = cast<VarRegion>(R); - const ParmVarDecl *Param = cast<ParmVarDecl>(VR->getDecl()); - - ProgramStateManager &StateMgr = BRC.getStateManager(); - CallEventManager &CallMgr = StateMgr.getCallEventManager(); - - CallEventRef<> Call = CallMgr.getCaller(CE->getCalleeContext(), - Succ->getState()); - InitE = Call->getArgExpr(Param->getFunctionScopeIndex()); - IsParam = true; + if (Optional<CallEnter> CE = Succ->getLocationAs<CallEnter>()) { + if (const VarRegion *VR = dyn_cast<VarRegion>(R)) { + const ParmVarDecl *Param = cast<ParmVarDecl>(VR->getDecl()); + + ProgramStateManager &StateMgr = BRC.getStateManager(); + CallEventManager &CallMgr = StateMgr.getCallEventManager(); + + CallEventRef<> Call = CallMgr.getCaller(CE->getCalleeContext(), + Succ->getState()); + InitE = Call->getArgExpr(Param->getFunctionScopeIndex()); + IsParam = true; + } } + + // If this is a CXXTempObjectRegion, the Expr responsible for its creation + // is wrapped inside of it. + if (const CXXTempObjectRegion *TmpR = dyn_cast<CXXTempObjectRegion>(R)) + InitE = TmpR->getExpr(); } if (!StoreSite) return NULL; - satisfied = true; + Satisfied = true; // If we have an expression that provided the value, try to track where it // came from. if (InitE) { - if (V.isUndef() || isa<loc::ConcreteInt>(V)) { + if (V.isUndef() || V.getAs<loc::ConcreteInt>()) { if (!IsParam) InitE = InitE->IgnoreParenCasts(); - bugreporter::trackNullOrUndefValue(StoreSite, InitE, BR, IsParam); + bugreporter::trackNullOrUndefValue(StoreSite, InitE, BR, IsParam, + EnableNullFPSuppression); } else { ReturnVisitor::addVisitorIfNecessary(StoreSite, InitE->IgnoreParenCasts(), - BR); + BR, EnableNullFPSuppression); } } @@ -437,73 +518,103 @@ PathDiagnosticPiece *FindLastStoreBRVisitor::VisitNode(const ExplodedNode *Succ, SmallString<256> sbuf; llvm::raw_svector_ostream os(sbuf); - if (const PostStmt *PS = StoreSite->getLocationAs<PostStmt>()) { - if (const DeclStmt *DS = PS->getStmtAs<DeclStmt>()) { - - if (const VarRegion *VR = dyn_cast<VarRegion>(R)) { - os << "Variable '" << *VR->getDecl() << "' "; + if (Optional<PostStmt> PS = StoreSite->getLocationAs<PostStmt>()) { + const Stmt *S = PS->getStmt(); + const char *action = 0; + const DeclStmt *DS = dyn_cast<DeclStmt>(S); + const VarRegion *VR = dyn_cast<VarRegion>(R); + + if (DS) { + action = "initialized to "; + } else if (isa<BlockExpr>(S)) { + action = "captured by block as "; + if (VR) { + // See if we can get the BlockVarRegion. + ProgramStateRef State = StoreSite->getState(); + SVal V = State->getSVal(S, PS->getLocationContext()); + if (const BlockDataRegion *BDR = + dyn_cast_or_null<BlockDataRegion>(V.getAsRegion())) { + if (const VarRegion *OriginalR = BDR->getOriginalRegion(VR)) { + if (Optional<KnownSVal> KV = + State->getSVal(OriginalR).getAs<KnownSVal>()) + BR.addVisitor(new FindLastStoreBRVisitor(*KV, OriginalR, + EnableNullFPSuppression)); + } + } } - else - return NULL; + } + + if (action) { + if (!R) + return 0; - if (isa<loc::ConcreteInt>(V)) { + os << '\''; + R->printPretty(os); + os << "' "; + + if (V.getAs<loc::ConcreteInt>()) { bool b = false; if (R->isBoundable()) { if (const TypedValueRegion *TR = dyn_cast<TypedValueRegion>(R)) { if (TR->getValueType()->isObjCObjectPointerType()) { - os << "initialized to nil"; + os << action << "nil"; b = true; } } } if (!b) - os << "initialized to a null pointer value"; - } - else if (isa<nonloc::ConcreteInt>(V)) { - os << "initialized to " << cast<nonloc::ConcreteInt>(V).getValue(); + os << action << "a null pointer value"; + } else if (Optional<nonloc::ConcreteInt> CVal = + V.getAs<nonloc::ConcreteInt>()) { + os << action << CVal->getValue(); } - else if (V.isUndef()) { - if (isa<VarRegion>(R)) { - const VarDecl *VD = cast<VarDecl>(DS->getSingleDecl()); - if (VD->getInit()) - os << "initialized to a garbage value"; - else - os << "declared without an initial value"; + else if (DS) { + if (V.isUndef()) { + if (isa<VarRegion>(R)) { + const VarDecl *VD = cast<VarDecl>(DS->getSingleDecl()); + if (VD->getInit()) + os << "initialized to a garbage value"; + else + os << "declared without an initial value"; + } + } + else { + os << "initialized here"; } - } - else { - os << "initialized here"; } } - } else if (isa<CallEnter>(StoreSite->getLocation())) { - const ParmVarDecl *Param = cast<ParmVarDecl>(cast<VarRegion>(R)->getDecl()); + } else if (StoreSite->getLocation().getAs<CallEnter>()) { + if (const VarRegion *VR = dyn_cast<VarRegion>(R)) { + const ParmVarDecl *Param = cast<ParmVarDecl>(VR->getDecl()); - os << "Passing "; + os << "Passing "; - if (isa<loc::ConcreteInt>(V)) { - if (Param->getType()->isObjCObjectPointerType()) - os << "nil object reference"; - else - os << "null pointer value"; - } else if (V.isUndef()) { - os << "uninitialized value"; - } else if (isa<nonloc::ConcreteInt>(V)) { - os << "the value " << cast<nonloc::ConcreteInt>(V).getValue(); - } else { - os << "value"; - } + if (V.getAs<loc::ConcreteInt>()) { + if (Param->getType()->isObjCObjectPointerType()) + os << "nil object reference"; + else + os << "null pointer value"; + } else if (V.isUndef()) { + os << "uninitialized value"; + } else if (Optional<nonloc::ConcreteInt> CI = + V.getAs<nonloc::ConcreteInt>()) { + os << "the value " << CI->getValue(); + } else { + os << "value"; + } - // Printed parameter indexes are 1-based, not 0-based. - unsigned Idx = Param->getFunctionScopeIndex() + 1; - os << " via " << Idx << llvm::getOrdinalSuffix(Idx) << " parameter '"; + // Printed parameter indexes are 1-based, not 0-based. + unsigned Idx = Param->getFunctionScopeIndex() + 1; + os << " via " << Idx << llvm::getOrdinalSuffix(Idx) << " parameter '"; - R->printPretty(os); - os << '\''; + R->printPretty(os); + os << '\''; + } } if (os.str().empty()) { - if (isa<loc::ConcreteInt>(V)) { + if (V.getAs<loc::ConcreteInt>()) { bool b = false; if (R->isBoundable()) { if (const TypedValueRegion *TR = dyn_cast<TypedValueRegion>(R)) { @@ -519,10 +630,9 @@ PathDiagnosticPiece *FindLastStoreBRVisitor::VisitNode(const ExplodedNode *Succ, } else if (V.isUndef()) { os << "Uninitialized value stored to "; - } - else if (isa<nonloc::ConcreteInt>(V)) { - os << "The value " << cast<nonloc::ConcreteInt>(V).getValue() - << " is assigned to "; + } else if (Optional<nonloc::ConcreteInt> CV = + V.getAs<nonloc::ConcreteInt>()) { + os << "The value " << CV->getValue() << " is assigned to "; } else os << "Value assigned to "; @@ -535,7 +645,7 @@ PathDiagnosticPiece *FindLastStoreBRVisitor::VisitNode(const ExplodedNode *Succ, // Construct a new PathDiagnosticPiece. ProgramPoint P = StoreSite->getLocation(); PathDiagnosticLocation L; - if (isa<CallEnter>(P)) + if (P.getAs<CallEnter>() && InitE) L = PathDiagnosticLocation(InitE, BRC.getSourceManager(), P.getLocationContext()); else @@ -558,32 +668,38 @@ const char *TrackConstraintBRVisitor::getTag() { return "TrackConstraintBRVisitor"; } +bool TrackConstraintBRVisitor::isUnderconstrained(const ExplodedNode *N) const { + if (IsZeroCheck) + return N->getState()->isNull(Constraint).isUnderconstrained(); + return N->getState()->assume(Constraint, !Assumption); +} + PathDiagnosticPiece * TrackConstraintBRVisitor::VisitNode(const ExplodedNode *N, const ExplodedNode *PrevN, BugReporterContext &BRC, BugReport &BR) { - if (isSatisfied) + if (IsSatisfied) return NULL; // Check if in the previous state it was feasible for this constraint // to *not* be true. - if (PrevN->getState()->assume(Constraint, !Assumption)) { + if (isUnderconstrained(PrevN)) { - isSatisfied = true; + IsSatisfied = true; // As a sanity check, make sure that the negation of the constraint // was infeasible in the current state. If it is feasible, we somehow // missed the transition point. - if (N->getState()->assume(Constraint, !Assumption)) + if (isUnderconstrained(N)) return NULL; // We found the transition point for the constraint. We now need to // pretty-print the constraint. (work-in-progress) - std::string sbuf; - llvm::raw_string_ostream os(sbuf); + SmallString<64> sbuf; + llvm::raw_svector_ostream os(sbuf); - if (isa<Loc>(Constraint)) { + if (Constraint.getAs<Loc>()) { os << "Assuming pointer value is "; os << (Assumption ? "non-null" : "null"); } @@ -606,25 +722,151 @@ TrackConstraintBRVisitor::VisitNode(const ExplodedNode *N, return NULL; } -bool bugreporter::trackNullOrUndefValue(const ExplodedNode *N, const Stmt *S, - BugReport &report, bool IsArg) { +SuppressInlineDefensiveChecksVisitor:: +SuppressInlineDefensiveChecksVisitor(DefinedSVal Value, const ExplodedNode *N) + : V(Value), IsSatisfied(false), IsTrackingTurnedOn(false) { + + // Check if the visitor is disabled. + SubEngine *Eng = N->getState()->getStateManager().getOwningEngine(); + assert(Eng && "Cannot file a bug report without an owning engine"); + AnalyzerOptions &Options = Eng->getAnalysisManager().options; + if (!Options.shouldSuppressInlinedDefensiveChecks()) + IsSatisfied = true; + + assert(N->getState()->isNull(V).isConstrainedTrue() && + "The visitor only tracks the cases where V is constrained to 0"); +} + +void SuppressInlineDefensiveChecksVisitor::Profile(FoldingSetNodeID &ID) const { + static int id = 0; + ID.AddPointer(&id); + ID.Add(V); +} + +const char *SuppressInlineDefensiveChecksVisitor::getTag() { + return "IDCVisitor"; +} + +PathDiagnosticPiece * +SuppressInlineDefensiveChecksVisitor::VisitNode(const ExplodedNode *Succ, + const ExplodedNode *Pred, + BugReporterContext &BRC, + BugReport &BR) { + if (IsSatisfied) + return 0; + + // Start tracking after we see the first state in which the value is null. + if (!IsTrackingTurnedOn) + if (Succ->getState()->isNull(V).isConstrainedTrue()) + IsTrackingTurnedOn = true; + if (!IsTrackingTurnedOn) + return 0; + + // Check if in the previous state it was feasible for this value + // to *not* be null. + if (!Pred->getState()->isNull(V).isConstrainedTrue()) { + IsSatisfied = true; + + assert(Succ->getState()->isNull(V).isConstrainedTrue()); + + // Check if this is inlined defensive checks. + const LocationContext *CurLC =Succ->getLocationContext(); + const LocationContext *ReportLC = BR.getErrorNode()->getLocationContext(); + if (CurLC != ReportLC && !CurLC->isParentOf(ReportLC)) + BR.markInvalid("Suppress IDC", CurLC); + } + return 0; +} + +static const MemRegion *getLocationRegionIfReference(const Expr *E, + const ExplodedNode *N) { + if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(E)) { + if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) { + if (!VD->getType()->isReferenceType()) + return 0; + ProgramStateManager &StateMgr = N->getState()->getStateManager(); + MemRegionManager &MRMgr = StateMgr.getRegionManager(); + return MRMgr.getVarRegion(VD, N->getLocationContext()); + } + } + + // FIXME: This does not handle other kinds of null references, + // for example, references from FieldRegions: + // struct Wrapper { int &ref; }; + // Wrapper w = { *(int *)0 }; + // w.ref = 1; + + return 0; +} + +static const Expr *peelOffOuterExpr(const Expr *Ex, + const ExplodedNode *N) { + Ex = Ex->IgnoreParenCasts(); + if (const ExprWithCleanups *EWC = dyn_cast<ExprWithCleanups>(Ex)) + return peelOffOuterExpr(EWC->getSubExpr(), N); + if (const OpaqueValueExpr *OVE = dyn_cast<OpaqueValueExpr>(Ex)) + return peelOffOuterExpr(OVE->getSourceExpr(), N); + + // Peel off the ternary operator. + if (const ConditionalOperator *CO = dyn_cast<ConditionalOperator>(Ex)) { + // Find a node where the branching occured and find out which branch + // we took (true/false) by looking at the ExplodedGraph. + const ExplodedNode *NI = N; + do { + ProgramPoint ProgPoint = NI->getLocation(); + if (Optional<BlockEdge> BE = ProgPoint.getAs<BlockEdge>()) { + const CFGBlock *srcBlk = BE->getSrc(); + if (const Stmt *term = srcBlk->getTerminator()) { + if (term == CO) { + bool TookTrueBranch = (*(srcBlk->succ_begin()) == BE->getDst()); + if (TookTrueBranch) + return peelOffOuterExpr(CO->getTrueExpr(), N); + else + return peelOffOuterExpr(CO->getFalseExpr(), N); + } + } + } + NI = NI->getFirstPred(); + } while (NI); + } + return Ex; +} + +bool bugreporter::trackNullOrUndefValue(const ExplodedNode *N, + const Stmt *S, + BugReport &report, bool IsArg, + bool EnableNullFPSuppression) { if (!S || !N) return false; - if (const OpaqueValueExpr *OVE = dyn_cast<OpaqueValueExpr>(S)) - S = OVE->getSourceExpr(); + if (const Expr *Ex = dyn_cast<Expr>(S)) { + Ex = Ex->IgnoreParenCasts(); + const Expr *PeeledEx = peelOffOuterExpr(Ex, N); + if (Ex != PeeledEx) + S = PeeledEx; + } + + const Expr *Inner = 0; + if (const Expr *Ex = dyn_cast<Expr>(S)) { + Ex = Ex->IgnoreParenCasts(); + if (ExplodedGraph::isInterestingLValueExpr(Ex) || CallEvent::isCallStmt(Ex)) + Inner = Ex; + } if (IsArg) { - assert(isa<CallEnter>(N->getLocation()) && "Tracking arg but not at call"); + assert(N->getLocation().getAs<CallEnter>() && "Tracking arg but not at call"); } else { // Walk through nodes until we get one that matches the statement exactly. + // Alternately, if we hit a known lvalue for the statement, we know we've + // gone too far (though we can likely track the lvalue better anyway). do { const ProgramPoint &pp = N->getLocation(); - if (const PostStmt *ps = dyn_cast<PostStmt>(&pp)) { - if (ps->getStmt() == S) + if (Optional<StmtPoint> ps = pp.getAs<StmtPoint>()) { + if (ps->getStmt() == S || ps->getStmt() == Inner) break; - } else if (const CallExitEnd *CEE = dyn_cast<CallExitEnd>(&pp)) { - if (CEE->getCalleeContext()->getCallSite() == S) + } else if (Optional<CallExitEnd> CEE = pp.getAs<CallExitEnd>()) { + if (CEE->getCalleeContext()->getCallSite() == S || + CEE->getCalleeContext()->getCallSite() == Inner) break; } N = N->getFirstPred(); @@ -636,129 +878,167 @@ bool bugreporter::trackNullOrUndefValue(const ExplodedNode *N, const Stmt *S, ProgramStateRef state = N->getState(); - // See if the expression we're interested refers to a variable. + // The message send could be nil due to the receiver being nil. + // At this point in the path, the receiver should be live since we are at the + // message send expr. If it is nil, start tracking it. + if (const Expr *Receiver = NilReceiverBRVisitor::getNilReceiver(S, N)) + trackNullOrUndefValue(N, Receiver, report, IsArg, EnableNullFPSuppression); + + + // See if the expression we're interested refers to a variable. // If so, we can track both its contents and constraints on its value. - if (const Expr *Ex = dyn_cast<Expr>(S)) { - // Strip off parens and casts. Note that this will never have issues with - // C++ user-defined implicit conversions, because those have a constructor - // or function call inside. - Ex = Ex->IgnoreParenCasts(); - if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(Ex)) { - // FIXME: Right now we only track VarDecls because it's non-trivial to - // get a MemRegion for any other DeclRefExprs. <rdar://problem/12114812> - if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) { - ProgramStateManager &StateMgr = state->getStateManager(); - MemRegionManager &MRMgr = StateMgr.getRegionManager(); - const VarRegion *R = MRMgr.getVarRegion(VD, N->getLocationContext()); - - // Mark both the variable region and its contents as interesting. - SVal V = state->getRawSVal(loc::MemRegionVal(R)); - - // If the value matches the default for the variable region, that - // might mean that it's been cleared out of the state. Fall back to - // the full argument expression (with casts and such intact). - if (IsArg) { - bool UseArgValue = V.isUnknownOrUndef() || V.isZeroConstant(); - if (!UseArgValue) { - const SymbolRegionValue *SRV = - dyn_cast_or_null<SymbolRegionValue>(V.getAsLocSymbol()); - if (SRV) - UseArgValue = (SRV->getRegion() == R); - } - if (UseArgValue) - V = state->getSValAsScalarOrLoc(S, N->getLocationContext()); + if (Inner && ExplodedGraph::isInterestingLValueExpr(Inner)) { + const MemRegion *R = 0; + + // Find the ExplodedNode where the lvalue (the value of 'Ex') + // was computed. We need this for getting the location value. + const ExplodedNode *LVNode = N; + while (LVNode) { + if (Optional<PostStmt> P = LVNode->getLocation().getAs<PostStmt>()) { + if (P->getStmt() == Inner) + break; + } + LVNode = LVNode->getFirstPred(); + } + assert(LVNode && "Unable to find the lvalue node."); + ProgramStateRef LVState = LVNode->getState(); + SVal LVal = LVState->getSVal(Inner, LVNode->getLocationContext()); + + if (LVState->isNull(LVal).isConstrainedTrue()) { + // In case of C++ references, we want to differentiate between a null + // reference and reference to null pointer. + // If the LVal is null, check if we are dealing with null reference. + // For those, we want to track the location of the reference. + if (const MemRegion *RR = getLocationRegionIfReference(Inner, N)) + R = RR; + } else { + R = LVState->getSVal(Inner, LVNode->getLocationContext()).getAsRegion(); + + // If this is a C++ reference to a null pointer, we are tracking the + // pointer. In additon, we should find the store at which the reference + // got initialized. + if (const MemRegion *RR = getLocationRegionIfReference(Inner, N)) { + if (Optional<KnownSVal> KV = LVal.getAs<KnownSVal>()) + report.addVisitor(new FindLastStoreBRVisitor(*KV, RR, + EnableNullFPSuppression)); + } + } + + if (R) { + // Mark both the variable region and its contents as interesting. + SVal V = state->getRawSVal(loc::MemRegionVal(R)); + + // If the value matches the default for the variable region, that + // might mean that it's been cleared out of the state. Fall back to + // the full argument expression (with casts and such intact). + if (IsArg) { + bool UseArgValue = V.isUnknownOrUndef() || V.isZeroConstant(); + if (!UseArgValue) { + const SymbolRegionValue *SRV = + dyn_cast_or_null<SymbolRegionValue>(V.getAsLocSymbol()); + if (SRV) + UseArgValue = (SRV->getRegion() == R); } + if (UseArgValue) + V = state->getSValAsScalarOrLoc(S, N->getLocationContext()); + } - report.markInteresting(R); - report.markInteresting(V); - report.addVisitor(new UndefOrNullArgVisitor(R)); + report.markInteresting(R); + report.markInteresting(V); + report.addVisitor(new UndefOrNullArgVisitor(R)); - // If the contents are symbolic, find out when they became null. - if (V.getAsLocSymbol()) { - BugReporterVisitor *ConstraintTracker - = new TrackConstraintBRVisitor(cast<DefinedSVal>(V), false); - report.addVisitor(ConstraintTracker); - } + if (isa<SymbolicRegion>(R)) { + TrackConstraintBRVisitor *VI = + new TrackConstraintBRVisitor(loc::MemRegionVal(R), false); + report.addVisitor(VI); + } - report.addVisitor(new FindLastStoreBRVisitor(V, R)); - return true; + // If the contents are symbolic, find out when they became null. + if (V.getAsLocSymbol()) { + BugReporterVisitor *ConstraintTracker = + new TrackConstraintBRVisitor(V.castAs<DefinedSVal>(), false); + report.addVisitor(ConstraintTracker); + + // Add visitor, which will suppress inline defensive checks. + if (N->getState()->isNull(V).isConstrainedTrue() && + EnableNullFPSuppression) { + BugReporterVisitor *IDCSuppressor = + new SuppressInlineDefensiveChecksVisitor(V.castAs<DefinedSVal>(), + N); + report.addVisitor(IDCSuppressor); + } } + + if (Optional<KnownSVal> KV = V.getAs<KnownSVal>()) + report.addVisitor(new FindLastStoreBRVisitor(*KV, R, + EnableNullFPSuppression)); + return true; } } - // If the expression does NOT refer to a variable, we can still track - // constraints on its contents. + // If the expression is not an "lvalue expression", we can still + // track the constraints on its contents. SVal V = state->getSValAsScalarOrLoc(S, N->getLocationContext()); + // If the value came from an inlined function call, we should at least make + // sure that function isn't pruned in our output. + if (const Expr *E = dyn_cast<Expr>(S)) + S = E->IgnoreParenCasts(); + + ReturnVisitor::addVisitorIfNecessary(N, S, report, EnableNullFPSuppression); + // Uncomment this to find cases where we aren't properly getting the // base value that was dereferenced. // assert(!V.isUnknownOrUndef()); - // Is it a symbolic value? - if (loc::MemRegionVal *L = dyn_cast<loc::MemRegionVal>(&V)) { + if (Optional<loc::MemRegionVal> L = V.getAs<loc::MemRegionVal>()) { // At this point we are dealing with the region's LValue. // However, if the rvalue is a symbolic region, we should track it as well. SVal RVal = state->getSVal(L->getRegion()); const MemRegion *RegionRVal = RVal.getAsRegion(); report.addVisitor(new UndefOrNullArgVisitor(L->getRegion())); - if (RegionRVal && isa<SymbolicRegion>(RegionRVal)) { report.markInteresting(RegionRVal); report.addVisitor(new TrackConstraintBRVisitor( loc::MemRegionVal(RegionRVal), false)); } - } else { - // Otherwise, if the value came from an inlined function call, - // we should at least make sure that function isn't pruned in our output. - if (const Expr *E = dyn_cast<Expr>(S)) - S = E->IgnoreParenCasts(); - ReturnVisitor::addVisitorIfNecessary(N, S, report); } return true; } -BugReporterVisitor * -FindLastStoreBRVisitor::createVisitorObject(const ExplodedNode *N, - const MemRegion *R) { - assert(R && "The memory region is null."); - - ProgramStateRef state = N->getState(); - SVal V = state->getSVal(R); - if (V.isUnknown()) +const Expr *NilReceiverBRVisitor::getNilReceiver(const Stmt *S, + const ExplodedNode *N) { + const ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(S); + if (!ME) return 0; - - return new FindLastStoreBRVisitor(V, R); + if (const Expr *Receiver = ME->getInstanceReceiver()) { + ProgramStateRef state = N->getState(); + SVal V = state->getSVal(Receiver, N->getLocationContext()); + if (state->isNull(V).isConstrainedTrue()) + return Receiver; + } + return 0; } - PathDiagnosticPiece *NilReceiverBRVisitor::VisitNode(const ExplodedNode *N, const ExplodedNode *PrevN, BugReporterContext &BRC, BugReport &BR) { - const PostStmt *P = N->getLocationAs<PostStmt>(); + Optional<PreStmt> P = N->getLocationAs<PreStmt>(); if (!P) return 0; - const ObjCMessageExpr *ME = P->getStmtAs<ObjCMessageExpr>(); - if (!ME) - return 0; - const Expr *Receiver = ME->getInstanceReceiver(); + + const Expr *Receiver = getNilReceiver(P->getStmt(), N); if (!Receiver) return 0; - ProgramStateRef state = N->getState(); - const SVal &V = state->getSVal(Receiver, N->getLocationContext()); - const DefinedOrUnknownSVal *DV = dyn_cast<DefinedOrUnknownSVal>(&V); - if (!DV) - return 0; - state = state->assume(*DV, true); - if (state) - return 0; // The receiver was nil, and hence the method was skipped. // Register a BugReporterVisitor to issue a message telling us how // the receiver was null. - bugreporter::trackNullOrUndefValue(N, Receiver, BR); + bugreporter::trackNullOrUndefValue(N, Receiver, BR, /*IsArg*/ false, + /*EnableNullFPSuppression*/ false); // Issue a message saying that the method was skipped. PathDiagnosticLocation L(Receiver, BRC.getSourceManager(), N->getLocationContext()); @@ -768,7 +1048,8 @@ PathDiagnosticPiece *NilReceiverBRVisitor::VisitNode(const ExplodedNode *N, // Registers every VarDecl inside a Stmt with a last store visitor. void FindLastStoreBRVisitor::registerStatementVarDecls(BugReport &BR, - const Stmt *S) { + const Stmt *S, + bool EnableNullFPSuppression) { const ExplodedNode *N = BR.getErrorNode(); std::deque<const Stmt *> WorkList; WorkList.push_back(S); @@ -788,9 +1069,10 @@ void FindLastStoreBRVisitor::registerStatementVarDecls(BugReport &BR, // What did we load? SVal V = state->getSVal(S, N->getLocationContext()); - if (isa<loc::ConcreteInt>(V) || isa<nonloc::ConcreteInt>(V)) { + if (V.getAs<loc::ConcreteInt>() || V.getAs<nonloc::ConcreteInt>()) { // Register a new visitor with the BugReport. - BR.addVisitor(new FindLastStoreBRVisitor(V, R)); + BR.addVisitor(new FindLastStoreBRVisitor(V.castAs<KnownSVal>(), R, + EnableNullFPSuppression)); } } } @@ -842,14 +1124,14 @@ PathDiagnosticPiece *ConditionBRVisitor::VisitNodeImpl(const ExplodedNode *N, // If an assumption was made on a branch, it should be caught // here by looking at the state transition. - if (const BlockEdge *BE = dyn_cast<BlockEdge>(&progPoint)) { + if (Optional<BlockEdge> BE = progPoint.getAs<BlockEdge>()) { const CFGBlock *srcBlk = BE->getSrc(); if (const Stmt *term = srcBlk->getTerminator()) return VisitTerminator(term, N, srcBlk, BE->getDst(), BR, BRC); return 0; } - if (const PostStmt *PS = dyn_cast<PostStmt>(&progPoint)) { + if (Optional<PostStmt> PS = progPoint.getAs<PostStmt>()) { // FIXME: Assuming that BugReporter is a GRBugReporter is a layering // violation. const std::pair<const ProgramPointTag *, const ProgramPointTag *> &tags = @@ -929,11 +1211,11 @@ ConditionBRVisitor::VisitTrueTest(const Expr *Cond, } } -bool ConditionBRVisitor::patternMatch(const Expr *Ex, llvm::raw_ostream &Out, +bool ConditionBRVisitor::patternMatch(const Expr *Ex, raw_ostream &Out, BugReporterContext &BRC, BugReport &report, const ExplodedNode *N, - llvm::Optional<bool> &prunable) { + Optional<bool> &prunable) { const Expr *OriginalExpr = Ex; Ex = Ex->IgnoreParenCasts(); @@ -992,7 +1274,7 @@ ConditionBRVisitor::VisitTrueTest(const Expr *Cond, const ExplodedNode *N) { bool shouldInvert = false; - llvm::Optional<bool> shouldPrune; + Optional<bool> shouldPrune; SmallString<128> LhsString, RhsString; { @@ -1161,6 +1443,58 @@ ConditionBRVisitor::VisitTrueTest(const Expr *Cond, return event; } + +// FIXME: Copied from ExprEngineCallAndReturn.cpp. +static bool isInStdNamespace(const Decl *D) { + const DeclContext *DC = D->getDeclContext()->getEnclosingNamespaceContext(); + const NamespaceDecl *ND = dyn_cast<NamespaceDecl>(DC); + if (!ND) + return false; + + while (const NamespaceDecl *Parent = dyn_cast<NamespaceDecl>(ND->getParent())) + ND = Parent; + + return ND->getName() == "std"; +} + + +PathDiagnosticPiece * +LikelyFalsePositiveSuppressionBRVisitor::getEndPath(BugReporterContext &BRC, + const ExplodedNode *N, + BugReport &BR) { + // Here we suppress false positives coming from system headers. This list is + // based on known issues. + + // Skip reports within the 'std' namespace. Although these can sometimes be + // the user's fault, we currently don't report them very well, and + // Note that this will not help for any other data structure libraries, like + // TR1, Boost, or llvm/ADT. + ExprEngine &Eng = BRC.getBugReporter().getEngine(); + AnalyzerOptions &Options = Eng.getAnalysisManager().options; + if (Options.shouldSuppressFromCXXStandardLibrary()) { + const LocationContext *LCtx = N->getLocationContext(); + if (isInStdNamespace(LCtx->getDecl())) { + BR.markInvalid(getTag(), 0); + return 0; + } + } + + // Skip reports within the sys/queue.h macros as we do not have the ability to + // reason about data structure shapes. + SourceManager &SM = BRC.getSourceManager(); + FullSourceLoc Loc = BR.getLocation(SM).asLocation(); + while (Loc.isMacroID()) { + if (SM.isInSystemMacro(Loc) && + (SM.getFilename(SM.getSpellingLoc(Loc)).endswith("sys/queue.h"))) { + BR.markInvalid(getTag(), 0); + return 0; + } + Loc = Loc.getSpellingLoc(); + } + + return 0; +} + PathDiagnosticPiece * UndefOrNullArgVisitor::VisitNode(const ExplodedNode *N, const ExplodedNode *PrevN, @@ -1171,7 +1505,7 @@ UndefOrNullArgVisitor::VisitNode(const ExplodedNode *N, ProgramPoint ProgLoc = N->getLocation(); // We are only interested in visiting CallEnter nodes. - CallEnter *CEnter = dyn_cast<CallEnter>(&ProgLoc); + Optional<CallEnter> CEnter = ProgLoc.getAs<CallEnter>(); if (!CEnter) return 0; diff --git a/lib/StaticAnalyzer/Core/CallEvent.cpp b/lib/StaticAnalyzer/Core/CallEvent.cpp index c5cb317bd18d..45b2e219d9e3 100644 --- a/lib/StaticAnalyzer/Core/CallEvent.cpp +++ b/lib/StaticAnalyzer/Core/CallEvent.cpp @@ -14,11 +14,12 @@ //===----------------------------------------------------------------------===// #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" -#include "clang/Analysis/ProgramPoint.h" #include "clang/AST/ParentMap.h" +#include "clang/Analysis/ProgramPoint.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" #include "llvm/ADT/SmallSet.h" #include "llvm/ADT/StringExtras.h" +#include "llvm/Support/raw_ostream.h" using namespace clang; using namespace ento; @@ -124,7 +125,7 @@ static bool isPointerToConst(QualType Ty) { // Try to retrieve the function declaration and find the function parameter // types which are pointers/references to a non-pointer const. // We will not invalidate the corresponding argument regions. -static void findPtrToConstParams(llvm::SmallSet<unsigned, 1> &PreserveArgs, +static void findPtrToConstParams(llvm::SmallSet<unsigned, 4> &PreserveArgs, const CallEvent &Call) { unsigned Idx = 0; for (CallEvent::param_type_iterator I = Call.param_type_begin(), @@ -136,69 +137,35 @@ static void findPtrToConstParams(llvm::SmallSet<unsigned, 1> &PreserveArgs, } ProgramStateRef CallEvent::invalidateRegions(unsigned BlockCount, - ProgramStateRef Orig) const { + ProgramStateRef Orig) const { ProgramStateRef Result = (Orig ? Orig : getState()); - SmallVector<const MemRegion *, 8> RegionsToInvalidate; - getExtraInvalidatedRegions(RegionsToInvalidate); + SmallVector<SVal, 8> ConstValues; + SmallVector<SVal, 8> ValuesToInvalidate; + + getExtraInvalidatedValues(ValuesToInvalidate); // Indexes of arguments whose values will be preserved by the call. - llvm::SmallSet<unsigned, 1> PreserveArgs; + llvm::SmallSet<unsigned, 4> PreserveArgs; if (!argumentsMayEscape()) findPtrToConstParams(PreserveArgs, *this); for (unsigned Idx = 0, Count = getNumArgs(); Idx != Count; ++Idx) { + // Mark this region for invalidation. We batch invalidate regions + // below for efficiency. if (PreserveArgs.count(Idx)) - continue; - - SVal V = getArgSVal(Idx); - - // If we are passing a location wrapped as an integer, unwrap it and - // invalidate the values referred by the location. - if (nonloc::LocAsInteger *Wrapped = dyn_cast<nonloc::LocAsInteger>(&V)) - V = Wrapped->getLoc(); - else if (!isa<Loc>(V)) - continue; - - if (const MemRegion *R = V.getAsRegion()) { - // 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 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 - // about (char*), etc., is just a form of passing raw bytes. - // e.g., void *p = alloca(); foo((char*)p); - if (const ElementRegion *ER = dyn_cast<ElementRegion>(R)) { - // Checking for 'integral type' is probably too promiscuous, but - // we'll leave it in for now until we have a systematic way of - // handling all of these cases. Eventually we need to come up - // with an interface to StoreManager so that this logic can be - // 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()) { - const MemRegion *superReg = ER->getSuperRegion(); - if (isa<VarRegion>(superReg) || isa<FieldRegion>(superReg) || - isa<ObjCIvarRegion>(superReg)) - R = cast<TypedRegion>(superReg); - } - // FIXME: What about layers of ElementRegions? - } - - // Mark this region for invalidation. We batch invalidate regions - // below for efficiency. - RegionsToInvalidate.push_back(R); - } + ConstValues.push_back(getArgSVal(Idx)); + else + ValuesToInvalidate.push_back(getArgSVal(Idx)); } // Invalidate designated regions using the batch invalidation API. // NOTE: Even if RegionsToInvalidate is empty, we may still invalidate // global variables. - return Result->invalidateRegions(RegionsToInvalidate, getOriginExpr(), + return Result->invalidateRegions(ValuesToInvalidate, getOriginExpr(), BlockCount, getLocationContext(), - /*Symbols=*/0, this); + /*CausedByPointerEscape*/ true, + /*Symbols=*/0, this, ConstValues); } ProgramPoint CallEvent::getProgramPoint(bool IsPreVisit, @@ -268,7 +235,6 @@ bool CallEvent::isCallStmt(const Stmt *S) { || isa<CXXNewExpr>(S); } -/// \brief Returns the result type, adjusted for references. QualType CallEvent::getDeclaredResultType(const Decl *D) { assert(D); if (const FunctionDecl* FD = dyn_cast<FunctionDecl>(D)) @@ -405,9 +371,8 @@ const FunctionDecl *CXXInstanceCall::getDecl() const { return getSVal(CE->getCallee()).getAsFunctionDecl(); } -void CXXInstanceCall::getExtraInvalidatedRegions(RegionList &Regions) const { - if (const MemRegion *R = getCXXThisVal().getAsRegion()) - Regions.push_back(R); +void CXXInstanceCall::getExtraInvalidatedValues(ValueList &Values) const { + Values.push_back(getCXXThisVal()); } SVal CXXInstanceCall::getCXXThisVal() const { @@ -417,7 +382,7 @@ SVal CXXInstanceCall::getCXXThisVal() const { return UnknownVal(); SVal ThisVal = getSVal(Base); - assert(ThisVal.isUnknownOrUndef() || isa<Loc>(ThisVal)); + assert(ThisVal.isUnknownOrUndef() || ThisVal.getAs<Loc>()); return ThisVal; } @@ -560,10 +525,10 @@ CallEvent::param_iterator BlockCall::param_end() const { return D->param_end(); } -void BlockCall::getExtraInvalidatedRegions(RegionList &Regions) const { +void BlockCall::getExtraInvalidatedValues(ValueList &Values) const { // FIXME: This also needs to invalidate captured globals. if (const MemRegion *R = getBlockRegion()) - Regions.push_back(R); + Values.push_back(loc::MemRegionVal(R)); } void BlockCall::getInitialStackFrameContents(const StackFrameContext *CalleeCtx, @@ -581,9 +546,9 @@ SVal CXXConstructorCall::getCXXThisVal() const { return UnknownVal(); } -void CXXConstructorCall::getExtraInvalidatedRegions(RegionList &Regions) const { +void CXXConstructorCall::getExtraInvalidatedValues(ValueList &Values) const { if (Data) - Regions.push_back(static_cast<const MemRegion *>(Data)); + Values.push_back(loc::MemRegionVal(static_cast<const MemRegion *>(Data))); } void CXXConstructorCall::getInitialStackFrameContents( @@ -635,9 +600,8 @@ CallEvent::param_iterator ObjCMethodCall::param_end() const { } void -ObjCMethodCall::getExtraInvalidatedRegions(RegionList &Regions) const { - if (const MemRegion *R = getReceiverSVal().getAsRegion()) - Regions.push_back(R); +ObjCMethodCall::getExtraInvalidatedValues(ValueList &Values) const { + Values.push_back(getReceiverSVal()); } SVal ObjCMethodCall::getSelfSVal() const { @@ -834,7 +798,34 @@ RuntimeDefinition ObjCMethodCall::getRuntimeDefinition() const { // Lookup the method implementation. if (ReceiverT) if (ObjCInterfaceDecl *IDecl = ReceiverT->getInterfaceDecl()) { - const ObjCMethodDecl *MD = IDecl->lookupPrivateMethod(Sel); + // Repeatedly calling lookupPrivateMethod() is expensive, especially + // when in many cases it returns null. We cache the results so + // that repeated queries on the same ObjCIntefaceDecl and Selector + // don't incur the same cost. On some test cases, we can see the + // same query being issued thousands of times. + // + // NOTE: This cache is essentially a "global" variable, but it + // only gets lazily created when we get here. The value of the + // cache probably comes from it being global across ExprEngines, + // where the same queries may get issued. If we are worried about + // concurrency, or possibly loading/unloading ASTs, etc., we may + // need to revisit this someday. In terms of memory, this table + // stays around until clang quits, which also may be bad if we + // need to release memory. + typedef std::pair<const ObjCInterfaceDecl*, Selector> + PrivateMethodKey; + typedef llvm::DenseMap<PrivateMethodKey, + Optional<const ObjCMethodDecl *> > + PrivateMethodCache; + + static PrivateMethodCache PMC; + Optional<const ObjCMethodDecl *> &Val = PMC[std::make_pair(IDecl, Sel)]; + + // Query lookupPrivateMethod() if the cache does not hit. + if (!Val.hasValue()) + Val = IDecl->lookupPrivateMethod(Sel); + + const ObjCMethodDecl *MD = Val.getValue(); if (CanBeSubClassed) return RuntimeDefinition(MD, Receiver); else @@ -931,8 +922,9 @@ CallEventManager::getCaller(const StackFrameContext *CalleeCtx, // destructors, though this could change in the future. const CFGBlock *B = CalleeCtx->getCallSiteBlock(); CFGElement E = (*B)[CalleeCtx->getIndex()]; - assert(isa<CFGImplicitDtor>(E) && "All other CFG elements should have exprs"); - assert(!isa<CFGTemporaryDtor>(E) && "We don't handle temporaries yet"); + assert(E.getAs<CFGImplicitDtor>() && + "All other CFG elements should have exprs"); + assert(!E.getAs<CFGTemporaryDtor>() && "We don't handle temporaries yet"); SValBuilder &SVB = State->getStateManager().getSValBuilder(); const CXXDestructorDecl *Dtor = cast<CXXDestructorDecl>(CalleeCtx->getDecl()); @@ -940,11 +932,12 @@ CallEventManager::getCaller(const StackFrameContext *CalleeCtx, SVal ThisVal = State->getSVal(ThisPtr); const Stmt *Trigger; - if (const CFGAutomaticObjDtor *AutoDtor = dyn_cast<CFGAutomaticObjDtor>(&E)) + if (Optional<CFGAutomaticObjDtor> AutoDtor = E.getAs<CFGAutomaticObjDtor>()) Trigger = AutoDtor->getTriggerStmt(); else Trigger = Dtor->getBody(); return getCXXDestructorCall(Dtor, Trigger, ThisVal.getAsRegion(), - isa<CFGBaseDtor>(E), State, CallerCtx); + E.getAs<CFGBaseDtor>().hasValue(), State, + CallerCtx); } diff --git a/lib/StaticAnalyzer/Core/CheckerManager.cpp b/lib/StaticAnalyzer/Core/CheckerManager.cpp index 3672952b8f6e..8adf3262b379 100644 --- a/lib/StaticAnalyzer/Core/CheckerManager.cpp +++ b/lib/StaticAnalyzer/Core/CheckerManager.cpp @@ -12,11 +12,11 @@ //===----------------------------------------------------------------------===// #include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/AST/DeclBase.h" +#include "clang/Analysis/ProgramPoint.h" #include "clang/StaticAnalyzer/Core/Checker.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" -#include "clang/Analysis/ProgramPoint.h" -#include "clang/AST/DeclBase.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" using namespace clang; using namespace ento; @@ -30,7 +30,7 @@ bool CheckerManager::hasPathSensitiveCheckers() const { !LocationCheckers.empty() || !BindCheckers.empty() || !EndAnalysisCheckers.empty() || - !EndPathCheckers.empty() || + !EndFunctionCheckers.empty() || !BranchConditionCheckers.empty() || !LiveSymbolsCheckers.empty() || !DeadSymbolsCheckers.empty() || @@ -353,17 +353,17 @@ void CheckerManager::runCheckersForEndAnalysis(ExplodedGraph &G, /// \brief Run checkers for end of path. // 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, - ExplodedNode *Pred, - ExprEngine &Eng) { +void CheckerManager::runCheckersForEndFunction(NodeBuilderContext &BC, + ExplodedNodeSet &Dst, + ExplodedNode *Pred, + ExprEngine &Eng) { // 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 checkFn = EndPathCheckers[i]; + for (unsigned i = 0, e = EndFunctionCheckers.size(); i != e; ++i) { + CheckEndFunctionFunc checkFn = EndFunctionCheckers[i]; const ProgramPoint &L = BlockEntrance(BC.Block, Pred->getLocationContext(), @@ -469,10 +469,10 @@ bool CheckerManager::wantsRegionChangeUpdate(ProgramStateRef state) { /// \brief Run checkers for region changes. ProgramStateRef CheckerManager::runCheckersForRegionChanges(ProgramStateRef state, - const StoreManager::InvalidatedSymbols *invalidated, + const InvalidatedSymbols *invalidated, ArrayRef<const MemRegion *> ExplicitRegions, - ArrayRef<const MemRegion *> Regions, - const CallEvent *Call) { + ArrayRef<const MemRegion *> Regions, + const CallEvent *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. @@ -484,6 +484,27 @@ CheckerManager::runCheckersForRegionChanges(ProgramStateRef state, return state; } +/// \brief Run checkers to process symbol escape event. +ProgramStateRef +CheckerManager::runCheckersForPointerEscape(ProgramStateRef State, + const InvalidatedSymbols &Escaped, + const CallEvent *Call, + PointerEscapeKind Kind, + bool IsConst) { + assert((Call != NULL || + (Kind != PSK_DirectEscapeOnCall && + Kind != PSK_IndirectEscapeOnCall)) && + "Call must not be NULL when escaping on call"); + for (unsigned i = 0, e = PointerEscapeCheckers.size(); i != e; ++i) { + // If any checker declares the state infeasible (or if it starts that + // way), bail out. + if (!State) + return NULL; + State = PointerEscapeCheckers[i](State, Escaped, Call, Kind, IsConst); + } + return State; +} + /// \brief Run checkers for handling assumptions on symbolic values. ProgramStateRef CheckerManager::runCheckersForEvalAssume(ProgramStateRef state, @@ -618,8 +639,8 @@ void CheckerManager::_registerForEndAnalysis(CheckEndAnalysisFunc checkfn) { EndAnalysisCheckers.push_back(checkfn); } -void CheckerManager::_registerForEndPath(CheckEndPathFunc checkfn) { - EndPathCheckers.push_back(checkfn); +void CheckerManager::_registerForEndFunction(CheckEndFunctionFunc checkfn) { + EndFunctionCheckers.push_back(checkfn); } void CheckerManager::_registerForBranchCondition( @@ -641,6 +662,15 @@ void CheckerManager::_registerForRegionChanges(CheckRegionChangesFunc checkfn, RegionChangesCheckers.push_back(info); } +void CheckerManager::_registerForPointerEscape(CheckPointerEscapeFunc checkfn){ + PointerEscapeCheckers.push_back(checkfn); +} + +void CheckerManager::_registerForConstPointerEscape( + CheckPointerEscapeFunc checkfn) { + PointerEscapeCheckers.push_back(checkfn); +} + void CheckerManager::_registerForEvalAssume(EvalAssumeFunc checkfn) { EvalAssumeCheckers.push_back(checkfn); } diff --git a/lib/StaticAnalyzer/Core/CheckerRegistry.cpp b/lib/StaticAnalyzer/Core/CheckerRegistry.cpp index 9791e2ecbf92..47299030cc45 100644 --- a/lib/StaticAnalyzer/Core/CheckerRegistry.cpp +++ b/lib/StaticAnalyzer/Core/CheckerRegistry.cpp @@ -10,6 +10,7 @@ #include "clang/StaticAnalyzer/Core/CheckerRegistry.h" #include "clang/StaticAnalyzer/Core/CheckerOptInfo.h" #include "llvm/ADT/SetVector.h" +#include "llvm/Support/raw_ostream.h" using namespace clang; using namespace ento; @@ -109,7 +110,7 @@ void CheckerRegistry::initializeManager(CheckerManager &checkerMgr, } } -void CheckerRegistry::printHelp(llvm::raw_ostream &out, +void CheckerRegistry::printHelp(raw_ostream &out, size_t maxNameChars) const { // FIXME: Alphabetical sort puts 'experimental' in the middle. // Would it be better to name it '~experimental' or something else diff --git a/lib/StaticAnalyzer/Core/CoreEngine.cpp b/lib/StaticAnalyzer/Core/CoreEngine.cpp index ec2379212dc6..b09b2c2ddfab 100644 --- a/lib/StaticAnalyzer/Core/CoreEngine.cpp +++ b/lib/StaticAnalyzer/Core/CoreEngine.cpp @@ -14,14 +14,14 @@ #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" #include "clang/AST/Expr.h" #include "clang/AST/StmtCXX.h" -#include "llvm/Support/Casting.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/Statistic.h" +#include "llvm/Support/Casting.h" using namespace clang; using namespace ento; @@ -114,7 +114,7 @@ namespace { } virtual void enqueue(const WorkListUnit& U) { - if (isa<BlockEntrance>(U.getNode()->getLocation())) + if (U.getNode()->getLocation().getAs<BlockEntrance>()) Queue.push_front(U); else Stack.push_back(U); @@ -230,11 +230,11 @@ void CoreEngine::dispatchWorkItem(ExplodedNode* Pred, ProgramPoint Loc, // Dispatch on the location type. switch (Loc.getKind()) { case ProgramPoint::BlockEdgeKind: - HandleBlockEdge(cast<BlockEdge>(Loc), Pred); + HandleBlockEdge(Loc.castAs<BlockEdge>(), Pred); break; case ProgramPoint::BlockEntranceKind: - HandleBlockEntrance(cast<BlockEntrance>(Loc), Pred); + HandleBlockEntrance(Loc.castAs<BlockEntrance>(), Pred); break; case ProgramPoint::BlockExitKind: @@ -242,7 +242,7 @@ void CoreEngine::dispatchWorkItem(ExplodedNode* Pred, ProgramPoint Loc, break; case ProgramPoint::CallEnterKind: { - CallEnter CEnter = cast<CallEnter>(Loc); + CallEnter CEnter = Loc.castAs<CallEnter>(); SubEng.processCallEnter(CEnter, Pred); break; } @@ -259,10 +259,10 @@ void CoreEngine::dispatchWorkItem(ExplodedNode* Pred, ProgramPoint Loc, break; } default: - assert(isa<PostStmt>(Loc) || - isa<PostInitializer>(Loc) || - isa<PostImplicitCall>(Loc) || - isa<CallExitEnd>(Loc)); + assert(Loc.getAs<PostStmt>() || + Loc.getAs<PostInitializer>() || + Loc.getAs<PostImplicitCall>() || + Loc.getAs<CallExitEnd>()); HandlePostStmt(WU.getBlock(), WU.getIndex(), Pred); break; } @@ -331,9 +331,9 @@ void CoreEngine::HandleBlockEntrance(const BlockEntrance &L, WList->setBlockCounter(Counter); // Process the entrance of the block. - if (CFGElement E = L.getFirstElement()) { + if (Optional<CFGElement> E = L.getFirstElement()) { NodeBuilderContext Ctx(*this, L.getBlock(), Pred); - SubEng.processCFGElement(E, Pred, 0, &Ctx); + SubEng.processCFGElement(*E, Pred, 0, &Ctx); } else HandleBlockExit(L.getBlock(), Pred); @@ -346,6 +346,11 @@ void CoreEngine::HandleBlockExit(const CFGBlock * B, ExplodedNode *Pred) { default: llvm_unreachable("Analysis for this terminator not implemented."); + // Model static initializers. + case Stmt::DeclStmtClass: + HandleStaticInit(cast<DeclStmt>(Term), B, Pred); + return; + case Stmt::BinaryOperatorClass: // '&&' and '||' HandleBranch(cast<BinaryOperator>(Term)->getLHS(), Term, B, Pred); return; @@ -456,6 +461,19 @@ void CoreEngine::HandleBranch(const Stmt *Cond, const Stmt *Term, enqueue(Dst); } + +void CoreEngine::HandleStaticInit(const DeclStmt *DS, const CFGBlock *B, + ExplodedNode *Pred) { + assert(B->succ_size() == 2); + NodeBuilderContext Ctx(*this, B, Pred); + ExplodedNodeSet Dst; + SubEng.processStaticInitializer(DS, 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); @@ -495,7 +513,7 @@ void CoreEngine::enqueueStmtNode(ExplodedNode *N, assert (!N->isSink()); // Check if this node entered a callee. - if (isa<CallEnter>(N->getLocation())) { + if (N->getLocation().getAs<CallEnter>()) { // Still use the index of the CallExpr. It's needed to create the callee // StackFrameContext. WList->enqueue(N, Block, Idx); @@ -503,19 +521,19 @@ void CoreEngine::enqueueStmtNode(ExplodedNode *N, } // Do not create extra nodes. Move to the next CFG element. - if (isa<PostInitializer>(N->getLocation()) || - isa<PostImplicitCall>(N->getLocation())) { + if (N->getLocation().getAs<PostInitializer>() || + N->getLocation().getAs<PostImplicitCall>()) { WList->enqueue(N, Block, Idx+1); return; } - if (isa<EpsilonPoint>(N->getLocation())) { + if (N->getLocation().getAs<EpsilonPoint>()) { WList->enqueue(N, Block, Idx); return; } // At this point, we know we're processing a normal statement. - CFGStmt CS = cast<CFGStmt>((*Block)[Idx]); + CFGStmt CS = (*Block)[Idx].castAs<CFGStmt>(); PostStmt Loc(CS.getStmt(), N->getLocationContext()); if (Loc == N->getLocation()) { diff --git a/lib/StaticAnalyzer/Core/Environment.cpp b/lib/StaticAnalyzer/Core/Environment.cpp index bab89c545c34..fe352aa8b4cf 100644 --- a/lib/StaticAnalyzer/Core/Environment.cpp +++ b/lib/StaticAnalyzer/Core/Environment.cpp @@ -16,6 +16,7 @@ #include "clang/Analysis/AnalysisContext.h" #include "clang/Analysis/CFG.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" +#include "llvm/Support/raw_ostream.h" using namespace clang; using namespace ento; @@ -36,9 +37,6 @@ static const Expr *ignoreTransparentExprs(const Expr *E) { case Stmt::SubstNonTypeTemplateParmExprClass: E = cast<SubstNonTypeTemplateParmExpr>(E)->getReplacement(); break; - case Stmt::CXXDefaultArgExprClass: - E = cast<CXXDefaultArgExpr>(E)->getExpr(); - break; default: // This is the base case: we can't look through more than we already have. return E; @@ -74,7 +72,6 @@ SVal Environment::getSVal(const EnvironmentEntry &Entry, switch (S->getStmtClass()) { case Stmt::CXXBindTemporaryExprClass: - case Stmt::CXXDefaultArgExprClass: case Stmt::ExprWithCleanupsClass: case Stmt::GenericSelectionExprClass: case Stmt::OpaqueValueExprClass: @@ -149,19 +146,6 @@ Environment EnvironmentManager::bindExpr(Environment Env, return Environment(F.add(Env.ExprBindings, E, V)); } -EnvironmentEntry EnvironmentEntry::makeLocation() const { - EnvironmentEntry Result = *this; - reinterpret_cast<uintptr_t &>(Result.first) |= 0x1; - return Result; -} - -Environment EnvironmentManager::bindExprAndLocation(Environment Env, - const EnvironmentEntry &E, - SVal location, SVal V) { - return Environment(F.add(F.add(Env.ExprBindings, E.makeLocation(), location), - E, V)); -} - namespace { class MarkLiveCallback : public SymbolVisitor { SymbolReaper &SymReaper; @@ -178,14 +162,6 @@ public: }; } // end anonymous namespace -// 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); -} - // removeDeadBindings: // - Remove subexpression bindings. // - Remove dead block expression bindings. @@ -202,8 +178,6 @@ EnvironmentManager::removeDeadBindings(Environment Env, // individually removing all the subexpression bindings (which will greatly // outnumber block-level expression bindings). Environment NewEnv = getInitialEnvironment(); - - SmallVector<std::pair<EnvironmentEntry, SVal>, 10> deferredLocations; MarkLiveCallback CB(SymReaper); ScanReachableSymbols RSScaner(ST, CB); @@ -217,15 +191,6 @@ EnvironmentManager::removeDeadBindings(Environment Env, I != E; ++I) { 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. - // NOTE: This assumes that loads/stores that evaluated to UnknownVal - // still have an entry in the map. - if (IsLocation(BlkExpr)) { - deferredLocations.push_back(std::make_pair(BlkExpr, I.getData())); - continue; - } const SVal &X = I.getData(); if (SymReaper.isLive(BlkExpr.getStmt(), BlkExpr.getLocationContext())) { @@ -233,26 +198,18 @@ EnvironmentManager::removeDeadBindings(Environment Env, EBMapRef = EBMapRef.add(BlkExpr, X); // If the block expr's value is a memory region, then mark that region. - if (isa<loc::MemRegionVal>(X)) { - const MemRegion *R = cast<loc::MemRegionVal>(X).getRegion(); - SymReaper.markLive(R); - } + if (Optional<loc::MemRegionVal> R = X.getAs<loc::MemRegionVal>()) + SymReaper.markLive(R->getRegion()); // Mark all symbols in the block expr's value live. RSScaner.scan(X); continue; + } else { + SymExpr::symbol_iterator SI = X.symbol_begin(), SE = X.symbol_end(); + for (; SI != SE; ++SI) + SymReaper.maybeDead(*SI); } } - - // 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<EnvironmentEntry, SVal> >::iterator - I = deferredLocations.begin(), E = deferredLocations.end(); I != E; ++I) { - 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; @@ -260,30 +217,14 @@ EnvironmentManager::removeDeadBindings(Environment Env, 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:") + << "Expressions:" << NL; isFirst = false; } else { @@ -291,9 +232,6 @@ void Environment::printAux(raw_ostream &Out, bool printLocations, } const Stmt *S = En.getStmt(); - if (printLocations) { - S = (Stmt*) (((uintptr_t) S) & ((uintptr_t) ~0x1)); - } Out << " (" << (const void*) En.getLocationContext() << ',' << (const void*) S << ") "; diff --git a/lib/StaticAnalyzer/Core/ExplodedGraph.cpp b/lib/StaticAnalyzer/Core/ExplodedGraph.cpp index c284bd7dfad4..af9518acc79d 100644 --- a/lib/StaticAnalyzer/Core/ExplodedGraph.cpp +++ b/lib/StaticAnalyzer/Core/ExplodedGraph.cpp @@ -13,12 +13,12 @@ //===----------------------------------------------------------------------===// #include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h" +#include "clang/AST/ParentMap.h" +#include "clang/AST/Stmt.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.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/DenseSet.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/Statistic.h" #include <vector> @@ -56,19 +56,42 @@ ExplodedGraph::~ExplodedGraph() {} // Node reclamation. //===----------------------------------------------------------------------===// +bool ExplodedGraph::isInterestingLValueExpr(const Expr *Ex) { + if (!Ex->isLValue()) + return false; + return isa<DeclRefExpr>(Ex) || + isa<MemberExpr>(Ex) || + isa<ObjCIvarRefExpr>(Ex); +} + bool ExplodedGraph::shouldCollect(const ExplodedNode *node) { - // Reclaim all nodes that match *all* the following criteria: + // First, we only consider nodes for reclamation of the following + // conditions apply: // // (1) 1 predecessor (that has one successor) // (2) 1 successor (that has one predecessor) + // + // If a node has no successor it is on the "frontier", while a node + // with no predecessor is a root. + // + // After these prerequisites, we discard all "filler" nodes that + // are used only for intermediate processing, and are not essential + // for analyzer history: + // + // (a) PreStmtPurgeDeadSymbols + // + // We then discard all other nodes where *all* of the following conditions + // apply: + // // (3) The ProgramPoint is for a PostStmt, but not a PostStore. // (4) There is no 'tag' for the ProgramPoint. // (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 isn't for a non-consumed Stmt or Expr. - // (9) The successor is not a CallExpr StmtPoint (so that we would be able to - // find it when retrying a call with no inlining). + // (8) Expressions that are *not* lvalue expressions. + // (9) The PostStmt isn't for a non-consumed Stmt or Expr. + // (10) The successor is not a CallExpr StmtPoint (so that we would + // be able to find it when retrying a call with no inlining). // FIXME: It may be safe to reclaim PreCall and PostCall nodes as well. // Conditions 1 and 2. @@ -83,14 +106,18 @@ bool ExplodedGraph::shouldCollect(const ExplodedNode *node) { if (succ->pred_size() != 1) return false; - // Condition 3. + // Now reclaim any nodes that are (by definition) not essential to + // analysis history and are not consulted by any client code. ProgramPoint progPoint = node->getLocation(); - if (!isa<PostStmt>(progPoint) || isa<PostStore>(progPoint)) + if (progPoint.getAs<PreStmtPurgeDeadSymbols>()) + return !progPoint.getTag(); + + // Condition 3. + if (!progPoint.getAs<PostStmt>() || progPoint.getAs<PostStore>()) return false; // Condition 4. - PostStmt ps = cast<PostStmt>(progPoint); - if (ps.getTag()) + if (progPoint.getTag()) return false; // Conditions 5, 6, and 7. @@ -99,23 +126,30 @@ bool ExplodedGraph::shouldCollect(const ExplodedNode *node) { if (state->store != pred_state->store || state->GDM != pred_state->GDM || progPoint.getLocationContext() != pred->getLocationContext()) return false; - + + // All further checks require expressions. As per #3, we know that we have + // a PostStmt. + const Expr *Ex = dyn_cast<Expr>(progPoint.castAs<PostStmt>().getStmt()); + if (!Ex) + return false; + // Condition 8. + // Do not collect nodes for "interesting" lvalue expressions since they are + // used extensively for generating path diagnostics. + if (isInterestingLValueExpr(Ex)) + return false; + + // Condition 9. // Do not collect nodes for non-consumed Stmt or Expr to ensure precise // diagnostic generation; specifically, so that we could anchor arrows // pointing to the beginning of statements (as written in code). - if (!isa<Expr>(ps.getStmt())) + ParentMap &PM = progPoint.getLocationContext()->getParentMap(); + if (!PM.isConsumedExpr(Ex)) return false; - - if (const Expr *Ex = dyn_cast<Expr>(ps.getStmt())) { - ParentMap &PM = progPoint.getLocationContext()->getParentMap(); - if (!PM.isConsumedExpr(Ex)) - return false; - } - - // Condition 9. + + // Condition 10. const ProgramPoint SuccLoc = succ->getLocation(); - if (const StmtPoint *SP = dyn_cast<StmtPoint>(&SuccLoc)) + if (Optional<StmtPoint> SP = SuccLoc.getAs<StmtPoint>()) if (CallEvent::isCallStmt(SP->getStmt())) return false; @@ -297,45 +331,31 @@ ExplodedNode *ExplodedGraph::getNode(const ProgramPoint &L, return V; } -std::pair<ExplodedGraph*, InterExplodedGraphMap*> -ExplodedGraph::Trim(const NodeTy* const* NBeg, const NodeTy* const* NEnd, - llvm::DenseMap<const void*, const void*> *InverseMap) const { - - if (NBeg == NEnd) - return std::make_pair((ExplodedGraph*) 0, - (InterExplodedGraphMap*) 0); +ExplodedGraph * +ExplodedGraph::trim(ArrayRef<const NodeTy *> Sinks, + InterExplodedGraphMap *ForwardMap, + InterExplodedGraphMap *InverseMap) const{ - assert (NBeg < NEnd); - - OwningPtr<InterExplodedGraphMap> M(new InterExplodedGraphMap()); - - ExplodedGraph* G = TrimInternal(NBeg, NEnd, M.get(), InverseMap); - - return std::make_pair(static_cast<ExplodedGraph*>(G), M.take()); -} - -ExplodedGraph* -ExplodedGraph::TrimInternal(const ExplodedNode* const* BeginSources, - const ExplodedNode* const* EndSources, - InterExplodedGraphMap* M, - llvm::DenseMap<const void*, const void*> *InverseMap) const { + if (Nodes.empty()) + return 0; typedef llvm::DenseSet<const ExplodedNode*> Pass1Ty; Pass1Ty Pass1; - typedef llvm::DenseMap<const ExplodedNode*, ExplodedNode*> Pass2Ty; - Pass2Ty& Pass2 = M->M; + typedef InterExplodedGraphMap Pass2Ty; + InterExplodedGraphMap Pass2Scratch; + Pass2Ty &Pass2 = ForwardMap ? *ForwardMap : Pass2Scratch; SmallVector<const ExplodedNode*, 10> WL1, WL2; // ===- Pass 1 (reverse DFS) -=== - for (const ExplodedNode* const* I = BeginSources; I != EndSources; ++I) { + for (ArrayRef<const NodeTy *>::iterator I = Sinks.begin(), E = Sinks.end(); + I != E; ++I) { if (*I) WL1.push_back(*I); } - // Process the first worklist until it is empty. Because it is a std::list - // it acts like a FIFO queue. + // Process the first worklist until it is empty. while (!WL1.empty()) { const ExplodedNode *N = WL1.back(); WL1.pop_back(); @@ -398,7 +418,7 @@ ExplodedGraph::TrimInternal(const ExplodedNode* const* BeginSources, if (PI == Pass2.end()) continue; - NewN->addPredecessor(PI->second, *G); + NewN->addPredecessor(const_cast<ExplodedNode *>(PI->second), *G); } // In the case that some of the intended successors of NewN have already @@ -409,7 +429,7 @@ ExplodedGraph::TrimInternal(const ExplodedNode* const* BeginSources, I != E; ++I) { Pass2Ty::iterator PI = Pass2.find(*I); if (PI != Pass2.end()) { - PI->second->addPredecessor(NewN, *G); + const_cast<ExplodedNode *>(PI->second)->addPredecessor(NewN, *G); continue; } @@ -422,13 +442,3 @@ ExplodedGraph::TrimInternal(const ExplodedNode* const* BeginSources, return G; } -void InterExplodedGraphMap::anchor() { } - -ExplodedNode* -InterExplodedGraphMap::getMappedNode(const ExplodedNode *N) const { - llvm::DenseMap<const ExplodedNode*, ExplodedNode*>::const_iterator I = - M.find(N); - - return I == M.end() ? 0 : I->second; -} - diff --git a/lib/StaticAnalyzer/Core/ExprEngine.cpp b/lib/StaticAnalyzer/Core/ExprEngine.cpp index 045591c9074b..ab4dbd752519 100644 --- a/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -15,21 +15,21 @@ #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/CallEvent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" #include "clang/AST/CharUnits.h" #include "clang/AST/ParentMap.h" -#include "clang/AST/StmtObjC.h" #include "clang/AST/StmtCXX.h" +#include "clang/AST/StmtObjC.h" #include "clang/Basic/Builtins.h" -#include "clang/Basic/SourceManager.h" #include "clang/Basic/PrettyStackTrace.h" -#include "llvm/Support/raw_ostream.h" +#include "clang/Basic/SourceManager.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "llvm/ADT/ImmutableList.h" #include "llvm/ADT/Statistic.h" +#include "llvm/Support/raw_ostream.h" #ifndef NDEBUG #include "llvm/Support/GraphWriter.h" @@ -56,7 +56,8 @@ STATISTIC(NumTimesRetriedWithoutInlining, ExprEngine::ExprEngine(AnalysisManager &mgr, bool gcEnabled, SetOfConstDecls *VisitedCalleesIn, - FunctionSummariesTy *FS) + FunctionSummariesTy *FS, + InliningModes HowToInlineIn) : AMgr(mgr), AnalysisDeclContexts(mgr.getAnalysisDeclContextManager()), Engine(*this, FS), @@ -66,11 +67,11 @@ ExprEngine::ExprEngine(AnalysisManager &mgr, bool gcEnabled, this), SymMgr(StateMgr.getSymbolManager()), svalBuilder(StateMgr.getSValBuilder()), - EntryNode(NULL), - currStmt(NULL), currStmtIdx(0), currBldrCtx(0), + currStmtIdx(0), currBldrCtx(0), ObjCNoRet(mgr.getASTContext()), ObjCGCEnabled(gcEnabled), BR(mgr, *this), - VisitedCallees(VisitedCalleesIn) + VisitedCallees(VisitedCalleesIn), + HowToInline(HowToInlineIn) { unsigned TrimInterval = mgr.options.getGraphTrimInterval(); if (TrimInterval != 0) { @@ -117,8 +118,8 @@ ProgramStateRef ExprEngine::getInitialState(const LocationContext *InitLoc) { svalBuilder.makeZeroVal(T), getContext().IntTy); - DefinedOrUnknownSVal *Constraint = - dyn_cast<DefinedOrUnknownSVal>(&Constraint_untested); + Optional<DefinedOrUnknownSVal> Constraint = + Constraint_untested.getAs<DefinedOrUnknownSVal>(); if (!Constraint) break; @@ -137,7 +138,7 @@ ProgramStateRef ExprEngine::getInitialState(const LocationContext *InitLoc) { const MemRegion *R = state->getRegion(SelfD, InitLoc); SVal V = state->getSVal(loc::MemRegionVal(R)); - if (const Loc *LV = dyn_cast<Loc>(&V)) { + if (Optional<Loc> LV = V.getAs<Loc>()) { // Assume that the pointer value in 'self' is non-null. state = state->assume(*LV, true); assert(state && "'self' cannot be null"); @@ -153,7 +154,7 @@ ProgramStateRef ExprEngine::getInitialState(const LocationContext *InitLoc) { if (SFC->getParent() == 0) { loc::MemRegionVal L = svalBuilder.getCXXThis(MD, SFC); SVal V = state->getSVal(L); - if (const Loc *LV = dyn_cast<Loc>(&V)) { + if (Optional<Loc> LV = V.getAs<Loc>()) { state = state->assume(*LV, true); assert(state && "'this' cannot be null"); } @@ -164,20 +165,63 @@ ProgramStateRef ExprEngine::getInitialState(const LocationContext *InitLoc) { return state; } -/// If the value of the given expression is a NonLoc, copy it into a new -/// temporary region, and replace the value of the expression with that. -static ProgramStateRef createTemporaryRegionIfNeeded(ProgramStateRef State, - const LocationContext *LC, - const Expr *E) { - SVal V = State->getSVal(E, LC); - - if (isa<NonLoc>(V)) { - MemRegionManager &MRMgr = State->getStateManager().getRegionManager(); - const MemRegion *R = MRMgr.getCXXTempObjectRegion(E, LC); - State = State->bindLoc(loc::MemRegionVal(R), V); - State = State->BindExpr(E, LC, loc::MemRegionVal(R)); +ProgramStateRef +ExprEngine::createTemporaryRegionIfNeeded(ProgramStateRef State, + const LocationContext *LC, + const Expr *Ex, + const Expr *Result) { + SVal V = State->getSVal(Ex, LC); + if (!Result) { + // If we don't have an explicit result expression, we're in "if needed" + // mode. Only create a region if the current value is a NonLoc. + if (!V.getAs<NonLoc>()) + return State; + Result = Ex; + } else { + // We need to create a region no matter what. For sanity, make sure we don't + // try to stuff a Loc into a non-pointer temporary region. + assert(!V.getAs<Loc>() || Loc::isLocType(Result->getType())); } + ProgramStateManager &StateMgr = State->getStateManager(); + MemRegionManager &MRMgr = StateMgr.getRegionManager(); + StoreManager &StoreMgr = StateMgr.getStoreManager(); + + // We need to be careful about treating a derived type's value as + // bindings for a base type. Unless we're creating a temporary pointer region, + // start by stripping and recording base casts. + SmallVector<const CastExpr *, 4> Casts; + const Expr *Inner = Ex->IgnoreParens(); + if (!Loc::isLocType(Result->getType())) { + while (const CastExpr *CE = dyn_cast<CastExpr>(Inner)) { + if (CE->getCastKind() == CK_DerivedToBase || + CE->getCastKind() == CK_UncheckedDerivedToBase) + Casts.push_back(CE); + else if (CE->getCastKind() != CK_NoOp) + break; + + Inner = CE->getSubExpr()->IgnoreParens(); + } + } + + // Create a temporary object region for the inner expression (which may have + // a more derived type) and bind the value into it. + const TypedValueRegion *TR = MRMgr.getCXXTempObjectRegion(Inner, LC); + SVal Reg = loc::MemRegionVal(TR); + + if (V.isUnknown()) + V = getSValBuilder().conjureSymbolVal(Result, LC, TR->getValueType(), + currBldrCtx->blockCount()); + State = State->bindLoc(Reg, V); + + // Re-apply the casts (from innermost to outermost) for type sanity. + for (SmallVectorImpl<const CastExpr *>::reverse_iterator I = Casts.rbegin(), + E = Casts.rend(); + I != E; ++I) { + Reg = StoreMgr.evalDerivedToBase(Reg, *I); + } + + State = State->BindExpr(Result, LC, Reg); return State; } @@ -198,7 +242,7 @@ bool ExprEngine::wantsRegionChangeUpdate(ProgramStateRef state) { ProgramStateRef ExprEngine::processRegionChanges(ProgramStateRef state, - const StoreManager::InvalidatedSymbols *invalidated, + const InvalidatedSymbols *invalidated, ArrayRef<const MemRegion *> Explicits, ArrayRef<const MemRegion *> Regions, const CallEvent *Call) { @@ -221,19 +265,17 @@ void ExprEngine::processCFGElement(const CFGElement E, ExplodedNode *Pred, currBldrCtx = Ctx; switch (E.getKind()) { - case CFGElement::Invalid: - llvm_unreachable("Unexpected CFGElement kind."); case CFGElement::Statement: - ProcessStmt(const_cast<Stmt*>(E.getAs<CFGStmt>()->getStmt()), Pred); + ProcessStmt(const_cast<Stmt*>(E.castAs<CFGStmt>().getStmt()), Pred); return; case CFGElement::Initializer: - ProcessInitializer(E.getAs<CFGInitializer>()->getInitializer(), Pred); + ProcessInitializer(E.castAs<CFGInitializer>().getInitializer(), Pred); return; case CFGElement::AutomaticObjectDtor: case CFGElement::BaseDtor: case CFGElement::MemberDtor: case CFGElement::TemporaryDtor: - ProcessImplicitDtor(*E.getAs<CFGImplicitDtor>(), Pred); + ProcessImplicitDtor(E.castAs<CFGImplicitDtor>(), Pred); return; } currBldrCtx = 0; @@ -249,7 +291,7 @@ static bool shouldRemoveDeadBindings(AnalysisManager &AMgr, return false; // Is this the beginning of a basic block? - if (isa<BlockEntrance>(Pred->getLocation())) + if (Pred->getLocation().getAs<BlockEntrance>()) return true; // Is this on a non-expression? @@ -268,22 +310,39 @@ static bool shouldRemoveDeadBindings(AnalysisManager &AMgr, void ExprEngine::removeDead(ExplodedNode *Pred, ExplodedNodeSet &Out, const Stmt *ReferenceStmt, - const StackFrameContext *LC, + const LocationContext *LC, const Stmt *DiagnosticStmt, ProgramPoint::Kind K) { assert((K == ProgramPoint::PreStmtPurgeDeadSymbolsKind || - ReferenceStmt == 0) + ReferenceStmt == 0 || isa<ReturnStmt>(ReferenceStmt)) && "PostStmt is not generally supported by the SymbolReaper yet"); + assert(LC && "Must pass the current (or expiring) LocationContext"); + + if (!DiagnosticStmt) { + DiagnosticStmt = ReferenceStmt; + assert(DiagnosticStmt && "Required for clearing a LocationContext"); + } + NumRemoveDeadBindings++; - CleanedState = Pred->getState(); - SymbolReaper SymReaper(LC, ReferenceStmt, SymMgr, getStoreManager()); + ProgramStateRef CleanedState = Pred->getState(); + + // LC is the location context being destroyed, but SymbolReaper wants a + // location context that is still live. (If this is the top-level stack + // frame, this will be null.) + if (!ReferenceStmt) { + assert(K == ProgramPoint::PostStmtPurgeDeadSymbolsKind && + "Use PostStmtPurgeDeadSymbolsKind for clearing a LocationContext"); + LC = LC->getParent(); + } + + const StackFrameContext *SFC = LC ? LC->getCurrentStackFrame() : 0; + SymbolReaper SymReaper(SFC, ReferenceStmt, SymMgr, getStoreManager()); getCheckerManager().runCheckersForLiveSymbols(CleanedState, SymReaper); // Create a state in which dead bindings are removed from the environment // and the store. TODO: The function should just return new env and store, // not a new state. - const StackFrameContext *SFC = LC->getCurrentStackFrame(); CleanedState = StateMgr.removeDeadBindings(CleanedState, SFC, SymReaper); // Process any special transfer function for dead symbols. @@ -336,19 +395,17 @@ void ExprEngine::ProcessStmt(const CFGStmt S, // Reclaim any unnecessary nodes in the ExplodedGraph. G.reclaimRecentlyAllocatedNodes(); - currStmt = S.getStmt(); + const Stmt *currStmt = S.getStmt(); PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(), currStmt->getLocStart(), "Error evaluating statement"); // Remove dead bindings and symbols. - EntryNode = Pred; ExplodedNodeSet CleanedStates; - if (shouldRemoveDeadBindings(AMgr, S, Pred, EntryNode->getLocationContext())){ - removeDead(EntryNode, CleanedStates, currStmt, - Pred->getStackFrame(), currStmt); + if (shouldRemoveDeadBindings(AMgr, S, Pred, Pred->getLocationContext())){ + removeDead(Pred, CleanedStates, currStmt, Pred->getLocationContext()); } else - CleanedStates.Add(EntryNode); + CleanedStates.Add(Pred); // Visit the statement. ExplodedNodeSet Dst; @@ -362,11 +419,6 @@ void ExprEngine::ProcessStmt(const CFGStmt S, // Enqueue the new nodes onto the work list. Engine.enqueue(Dst, currBldrCtx->getBlock(), currStmtIdx); - - // NULL out these variables to cleanup. - CleanedState = NULL; - EntryNode = NULL; - currStmt = 0; } void ExprEngine::ProcessInitializer(const CFGInitializer Init, @@ -377,7 +429,7 @@ void ExprEngine::ProcessInitializer(const CFGInitializer Init, BMI->getSourceLocation(), "Error evaluating initializer"); - // We don't set EntryNode and currStmt. And we don't clean up state. + // We don't clean up dead bindings here. const StackFrameContext *stackFrame = cast<StackFrameContext>(Pred->getLocationContext()); const CXXConstructorDecl *decl = @@ -386,24 +438,52 @@ void ExprEngine::ProcessInitializer(const CFGInitializer Init, ProgramStateRef State = Pred->getState(); SVal thisVal = State->getSVal(svalBuilder.getCXXThis(decl, stackFrame)); - PostInitializer PP(BMI, stackFrame); ExplodedNodeSet Tmp(Pred); + SVal FieldLoc; // Evaluate the initializer, if necessary if (BMI->isAnyMemberInitializer()) { // Constructors build the object directly in the field, // but non-objects must be copied in from the initializer. - const Expr *Init = BMI->getInit(); + const Expr *Init = BMI->getInit()->IgnoreImplicit(); if (!isa<CXXConstructExpr>(Init)) { - SVal FieldLoc; - if (BMI->isIndirectMemberInitializer()) + const ValueDecl *Field; + if (BMI->isIndirectMemberInitializer()) { + Field = BMI->getIndirectMember(); FieldLoc = State->getLValue(BMI->getIndirectMember(), thisVal); - else + } else { + Field = BMI->getMember(); FieldLoc = State->getLValue(BMI->getMember(), thisVal); + } - SVal InitVal = State->getSVal(BMI->getInit(), stackFrame); + SVal InitVal; + if (BMI->getNumArrayIndices() > 0) { + // Handle arrays of trivial type. We can represent this with a + // primitive load/copy from the base array region. + const ArraySubscriptExpr *ASE; + while ((ASE = dyn_cast<ArraySubscriptExpr>(Init))) + Init = ASE->getBase()->IgnoreImplicit(); + + SVal LValue = State->getSVal(Init, stackFrame); + if (Optional<Loc> LValueLoc = LValue.getAs<Loc>()) + InitVal = State->getSVal(*LValueLoc); + + // If we fail to get the value for some reason, use a symbolic value. + if (InitVal.isUnknownOrUndef()) { + SValBuilder &SVB = getSValBuilder(); + InitVal = SVB.conjureSymbolVal(BMI->getInit(), stackFrame, + Field->getType(), + currBldrCtx->blockCount()); + } + } else { + InitVal = State->getSVal(BMI->getInit(), stackFrame); + } + assert(Tmp.size() == 1 && "have not generated any new nodes yet"); + assert(*Tmp.begin() == Pred && "have not generated any new nodes yet"); Tmp.clear(); + + PostInitializer PP(BMI, FieldLoc.getAsRegion(), stackFrame); evalBind(Tmp, Init, Pred, FieldLoc, InitVal, /*isInit=*/true, &PP); } } else { @@ -413,6 +493,7 @@ void ExprEngine::ProcessInitializer(const CFGInitializer Init, // Construct PostInitializer nodes whether the state changed or not, // so that the diagnostics don't get confused. + PostInitializer PP(BMI, FieldLoc.getAsRegion(), stackFrame); ExplodedNodeSet Dst; NodeBuilder Bldr(Tmp, Dst, *currBldrCtx); for (ExplodedNodeSet::iterator I = Tmp.begin(), E = Tmp.end(); I != E; ++I) { @@ -429,16 +510,16 @@ void ExprEngine::ProcessImplicitDtor(const CFGImplicitDtor D, ExplodedNodeSet Dst; switch (D.getKind()) { case CFGElement::AutomaticObjectDtor: - ProcessAutomaticObjDtor(cast<CFGAutomaticObjDtor>(D), Pred, Dst); + ProcessAutomaticObjDtor(D.castAs<CFGAutomaticObjDtor>(), Pred, Dst); break; case CFGElement::BaseDtor: - ProcessBaseDtor(cast<CFGBaseDtor>(D), Pred, Dst); + ProcessBaseDtor(D.castAs<CFGBaseDtor>(), Pred, Dst); break; case CFGElement::MemberDtor: - ProcessMemberDtor(cast<CFGMemberDtor>(D), Pred, Dst); + ProcessMemberDtor(D.castAs<CFGMemberDtor>(), Pred, Dst); break; case CFGElement::TemporaryDtor: - ProcessTemporaryDtor(cast<CFGTemporaryDtor>(D), Pred, Dst); + ProcessTemporaryDtor(D.castAs<CFGTemporaryDtor>(), Pred, Dst); break; default: llvm_unreachable("Unexpected dtor kind."); @@ -451,18 +532,20 @@ void ExprEngine::ProcessImplicitDtor(const CFGImplicitDtor D, void ExprEngine::ProcessAutomaticObjDtor(const CFGAutomaticObjDtor Dtor, ExplodedNode *Pred, ExplodedNodeSet &Dst) { - ProgramStateRef state = Pred->getState(); const VarDecl *varDecl = Dtor.getVarDecl(); - QualType varType = varDecl->getType(); - if (const ReferenceType *refType = varType->getAs<ReferenceType>()) - varType = refType->getPointeeType(); + ProgramStateRef state = Pred->getState(); + SVal dest = state->getLValue(varDecl, Pred->getLocationContext()); + const MemRegion *Region = dest.castAs<loc::MemRegionVal>().getRegion(); - Loc dest = state->getLValue(varDecl, Pred->getLocationContext()); + if (const ReferenceType *refType = varType->getAs<ReferenceType>()) { + varType = refType->getPointeeType(); + Region = state->getSVal(Region).getAsRegion(); + } - VisitCXXDestructor(varType, cast<loc::MemRegionVal>(dest).getRegion(), - Dtor.getTriggerStmt(), /*IsBase=*/false, Pred, Dst); + VisitCXXDestructor(varType, Region, Dtor.getTriggerStmt(), /*IsBase=*/ false, + Pred, Dst); } void ExprEngine::ProcessBaseDtor(const CFGBaseDtor D, @@ -476,11 +559,13 @@ void ExprEngine::ProcessBaseDtor(const CFGBaseDtor D, SVal ThisVal = Pred->getState()->getSVal(ThisPtr); // Create the base object region. - QualType BaseTy = D.getBaseSpecifier()->getType(); - SVal BaseVal = getStoreManager().evalDerivedToBase(ThisVal, BaseTy); + const CXXBaseSpecifier *Base = D.getBaseSpecifier(); + QualType BaseTy = Base->getType(); + SVal BaseVal = getStoreManager().evalDerivedToBase(ThisVal, BaseTy, + Base->isVirtual()); - VisitCXXDestructor(BaseTy, cast<loc::MemRegionVal>(BaseVal).getRegion(), - CurDtor->getBody(), /*IsBase=*/true, Pred, Dst); + VisitCXXDestructor(BaseTy, BaseVal.castAs<loc::MemRegionVal>().getRegion(), + CurDtor->getBody(), /*IsBase=*/ true, Pred, Dst); } void ExprEngine::ProcessMemberDtor(const CFGMemberDtor D, @@ -492,10 +577,11 @@ void ExprEngine::ProcessMemberDtor(const CFGMemberDtor D, const CXXDestructorDecl *CurDtor = cast<CXXDestructorDecl>(LCtx->getDecl()); Loc ThisVal = getSValBuilder().getCXXThis(CurDtor, LCtx->getCurrentStackFrame()); - SVal FieldVal = State->getLValue(Member, cast<Loc>(State->getSVal(ThisVal))); + SVal FieldVal = + State->getLValue(Member, State->getSVal(ThisVal).castAs<Loc>()); VisitCXXDestructor(Member->getType(), - cast<loc::MemRegionVal>(FieldVal).getRegion(), + FieldVal.castAs<loc::MemRegionVal>().getRegion(), CurDtor->getBody(), /*IsBase=*/false, Pred, Dst); } @@ -511,16 +597,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, ExplodedNodeSet Dst; StmtNodeBuilder Bldr(Pred, DstTop, *currBldrCtx); - // Expressions to ignore. - if (const Expr *Ex = dyn_cast<Expr>(S)) - S = Ex->IgnoreParens(); - - // FIXME: add metadata to the CFG so that we can disable - // this check when we KNOW that there is no block-level subexpression. - // The motivation is that this check requires a hashtable lookup. - - if (S != currStmt && Pred->getLocationContext()->getCFG()->isBlkExpr(S)) - return; + assert(!isa<Expr>(S) || S == cast<Expr>(S)->IgnoreParens()); switch (S->getStmtClass()) { // C++ and ARC stuff we don't support yet. @@ -637,7 +714,6 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, case Stmt::StringLiteralClass: case Stmt::ObjCStringLiteralClass: case Stmt::CXXBindTemporaryExprClass: - case Stmt::CXXDefaultArgExprClass: case Stmt::SubstNonTypeTemplateParmExprClass: case Stmt::CXXNullPtrLiteralExprClass: { Bldr.takeNodes(Pred); @@ -648,6 +724,43 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, break; } + case Stmt::CXXDefaultArgExprClass: { + Bldr.takeNodes(Pred); + ExplodedNodeSet PreVisit; + getCheckerManager().runCheckersForPreStmt(PreVisit, Pred, S, *this); + + ExplodedNodeSet Tmp; + StmtNodeBuilder Bldr2(PreVisit, Tmp, *currBldrCtx); + + const LocationContext *LCtx = Pred->getLocationContext(); + const CXXDefaultArgExpr *DefaultE = cast<CXXDefaultArgExpr>(S); + const Expr *ArgE = DefaultE->getExpr(); + + // Avoid creating and destroying a lot of APSInts. + SVal V; + llvm::APSInt Result; + + for (ExplodedNodeSet::iterator I = PreVisit.begin(), E = PreVisit.end(); + I != E; ++I) { + ProgramStateRef State = (*I)->getState(); + + if (ArgE->EvaluateAsInt(Result, getContext())) + V = svalBuilder.makeIntVal(Result); + else + V = State->getSVal(ArgE, LCtx); + + State = State->BindExpr(DefaultE, LCtx, V); + if (DefaultE->isGLValue()) + State = createTemporaryRegionIfNeeded(State, LCtx, DefaultE, + DefaultE); + Bldr2.generateNode(S, *I, State); + } + + getCheckerManager().runCheckersForPostStmt(Dst, Tmp, S, *this); + Bldr.addNodes(Dst); + break; + } + case Expr::ObjCArrayLiteralClass: case Expr::ObjCDictionaryLiteralClass: // FIXME: explicitly model with a region and the actual contents @@ -780,16 +893,23 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, case Stmt::CXXNewExprClass: { Bldr.takeNodes(Pred); - const CXXNewExpr *NE = cast<CXXNewExpr>(S); - VisitCXXNewExpr(NE, Pred, Dst); + ExplodedNodeSet PostVisit; + VisitCXXNewExpr(cast<CXXNewExpr>(S), Pred, PostVisit); + getCheckerManager().runCheckersForPostStmt(Dst, PostVisit, S, *this); Bldr.addNodes(Dst); break; } case Stmt::CXXDeleteExprClass: { Bldr.takeNodes(Pred); + ExplodedNodeSet PreVisit; const CXXDeleteExpr *CDE = cast<CXXDeleteExpr>(S); - VisitCXXDeleteExpr(CDE, Pred, Dst); + getCheckerManager().runCheckersForPreStmt(PreVisit, Pred, S, *this); + + for (ExplodedNodeSet::iterator i = PreVisit.begin(), + e = PreVisit.end(); i != e ; ++i) + VisitCXXDeleteExpr(CDE, *i, Dst); + Bldr.addNodes(Dst); break; } @@ -1012,11 +1132,11 @@ bool ExprEngine::replayWithoutInlining(ExplodedNode *N, // processing the call. if (L.isPurgeKind()) continue; - if (isa<PreImplicitCall>(&L)) + if (L.getAs<PreImplicitCall>()) continue; - if (isa<CallEnter>(&L)) + if (L.getAs<CallEnter>()) continue; - if (const StmtPoint *SP = dyn_cast<StmtPoint>(&L)) + if (Optional<StmtPoint> SP = L.getAs<StmtPoint>()) if (SP->getStmt() == CE) continue; break; @@ -1034,7 +1154,8 @@ bool ExprEngine::replayWithoutInlining(ExplodedNode *N, // 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); + NewNodeState = + NewNodeState->set<ReplayWithoutInlining>(const_cast<Stmt *>(CE)); // Make the new node a successor of BeforeProcessingCall. bool IsNew = false; @@ -1155,7 +1276,7 @@ static const Stmt *ResolveCondition(const Stmt *Condition, CFGBlock::const_reverse_iterator I = B->rbegin(), E = B->rend(); for (; I != E; ++I) { CFGElement Elem = *I; - CFGStmt *CS = dyn_cast<CFGStmt>(&Elem); + Optional<CFGStmt> CS = Elem.getAs<CFGStmt>(); if (!CS) continue; if (CS->getStmt() != Condition) @@ -1215,8 +1336,8 @@ void ExprEngine::processBranch(const Stmt *Condition, const Stmt *Term, if (PredI->isSink()) continue; - ProgramStateRef PrevState = Pred->getState(); - SVal X = PrevState->getSVal(Condition, Pred->getLocationContext()); + ProgramStateRef PrevState = PredI->getState(); + SVal X = PrevState->getSVal(Condition, PredI->getLocationContext()); if (X.isUnknownOrUndef()) { // Give it a chance to recover from unknown. @@ -1228,7 +1349,7 @@ void ExprEngine::processBranch(const Stmt *Condition, const Stmt *Term, // underlying value and use that instead. SVal recovered = RecoverCastedSymbol(getStateManager(), PrevState, Condition, - Pred->getLocationContext(), + PredI->getLocationContext(), getContext()); if (!recovered.isUnknown()) { @@ -1245,20 +1366,23 @@ void ExprEngine::processBranch(const Stmt *Condition, const Stmt *Term, continue; } - DefinedSVal V = cast<DefinedSVal>(X); + DefinedSVal V = X.castAs<DefinedSVal>(); + + ProgramStateRef StTrue, StFalse; + tie(StTrue, StFalse) = PrevState->assume(V); // Process the true branch. if (builder.isFeasible(true)) { - if (ProgramStateRef state = PrevState->assume(V, true)) - builder.generateNode(state, true, PredI); + if (StTrue) + builder.generateNode(StTrue, true, PredI); else builder.markInfeasible(true); } // Process the false branch. if (builder.isFeasible(false)) { - if (ProgramStateRef state = PrevState->assume(V, false)) - builder.generateNode(state, false, PredI); + if (StFalse) + builder.generateNode(StFalse, false, PredI); else builder.markInfeasible(false); } @@ -1266,6 +1390,34 @@ void ExprEngine::processBranch(const Stmt *Condition, const Stmt *Term, currBldrCtx = 0; } +/// The GDM component containing the set of global variables which have been +/// previously initialized with explicit initializers. +REGISTER_TRAIT_WITH_PROGRAMSTATE(InitializedGlobalsSet, + llvm::ImmutableSet<const VarDecl *>) + +void ExprEngine::processStaticInitializer(const DeclStmt *DS, + NodeBuilderContext &BuilderCtx, + ExplodedNode *Pred, + clang::ento::ExplodedNodeSet &Dst, + const CFGBlock *DstT, + const CFGBlock *DstF) { + currBldrCtx = &BuilderCtx; + + const VarDecl *VD = cast<VarDecl>(DS->getSingleDecl()); + ProgramStateRef state = Pred->getState(); + bool initHasRun = state->contains<InitializedGlobalsSet>(VD); + BranchNodeBuilder builder(Pred, Dst, BuilderCtx, DstT, DstF); + + if (!initHasRun) { + state = state->add<InitializedGlobalsSet>(VD); + } + + builder.generateNode(state, initHasRun, Pred); + builder.markInfeasible(!initHasRun); + + currBldrCtx = 0; +} + /// processIndirectGoto - Called by CoreEngine. Used to generate successor /// nodes by processing the 'effects' of a computed goto jump. void ExprEngine::processIndirectGoto(IndirectGotoNodeBuilder &builder) { @@ -1282,8 +1434,8 @@ void ExprEngine::processIndirectGoto(IndirectGotoNodeBuilder &builder) { typedef IndirectGotoNodeBuilder::iterator iterator; - if (isa<loc::GotoLabel>(V)) { - const LabelDecl *L = cast<loc::GotoLabel>(V).getLabel(); + if (Optional<loc::GotoLabel> LV = V.getAs<loc::GotoLabel>()) { + const LabelDecl *L = LV->getLabel(); for (iterator I = builder.begin(), E = builder.end(); I != E; ++I) { if (I.getLabel() == L) { @@ -1295,7 +1447,7 @@ void ExprEngine::processIndirectGoto(IndirectGotoNodeBuilder &builder) { llvm_unreachable("No block with label."); } - if (isa<loc::ConcreteInt>(V) || isa<UndefinedVal>(V)) { + if (V.getAs<loc::ConcreteInt>() || V.getAs<UndefinedVal>()) { // Dispatch to the first target and mark it as a sink. //ExplodedNode* N = builder.generateNode(builder.begin(), state, true); // FIXME: add checker visit. @@ -1325,10 +1477,10 @@ void ExprEngine::processEndOfFunction(NodeBuilderContext& BC, // Notify checkers. for (ExplodedNodeSet::iterator I = AfterRemovedDead.begin(), E = AfterRemovedDead.end(); I != E; ++I) { - getCheckerManager().runCheckersForEndPath(BC, Dst, *I, *this); + getCheckerManager().runCheckersForEndFunction(BC, Dst, *I, *this); } } else { - getCheckerManager().runCheckersForEndPath(BC, Dst, Pred, *this); + getCheckerManager().runCheckersForEndFunction(BC, Dst, Pred, *this); } Engine.enqueueEndOfFunction(Dst); @@ -1349,7 +1501,7 @@ void ExprEngine::processSwitch(SwitchNodeBuilder& builder) { return; } - DefinedOrUnknownSVal CondV = cast<DefinedOrUnknownSVal>(CondV_untested); + DefinedOrUnknownSVal CondV = CondV_untested.castAs<DefinedOrUnknownSVal>(); ProgramStateRef DefaultSt = state; @@ -1390,7 +1542,7 @@ void ExprEngine::processSwitch(SwitchNodeBuilder& builder) { // If CondV evaluates to a constant, then we know that this // is the *only* case that we can take, so stop evaluating the // others. - if (isa<nonloc::ConcreteInt>(CondV)) + if (CondV.getAs<nonloc::ConcreteInt>()) return; } @@ -1484,7 +1636,7 @@ void ExprEngine::VisitCommonDeclRefExpr(const Expr *Ex, const NamedDecl *D, // results in boolean contexts. SVal V = svalBuilder.conjureSymbolVal(Ex, LCtx, getContext().VoidPtrTy, currBldrCtx->blockCount()); - state = state->assume(cast<DefinedOrUnknownSVal>(V), true); + state = state->assume(V.castAs<DefinedOrUnknownSVal>(), true); Bldr.generateNode(Ex, Pred, state->BindExpr(Ex, LCtx, V), 0, ProgramPoint::PostLValueKind); return; @@ -1576,6 +1728,122 @@ void ExprEngine::VisitMemberExpr(const MemberExpr *M, ExplodedNode *Pred, } } +namespace { +class CollectReachableSymbolsCallback : public SymbolVisitor { + InvalidatedSymbols Symbols; +public: + CollectReachableSymbolsCallback(ProgramStateRef State) {} + const InvalidatedSymbols &getSymbols() const { return Symbols; } + + bool VisitSymbol(SymbolRef Sym) { + Symbols.insert(Sym); + return true; + } +}; +} // end anonymous namespace + +// 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 MemrRegion that does not have stack storage. +// (3) We are binding to a MemRegion with stack storage that the store +// does not understand. +ProgramStateRef ExprEngine::processPointerEscapedOnBind(ProgramStateRef State, + SVal Loc, SVal Val) { + // Are we storing to something that causes the value to "escape"? + bool escapes = true; + + // TODO: Move to StoreManager. + if (Optional<loc::MemRegionVal> regionLoc = Loc.getAs<loc::MemRegionVal>()) { + 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). + // Do this only if we know that the store is not supposed to generate the + // same state. + SVal StoredVal = State->getSVal(regionLoc->getRegion()); + if (StoredVal != Val) + escapes = (State == (State->bindLoc(*regionLoc, Val))); + } + } + + // If our store can represent the binding and we aren't storing to something + // that doesn't have local storage then just return and have the simulation + // state continue as is. + if (!escapes) + return State; + + // Otherwise, find all symbols referenced by 'val' that we are tracking + // and stop tracking them. + CollectReachableSymbolsCallback Scanner = + State->scanReachableSymbols<CollectReachableSymbolsCallback>(Val); + const InvalidatedSymbols &EscapedSymbols = Scanner.getSymbols(); + State = getCheckerManager().runCheckersForPointerEscape(State, + EscapedSymbols, + /*CallEvent*/ 0, + PSK_EscapeOnBind); + + return State; +} + +ProgramStateRef +ExprEngine::notifyCheckersOfPointerEscape(ProgramStateRef State, + const InvalidatedSymbols *Invalidated, + ArrayRef<const MemRegion *> ExplicitRegions, + ArrayRef<const MemRegion *> Regions, + const CallEvent *Call, + bool IsConst) { + + if (!Invalidated || Invalidated->empty()) + return State; + + if (!Call) + return getCheckerManager().runCheckersForPointerEscape(State, + *Invalidated, + 0, + PSK_EscapeOther); + + // Note: Due to current limitations of RegionStore, we only process the top + // level const pointers correctly. The lower level const pointers are + // currently treated as non-const. + if (IsConst) + return getCheckerManager().runCheckersForPointerEscape(State, + *Invalidated, + Call, + PSK_DirectEscapeOnCall, + true); + + // If the symbols were invalidated by a call, we want to find out which ones + // were invalidated directly due to being arguments to the call. + InvalidatedSymbols SymbolsDirectlyInvalidated; + for (ArrayRef<const MemRegion *>::iterator I = ExplicitRegions.begin(), + E = ExplicitRegions.end(); I != E; ++I) { + if (const SymbolicRegion *R = (*I)->StripCasts()->getAs<SymbolicRegion>()) + SymbolsDirectlyInvalidated.insert(R->getSymbol()); + } + + InvalidatedSymbols SymbolsIndirectlyInvalidated; + for (InvalidatedSymbols::const_iterator I=Invalidated->begin(), + E = Invalidated->end(); I!=E; ++I) { + SymbolRef sym = *I; + if (SymbolsDirectlyInvalidated.count(sym)) + continue; + SymbolsIndirectlyInvalidated.insert(sym); + } + + if (!SymbolsDirectlyInvalidated.empty()) + State = getCheckerManager().runCheckersForPointerEscape(State, + SymbolsDirectlyInvalidated, Call, PSK_DirectEscapeOnCall); + + // Notify about the symbols that get indirectly invalidated by the call. + if (!SymbolsIndirectlyInvalidated.empty()) + State = getCheckerManager().runCheckersForPointerEscape(State, + SymbolsIndirectlyInvalidated, Call, PSK_IndirectEscapeOnCall); + + return State; +} + /// evalBind - Handle the semantics of binding a value to a specific location. /// This method is used by evalStore and (soon) VisitDeclStmt, and others. void ExprEngine::evalBind(ExplodedNodeSet &Dst, const Stmt *StoreE, @@ -1593,36 +1861,42 @@ void ExprEngine::evalBind(ExplodedNodeSet &Dst, const Stmt *StoreE, getCheckerManager().runCheckersForBind(CheckedSet, Pred, location, Val, StoreE, *this, *PP); + + StmtNodeBuilder Bldr(CheckedSet, Dst, *currBldrCtx); + // If the location is not a 'Loc', it will already be handled by // the checkers. There is nothing left to do. - if (!isa<Loc>(location)) { - Dst = CheckedSet; + if (!location.getAs<Loc>()) { + const ProgramPoint L = PostStore(StoreE, LC, /*Loc*/0, /*tag*/0); + ProgramStateRef state = Pred->getState(); + state = processPointerEscapedOnBind(state, location, Val); + Bldr.generateNode(L, state, Pred); return; } - ExplodedNodeSet TmpDst; - StmtNodeBuilder Bldr(CheckedSet, TmpDst, *currBldrCtx); for (ExplodedNodeSet::iterator I = CheckedSet.begin(), E = CheckedSet.end(); I!=E; ++I) { ExplodedNode *PredI = *I; ProgramStateRef state = PredI->getState(); + state = processPointerEscapedOnBind(state, location, Val); + // When binding the value, pass on the hint that this is a initialization. // For initializations, we do not need to inform clients of region // changes. - state = state->bindLoc(cast<Loc>(location), + state = state->bindLoc(location.castAs<Loc>(), Val, /* notifyChanges = */ !atDeclInit); - + const MemRegion *LocReg = 0; - if (loc::MemRegionVal *LocRegVal = dyn_cast<loc::MemRegionVal>(&location)) { + if (Optional<loc::MemRegionVal> LocRegVal = + location.getAs<loc::MemRegionVal>()) { LocReg = LocRegVal->getRegion(); } const ProgramPoint L = PostStore(StoreE, LC, LocReg, 0); Bldr.generateNode(L, state, PredI); } - Dst.insert(TmpDst); } /// evalStore - Handle the semantics of a store via an assignment. @@ -1665,7 +1939,7 @@ void ExprEngine::evalLoad(ExplodedNodeSet &Dst, const ProgramPointTag *tag, QualType LoadTy) { - assert(!isa<NonLoc>(location) && "location cannot be a NonLoc."); + assert(!location.getAs<NonLoc>() && "location cannot be a NonLoc."); // 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 @@ -1720,20 +1994,15 @@ void ExprEngine::evalLoadCommon(ExplodedNodeSet &Dst, state = (*NI)->getState(); const LocationContext *LCtx = (*NI)->getLocationContext(); - if (location.isUnknown()) { - // This is important. We must nuke the old binding. - Bldr.generateNode(NodeEx, *NI, - state->BindExpr(BoundEx, LCtx, UnknownVal()), - tag, ProgramPoint::PostLoadKind); - } - else { + SVal V = UnknownVal(); + if (location.isValid()) { if (LoadTy.isNull()) LoadTy = BoundEx->getType(); - SVal V = state->getSVal(cast<Loc>(location), LoadTy); - Bldr.generateNode(NodeEx, *NI, - state->bindExprAndLocation(BoundEx, LCtx, location, V), - tag, ProgramPoint::PostLoadKind); + V = state->getSVal(location.castAs<Loc>(), LoadTy); } + + Bldr.generateNode(NodeEx, *NI, state->BindExpr(BoundEx, LCtx, V), tag, + ProgramPoint::PostLoadKind); } } @@ -1793,26 +2062,29 @@ void ExprEngine::evalEagerlyAssumeBinOpBifurcation(ExplodedNodeSet &Dst, // 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) { + if (!P.getAs<PostStmt>() || P.castAs<PostStmt>().getStmt() != Ex) { continue; } ProgramStateRef state = Pred->getState(); SVal V = state->getSVal(Ex, Pred->getLocationContext()); - nonloc::SymbolVal *SEV = dyn_cast<nonloc::SymbolVal>(&V); + Optional<nonloc::SymbolVal> SEV = V.getAs<nonloc::SymbolVal>(); if (SEV && SEV->isExpression()) { const std::pair<const ProgramPointTag *, const ProgramPointTag*> &tags = geteagerlyAssumeBinOpBifurcationTags(); + ProgramStateRef StateTrue, StateFalse; + tie(StateTrue, StateFalse) = state->assume(*SEV); + // First assume that the condition is true. - if (ProgramStateRef StateTrue = state->assume(*SEV, true)) { + if (StateTrue) { SVal Val = svalBuilder.makeIntVal(1U, Ex->getType()); StateTrue = StateTrue->BindExpr(Ex, Pred->getLocationContext(), Val); Bldr.generateNode(Ex, Pred, StateTrue, tags.first); } // Next, assume that the condition is false. - if (ProgramStateRef StateFalse = state->assume(*SEV, false)) { + if (StateFalse) { SVal Val = svalBuilder.makeIntVal(0U, Ex->getType()); StateFalse = StateFalse->BindExpr(Ex, Pred->getLocationContext(), Val); Bldr.generateNode(Ex, Pred, StateFalse, tags.second); @@ -1836,10 +2108,10 @@ void ExprEngine::VisitGCCAsmStmt(const GCCAsmStmt *A, ExplodedNode *Pred, for (GCCAsmStmt::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. + assert (!X.getAs<NonLoc>()); // Should be an Lval, or unknown, undef. - if (isa<Loc>(X)) - state = state->bindLoc(cast<Loc>(X), UnknownVal()); + if (Optional<Loc> LV = X.getAs<Loc>()) + state = state->bindLoc(*LV, UnknownVal()); } Bldr.generateNode(A, Pred, state); @@ -1889,7 +2161,7 @@ struct DOTGraphTraits<ExplodedNode*> : return ""; } - static void printLocation(llvm::raw_ostream &Out, SourceLocation SLoc) { + static void printLocation(raw_ostream &Out, SourceLocation SLoc) { if (SLoc.isFileID()) { Out << "\\lline=" << GraphPrintSourceManager->getExpansionLineNumber(SLoc) @@ -1910,7 +2182,7 @@ struct DOTGraphTraits<ExplodedNode*> : switch (Loc.getKind()) { case ProgramPoint::BlockEntranceKind: { Out << "Block Entrance: B" - << cast<BlockEntrance>(Loc).getBlock()->getBlockID(); + << Loc.castAs<BlockEntrance>().getBlock()->getBlockID(); if (const NamedDecl *ND = dyn_cast<NamedDecl>(Loc.getLocationContext()->getDecl())) { Out << " ("; @@ -1949,73 +2221,46 @@ struct DOTGraphTraits<ExplodedNode*> : break; case ProgramPoint::PreImplicitCallKind: { - ImplicitCallPoint *PC = cast<ImplicitCallPoint>(&Loc); + ImplicitCallPoint PC = Loc.castAs<ImplicitCallPoint>(); Out << "PreCall: "; // FIXME: Get proper printing options. - PC->getDecl()->print(Out, LangOptions()); - printLocation(Out, PC->getLocation()); + PC.getDecl()->print(Out, LangOptions()); + printLocation(Out, PC.getLocation()); break; } case ProgramPoint::PostImplicitCallKind: { - ImplicitCallPoint *PC = cast<ImplicitCallPoint>(&Loc); + ImplicitCallPoint PC = Loc.castAs<ImplicitCallPoint>(); Out << "PostCall: "; // FIXME: Get proper printing options. - PC->getDecl()->print(Out, LangOptions()); - printLocation(Out, PC->getLocation()); + PC.getDecl()->print(Out, LangOptions()); + printLocation(Out, PC.getLocation()); break; } - default: { - if (StmtPoint *L = dyn_cast<StmtPoint>(&Loc)) { - const Stmt *S = L->getStmt(); - - Out << S->getStmtClassName() << ' ' << (const void*) S << ' '; + case ProgramPoint::PostInitializerKind: { + Out << "PostInitializer: "; + const CXXCtorInitializer *Init = + Loc.castAs<PostInitializer>().getInitializer(); + if (const FieldDecl *FD = Init->getAnyMember()) + Out << *FD; + else { + QualType Ty = Init->getTypeSourceInfo()->getType(); + Ty = Ty.getLocalUnqualifiedType(); LangOptions LO; // FIXME. - S->printPretty(Out, 0, PrintingPolicy(LO)); - printLocation(Out, S->getLocStart()); - - if (isa<PreStmt>(Loc)) - Out << "\\lPreStmt\\l;"; - else if (isa<PostLoad>(Loc)) - Out << "\\lPostLoad\\l;"; - else if (isa<PostStore>(Loc)) - Out << "\\lPostStore\\l"; - else if (isa<PostLValue>(Loc)) - Out << "\\lPostLValue\\l"; - -#if 0 - // FIXME: Replace with a general scheme to determine - // the name of the check. - if (GraphPrintCheckerState->isImplicitNullDeref(N)) - Out << "\\|Implicit-Null Dereference.\\l"; - else if (GraphPrintCheckerState->isExplicitNullDeref(N)) - Out << "\\|Explicit-Null Dereference.\\l"; - else if (GraphPrintCheckerState->isUndefDeref(N)) - Out << "\\|Dereference of undefialied value.\\l"; - else if (GraphPrintCheckerState->isUndefStore(N)) - Out << "\\|Store to Undefined Loc."; - else if (GraphPrintCheckerState->isUndefResult(N)) - Out << "\\|Result of operation is undefined."; - else if (GraphPrintCheckerState->isNoReturnCall(N)) - Out << "\\|Call to function marked \"noreturn\"."; - else if (GraphPrintCheckerState->isBadCall(N)) - Out << "\\|Call to NULL/Undefined."; - else if (GraphPrintCheckerState->isUndefArg(N)) - Out << "\\|Argument in call is undefined"; -#endif - - break; + Ty.print(Out, LO); } + break; + } - const BlockEdge &E = cast<BlockEdge>(Loc); + case ProgramPoint::BlockEdgeKind: { + const BlockEdge &E = Loc.castAs<BlockEdge>(); Out << "Edge: (B" << E.getSrc()->getBlockID() << ", B" << E.getDst()->getBlockID() << ')'; if (const Stmt *T = E.getSrc()->getTerminator()) { - SourceLocation SLoc = T->getLocStart(); Out << "\\|Terminator: "; @@ -2074,6 +2319,48 @@ struct DOTGraphTraits<ExplodedNode*> : Out << "\\|Control-flow based on\\lUndefined value.\\l"; } #endif + break; + } + + default: { + const Stmt *S = Loc.castAs<StmtPoint>().getStmt(); + + Out << S->getStmtClassName() << ' ' << (const void*) S << ' '; + LangOptions LO; // FIXME. + S->printPretty(Out, 0, PrintingPolicy(LO)); + printLocation(Out, S->getLocStart()); + + if (Loc.getAs<PreStmt>()) + Out << "\\lPreStmt\\l;"; + else if (Loc.getAs<PostLoad>()) + Out << "\\lPostLoad\\l;"; + else if (Loc.getAs<PostStore>()) + Out << "\\lPostStore\\l"; + else if (Loc.getAs<PostLValue>()) + Out << "\\lPostLValue\\l"; + +#if 0 + // FIXME: Replace with a general scheme to determine + // the name of the check. + if (GraphPrintCheckerState->isImplicitNullDeref(N)) + Out << "\\|Implicit-Null Dereference.\\l"; + else if (GraphPrintCheckerState->isExplicitNullDeref(N)) + Out << "\\|Explicit-Null Dereference.\\l"; + else if (GraphPrintCheckerState->isUndefDeref(N)) + Out << "\\|Dereference of undefialied value.\\l"; + else if (GraphPrintCheckerState->isUndefStore(N)) + Out << "\\|Store to Undefined Loc."; + else if (GraphPrintCheckerState->isUndefResult(N)) + Out << "\\|Result of operation is undefined."; + else if (GraphPrintCheckerState->isNoReturnCall(N)) + Out << "\\|Call to function marked \"noreturn\"."; + else if (GraphPrintCheckerState->isBadCall(N)) + Out << "\\|Call to NULL/Undefined."; + else if (GraphPrintCheckerState->isUndefArg(N)) + Out << "\\|Argument in call is undefined"; +#endif + + break; } } @@ -2108,7 +2395,7 @@ GetGraphNode<llvm::DenseMap<ExplodedNode*, Expr*>::iterator> void ExprEngine::ViewGraph(bool trim) { #ifndef NDEBUG if (trim) { - std::vector<ExplodedNode*> Src; + std::vector<const ExplodedNode*> Src; // Flush any outstanding reports to make sure we cover all the nodes. // This does not cause them to get displayed. @@ -2122,7 +2409,7 @@ void ExprEngine::ViewGraph(bool trim) { if (N) Src.push_back(N); } - ViewGraph(&Src[0], &Src[0]+Src.size()); + ViewGraph(Src); } else { GraphPrintCheckerState = this; @@ -2136,12 +2423,12 @@ void ExprEngine::ViewGraph(bool trim) { #endif } -void ExprEngine::ViewGraph(ExplodedNode** Beg, ExplodedNode** End) { +void ExprEngine::ViewGraph(ArrayRef<const ExplodedNode*> Nodes) { #ifndef NDEBUG GraphPrintCheckerState = this; GraphPrintSourceManager = &getContext().getSourceManager(); - std::auto_ptr<ExplodedGraph> TrimmedG(G.Trim(Beg, End).first); + OwningPtr<ExplodedGraph> TrimmedG(G.trim(Nodes)); if (!TrimmedG.get()) llvm::errs() << "warning: Trimmed ExplodedGraph is empty.\n"; diff --git a/lib/StaticAnalyzer/Core/ExprEngineC.cpp b/lib/StaticAnalyzer/Core/ExprEngineC.cpp index 00b2f4a6bee9..3a3c9713dd20 100644 --- a/lib/StaticAnalyzer/Core/ExprEngineC.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngineC.cpp @@ -11,6 +11,7 @@ // //===----------------------------------------------------------------------===// +#include "clang/AST/ExprCXX.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" @@ -66,12 +67,12 @@ void ExprEngine::VisitBinaryOperator(const BinaryOperator* B, // TODO: This can be removed after we enable history tracking with // SymSymExpr. unsigned Count = currBldrCtx->blockCount(); - if (isa<Loc>(LeftV) && + if (LeftV.getAs<Loc>() && RHS->getType()->isIntegerType() && RightV.isUnknown()) { RightV = svalBuilder.conjureSymbolVal(RHS, LCtx, RHS->getType(), Count); } - if (isa<Loc>(RightV) && + if (RightV.getAs<Loc>() && LHS->getType()->isIntegerType() && LeftV.isUnknown()) { LeftV = svalBuilder.conjureSymbolVal(LHS, LCtx, LHS->getType(), Count); @@ -305,7 +306,8 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex, case CK_CPointerToObjCPointerCast: case CK_BlockPointerToObjCPointerCast: case CK_AnyPointerToBlockPointerCast: - case CK_ObjCObjectLValueCast: { + case CK_ObjCObjectLValueCast: + case CK_ZeroToOCLEvent: { // Delegate to SValBuilder to process. SVal V = state->getSVal(Ex, LCtx); V = svalBuilder.evalCast(V, T, ExTy); @@ -423,15 +425,10 @@ void ExprEngine::VisitCompoundLiteralExpr(const CompoundLiteralExpr *CL, void ExprEngine::VisitDeclStmt(const DeclStmt *DS, ExplodedNode *Pred, ExplodedNodeSet &Dst) { - - // FIXME: static variables may have an initializer, but the second - // time a function is called those values may not be current. - // This may need to be reflected in the CFG. - // Assumption: The CFG has one DeclStmt per Decl. - const Decl *D = *DS->decl_begin(); - - if (!D || !isa<VarDecl>(D)) { + const VarDecl *VD = dyn_cast_or_null<VarDecl>(*DS->decl_begin()); + + if (!VD) { //TODO:AZ: remove explicit insertion after refactoring is done. Dst.insert(Pred); return; @@ -442,31 +439,33 @@ void ExprEngine::VisitDeclStmt(const DeclStmt *DS, ExplodedNode *Pred, getCheckerManager().runCheckersForPreStmt(dstPreVisit, Pred, DS, *this); StmtNodeBuilder B(dstPreVisit, Dst, *currBldrCtx); - const VarDecl *VD = dyn_cast<VarDecl>(D); for (ExplodedNodeSet::iterator I = dstPreVisit.begin(), E = dstPreVisit.end(); I!=E; ++I) { ExplodedNode *N = *I; ProgramStateRef state = N->getState(); - - // Decls without InitExpr are not initialized explicitly. const LocationContext *LC = N->getLocationContext(); - + + // Decls without InitExpr are not initialized explicitly. if (const Expr *InitEx = VD->getInit()) { + + // Note in the state that the initialization has occurred. + ExplodedNode *UpdatedN = N; SVal InitVal = state->getSVal(InitEx, LC); - if (InitVal == state->getLValue(VD, LC) || - (VD->getType()->isArrayType() && - isa<CXXConstructExpr>(InitEx->IgnoreImplicit()))) { + if (isa<CXXConstructExpr>(InitEx->IgnoreImplicit())) { // We constructed the object directly in the variable. // No need to bind anything. - B.generateNode(DS, N, state); + B.generateNode(DS, UpdatedN, state); } else { // We bound the temp obj region to the CXXConstructExpr. Now recover // the lazy compound value when the variable is not a reference. - 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)); + if (AMgr.getLangOpts().CPlusPlus && VD->getType()->isRecordType() && + !VD->getType()->isReferenceType()) { + if (Optional<loc::MemRegionVal> M = + InitVal.getAs<loc::MemRegionVal>()) { + InitVal = state->getSVal(M->getRegion()); + assert(InitVal.getAs<nonloc::LazyCompoundVal>()); + } } // Recover some path-sensitivity if a scalar value evaluated to @@ -480,9 +479,11 @@ void ExprEngine::VisitDeclStmt(const DeclStmt *DS, ExplodedNode *Pred, InitVal = svalBuilder.conjureSymbolVal(0, InitEx, LC, Ty, currBldrCtx->blockCount()); } - B.takeNodes(N); + + + B.takeNodes(UpdatedN); ExplodedNodeSet Dst2; - evalBind(Dst2, DS, N, state->getLValue(VD, LC), InitVal, true); + evalBind(Dst2, DS, UpdatedN, state->getLValue(VD, LC), InitVal, true); B.addNodes(Dst2); } } @@ -501,16 +502,16 @@ void ExprEngine::VisitLogicalExpr(const BinaryOperator* B, ExplodedNode *Pred, ProgramStateRef state = Pred->getState(); ExplodedNode *N = Pred; - while (!isa<BlockEntrance>(N->getLocation())) { + while (!N->getLocation().getAs<BlockEntrance>()) { ProgramPoint P = N->getLocation(); - assert(isa<PreStmt>(P)|| isa<PreStmtPurgeDeadSymbols>(P)); + assert(P.getAs<PreStmt>()|| P.getAs<PreStmtPurgeDeadSymbols>()); (void) P; assert(N->pred_size() == 1); N = *N->pred_begin(); } assert(N->pred_size() == 1); N = *N->pred_begin(); - BlockEdge BE = cast<BlockEdge>(N->getLocation()); + BlockEdge BE = N->getLocation().castAs<BlockEdge>(); SVal X; // Determine the value of the expression by introspecting how we @@ -532,28 +533,32 @@ void ExprEngine::VisitLogicalExpr(const BinaryOperator* B, ExplodedNode *Pred, // in SrcBlock is the value of the enclosing expression. // However, we still need to constrain that value to be 0 or 1. assert(!SrcBlock->empty()); - CFGStmt Elem = cast<CFGStmt>(*SrcBlock->rbegin()); + CFGStmt Elem = SrcBlock->rbegin()->castAs<CFGStmt>(); const Expr *RHS = cast<Expr>(Elem.getStmt()); SVal RHSVal = N->getState()->getSVal(RHS, Pred->getLocationContext()); - DefinedOrUnknownSVal DefinedRHS = cast<DefinedOrUnknownSVal>(RHSVal); - ProgramStateRef StTrue, StFalse; - llvm::tie(StTrue, StFalse) = N->getState()->assume(DefinedRHS); - if (StTrue) { - if (StFalse) { - // We can't constrain the value to 0 or 1; the best we can do is a cast. - X = getSValBuilder().evalCast(RHSVal, B->getType(), RHS->getType()); + if (RHSVal.isUndef()) { + X = RHSVal; + } else { + DefinedOrUnknownSVal DefinedRHS = RHSVal.castAs<DefinedOrUnknownSVal>(); + ProgramStateRef StTrue, StFalse; + llvm::tie(StTrue, StFalse) = N->getState()->assume(DefinedRHS); + if (StTrue) { + if (StFalse) { + // We can't constrain the value to 0 or 1. + // The best we can do is a cast. + X = getSValBuilder().evalCast(RHSVal, B->getType(), RHS->getType()); + } else { + // The value is known to be true. + X = getSValBuilder().makeIntVal(1, B->getType()); + } } else { - // The value is known to be true. - X = getSValBuilder().makeIntVal(1, B->getType()); + // The value is known to be false. + assert(StFalse && "Infeasible path!"); + X = getSValBuilder().makeIntVal(0, B->getType()); } - } else { - // The value is known to be false. - assert(StFalse && "Infeasible path!"); - X = getSValBuilder().makeIntVal(0, B->getType()); } } - Bldr.generateNode(B, Pred, state->BindExpr(B, Pred->getLocationContext(), X)); } @@ -581,8 +586,10 @@ void ExprEngine::VisitInitListExpr(const InitListExpr *IE, for (InitListExpr::const_reverse_iterator it = IE->rbegin(), ei = IE->rend(); it != ei; ++it) { - vals = getBasicVals().consVals(state->getSVal(cast<Expr>(*it), LCtx), - vals); + SVal V = state->getSVal(cast<Expr>(*it), LCtx); + if (dyn_cast_or_null<CXXTempObjectRegion>(V.getAsRegion())) + V = UnknownVal(); + vals = getBasicVals().consVals(V, vals); } B.generateNode(IE, Pred, @@ -615,14 +622,16 @@ void ExprEngine::VisitGuardedExpr(const Expr *Ex, for (const ExplodedNode *N = Pred ; N ; N = *N->pred_begin()) { ProgramPoint PP = N->getLocation(); - if (isa<PreStmtPurgeDeadSymbols>(PP) || isa<BlockEntrance>(PP)) { + if (PP.getAs<PreStmtPurgeDeadSymbols>() || PP.getAs<BlockEntrance>()) { assert(N->pred_size() == 1); continue; } - SrcBlock = cast<BlockEdge>(&PP)->getSrc(); + SrcBlock = PP.castAs<BlockEdge>().getSrc(); break; } + assert(SrcBlock && "missing function entry"); + // Find the last expression in the predecessor block. That is the // expression that is used for the value of the ternary expression. bool hasValue = false; @@ -631,7 +640,7 @@ void ExprEngine::VisitGuardedExpr(const Expr *Ex, for (CFGBlock::const_reverse_iterator I = SrcBlock->rbegin(), E = SrcBlock->rend(); I != E; ++I) { CFGElement CE = *I; - if (CFGStmt *CS = dyn_cast<CFGStmt>(&CE)) { + if (Optional<CFGStmt> CS = CE.getAs<CFGStmt>()) { const Expr *ValEx = cast<Expr>(CS->getStmt()); hasValue = true; V = state->getSVal(ValEx, LCtx); @@ -785,11 +794,11 @@ void ExprEngine::VisitUnaryOperator(const UnaryOperator* U, llvm_unreachable("Invalid Opcode."); case UO_Not: // FIXME: Do we need to handle promotions? - state = state->BindExpr(U, LCtx, evalComplement(cast<NonLoc>(V))); + state = state->BindExpr(U, LCtx, evalComplement(V.castAs<NonLoc>())); break; case UO_Minus: // FIXME: Do we need to handle promotions? - state = state->BindExpr(U, LCtx, evalMinus(cast<NonLoc>(V))); + state = state->BindExpr(U, LCtx, evalMinus(V.castAs<NonLoc>())); break; case UO_LNot: // C99 6.5.3.3: "The expression !E is equivalent to (0==E)." @@ -797,14 +806,16 @@ void ExprEngine::VisitUnaryOperator(const UnaryOperator* U, // Note: technically we do "E == 0", but this is the same in the // transfer functions as "0 == E". SVal Result; - if (isa<Loc>(V)) { + if (Optional<Loc> LV = V.getAs<Loc>()) { Loc X = svalBuilder.makeNull(); - Result = evalBinOp(state, BO_EQ, cast<Loc>(V), X, - U->getType()); + Result = evalBinOp(state, BO_EQ, *LV, X, U->getType()); } - else { + else if (Ex->getType()->isFloatingType()) { + // FIXME: handle floating point types. + Result = UnknownVal(); + } else { nonloc::ConcreteInt X(getBasicVals().getValue(0, Ex->getType())); - Result = evalBinOp(state, BO_EQ, cast<NonLoc>(V), X, + Result = evalBinOp(state, BO_EQ, V.castAs<NonLoc>(), X, U->getType()); } @@ -846,7 +857,7 @@ void ExprEngine::VisitIncrementDecrementOperator(const UnaryOperator* U, Bldr.generateNode(U, *I, state->BindExpr(U, LCtx, V2_untested)); continue; } - DefinedSVal V2 = cast<DefinedSVal>(V2_untested); + DefinedSVal V2 = V2_untested.castAs<DefinedSVal>(); // Handle all other values. BinaryOperator::Opcode Op = U->isIncrementOp() ? BO_Add : BO_Sub; diff --git a/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp b/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp index b3baa7905782..ed90dc589181 100644 --- a/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp @@ -11,13 +11,13 @@ // //===----------------------------------------------------------------------===// -#include "clang/StaticAnalyzer/Core/CheckerManager.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/StmtCXX.h" #include "clang/Basic/PrettyStackTrace.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" using namespace clang; using namespace ento; @@ -30,23 +30,90 @@ void ExprEngine::CreateCXXTemporaryObject(const MaterializeTemporaryExpr *ME, 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, LCtx); // If the value is already a CXXTempObjectRegion, it is fine as it is. // Otherwise, create a new CXXTempObjectRegion, and copy the value into it. + // This is an optimization for when an rvalue is constructed and then + // immediately materialized. const MemRegion *MR = V.getAsRegion(); - if (!MR || !isa<CXXTempObjectRegion>(MR)) { - const MemRegion *R = - svalBuilder.getRegionManager().getCXXTempObjectRegion(ME, LCtx); + if (const CXXTempObjectRegion *TR = + dyn_cast_or_null<CXXTempObjectRegion>(MR)) { + if (getContext().hasSameUnqualifiedType(TR->getValueType(), ME->getType())) + state = state->BindExpr(ME, LCtx, V); + } + + if (state == Pred->getState()) + state = createTemporaryRegionIfNeeded(state, LCtx, tempExpr, ME); + Bldr.generateNode(ME, Pred, state); +} + +// FIXME: This is the sort of code that should eventually live in a Core +// checker rather than as a special case in ExprEngine. +void ExprEngine::performTrivialCopy(NodeBuilder &Bldr, ExplodedNode *Pred, + const CallEvent &Call) { + SVal ThisVal; + bool AlwaysReturnsLValue; + if (const CXXConstructorCall *Ctor = dyn_cast<CXXConstructorCall>(&Call)) { + assert(Ctor->getDecl()->isTrivial()); + assert(Ctor->getDecl()->isCopyOrMoveConstructor()); + ThisVal = Ctor->getCXXThisVal(); + AlwaysReturnsLValue = false; + } else { + assert(cast<CXXMethodDecl>(Call.getDecl())->isTrivial()); + assert(cast<CXXMethodDecl>(Call.getDecl())->getOverloadedOperator() == + OO_Equal); + ThisVal = cast<CXXInstanceCall>(Call).getCXXThisVal(); + AlwaysReturnsLValue = true; + } - SVal L = loc::MemRegionVal(R); - state = state->bindLoc(L, V); - V = L; + const LocationContext *LCtx = Pred->getLocationContext(); + + ExplodedNodeSet Dst; + Bldr.takeNodes(Pred); + + SVal V = Call.getArgSVal(0); + + // If the value being copied is not unknown, load from its location to get + // an aggregate rvalue. + if (Optional<Loc> L = V.getAs<Loc>()) + V = Pred->getState()->getSVal(*L); + else + assert(V.isUnknown()); + + const Expr *CallExpr = Call.getOriginExpr(); + evalBind(Dst, CallExpr, Pred, ThisVal, V, true); + + PostStmt PS(CallExpr, LCtx); + for (ExplodedNodeSet::iterator I = Dst.begin(), E = Dst.end(); + I != E; ++I) { + ProgramStateRef State = (*I)->getState(); + if (AlwaysReturnsLValue) + State = State->BindExpr(CallExpr, LCtx, ThisVal); + else + State = bindReturnValue(Call, LCtx, State); + Bldr.generateNode(PS, State, *I); + } +} + + +/// Returns a region representing the first element of a (possibly +/// multi-dimensional) array. +/// +/// On return, \p Ty will be set to the base type of the array. +/// +/// If the type is not an array type at all, the original value is returned. +static SVal makeZeroElementRegion(ProgramStateRef State, SVal LValue, + QualType &Ty) { + SValBuilder &SVB = State->getStateManager().getSValBuilder(); + ASTContext &Ctx = SVB.getContext(); + + while (const ArrayType *AT = Ctx.getAsArrayType(Ty)) { + Ty = AT->getElementType(); + LValue = State->getLValue(Ty, SVB.makeZeroArrayIndex(), LValue); } - Bldr.generateNode(ME, Pred, state->BindExpr(ME, LCtx, V)); + return LValue; } void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *CE, @@ -57,6 +124,10 @@ void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *CE, const MemRegion *Target = 0; + // FIXME: Handle arrays, which run the same constructor for every element. + // For now, we just run the first constructor (which should still invalidate + // the entire array). + switch (CE->getConstructionKind()) { case CXXConstructExpr::CK_Complete: { // See if we're constructing an existing region by looking at the next @@ -66,29 +137,21 @@ void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *CE, CFGElement Next = (*B)[currStmtIdx+1]; // Is this a constructor for a local variable? - if (const CFGStmt *StmtElem = dyn_cast<CFGStmt>(&Next)) { + if (Optional<CFGStmt> StmtElem = Next.getAs<CFGStmt>()) { if (const DeclStmt *DS = dyn_cast<DeclStmt>(StmtElem->getStmt())) { if (const VarDecl *Var = dyn_cast<VarDecl>(DS->getSingleDecl())) { if (Var->getInit()->IgnoreImplicit() == CE) { + SVal LValue = State->getLValue(Var, LCtx); QualType Ty = Var->getType(); - if (const ArrayType *AT = getContext().getAsArrayType(Ty)) { - // FIXME: Handle arrays, which run the same constructor for - // every element. This workaround will just run the first - // constructor (which should still invalidate the entire array). - SVal Base = State->getLValue(Var, LCtx); - Target = State->getLValue(AT->getElementType(), - getSValBuilder().makeZeroArrayIndex(), - Base).getAsRegion(); - } else { - Target = State->getLValue(Var, LCtx).getAsRegion(); - } + LValue = makeZeroElementRegion(State, LValue, Ty); + Target = LValue.getAsRegion(); } } } } // Is this a constructor for a member? - if (const CFGInitializer *InitElem = dyn_cast<CFGInitializer>(&Next)) { + if (Optional<CFGInitializer> InitElem = Next.getAs<CFGInitializer>()) { const CXXCtorInitializer *Init = InitElem->getInitializer(); assert(Init->isAnyMemberInitializer()); @@ -97,13 +160,19 @@ void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *CE, LCtx->getCurrentStackFrame()); SVal ThisVal = State->getSVal(ThisPtr); + const ValueDecl *Field; + SVal FieldVal; if (Init->isIndirectMemberInitializer()) { - SVal Field = State->getLValue(Init->getIndirectMember(), ThisVal); - Target = Field.getAsRegion(); + Field = Init->getIndirectMember(); + FieldVal = State->getLValue(Init->getIndirectMember(), ThisVal); } else { - SVal Field = State->getLValue(Init->getMember(), ThisVal); - Target = Field.getAsRegion(); + Field = Init->getMember(); + FieldVal = State->getLValue(Init->getMember(), ThisVal); } + + QualType Ty = Field->getType(); + FieldVal = makeZeroElementRegion(State, FieldVal, Ty); + Target = FieldVal.getAsRegion(); } // FIXME: This will eventually need to handle new-expressions as well. @@ -130,8 +199,10 @@ void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *CE, Target = ThisVal.getAsRegion(); } else { // Cast to the base type. - QualType BaseTy = CE->getType(); - SVal BaseVal = getStoreManager().evalDerivedToBase(ThisVal, BaseTy); + bool IsVirtual = + (CE->getConstructionKind() == CXXConstructExpr::CK_VirtualBase); + SVal BaseVal = getStoreManager().evalDerivedToBase(ThisVal, CE->getType(), + IsVirtual); Target = BaseVal.getAsRegion(); } break; @@ -148,14 +219,26 @@ void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *CE, getCheckerManager().runCheckersForPreCall(DstPreCall, DstPreVisit, *Call, *this); - ExplodedNodeSet DstInvalidated; - StmtNodeBuilder Bldr(DstPreCall, DstInvalidated, *currBldrCtx); - for (ExplodedNodeSet::iterator I = DstPreCall.begin(), E = DstPreCall.end(); - I != E; ++I) - defaultEvalCall(Bldr, *I, *Call); + ExplodedNodeSet DstEvaluated; + StmtNodeBuilder Bldr(DstPreCall, DstEvaluated, *currBldrCtx); + + bool IsArray = isa<ElementRegion>(Target); + if (CE->getConstructor()->isTrivial() && + CE->getConstructor()->isCopyOrMoveConstructor() && + !IsArray) { + // FIXME: Handle other kinds of trivial constructors as well. + for (ExplodedNodeSet::iterator I = DstPreCall.begin(), E = DstPreCall.end(); + I != E; ++I) + performTrivialCopy(Bldr, *I, *Call); + + } else { + for (ExplodedNodeSet::iterator I = DstPreCall.begin(), E = DstPreCall.end(); + I != E; ++I) + defaultEvalCall(Bldr, *I, *Call); + } ExplodedNodeSet DstPostCall; - getCheckerManager().runCheckersForPostCall(DstPostCall, DstInvalidated, + getCheckerManager().runCheckersForPostCall(DstPostCall, DstEvaluated, *Call, *this); getCheckerManager().runCheckersForPostStmt(destNodes, DstPostCall, CE, *this); } @@ -172,11 +255,9 @@ void ExprEngine::VisitCXXDestructor(QualType ObjectType, // FIXME: We need to run the same destructor on every element of the array. // This workaround will just run the first destructor (which will still // invalidate the entire array). - if (const ArrayType *AT = getContext().getAsArrayType(ObjectType)) { - ObjectType = AT->getElementType(); - Dest = State->getLValue(ObjectType, getSValBuilder().makeZeroArrayIndex(), - loc::MemRegionVal(Dest)).getAsRegion(); - } + SVal DestVal = loc::MemRegionVal(Dest); + DestVal = makeZeroElementRegion(State, DestVal, ObjectType); + Dest = DestVal.getAsRegion(); const CXXRecordDecl *RecordDecl = ObjectType->getAsCXXRecordDecl(); assert(RecordDecl && "Only CXXRecordDecls should have destructors"); @@ -211,15 +292,35 @@ void ExprEngine::VisitCXXNewExpr(const CXXNewExpr *CNE, ExplodedNode *Pred, // Also, we need to decide how allocators actually work -- they're not // really part of the CXXNewExpr because they happen BEFORE the // CXXConstructExpr subexpression. See PR12014 for some discussion. - StmtNodeBuilder Bldr(Pred, Dst, *currBldrCtx); unsigned blockCount = currBldrCtx->blockCount(); const LocationContext *LCtx = Pred->getLocationContext(); - DefinedOrUnknownSVal symVal = svalBuilder.conjureSymbolVal(0, CNE, LCtx, - CNE->getType(), - blockCount); - ProgramStateRef State = Pred->getState(); + DefinedOrUnknownSVal symVal = UnknownVal(); + FunctionDecl *FD = CNE->getOperatorNew(); + + bool IsStandardGlobalOpNewFunction = false; + if (FD && !isa<CXXMethodDecl>(FD) && !FD->isVariadic()) { + if (FD->getNumParams() == 2) { + QualType T = FD->getParamDecl(1)->getType(); + if (const IdentifierInfo *II = T.getBaseTypeIdentifier()) + // NoThrow placement new behaves as a standard new. + IsStandardGlobalOpNewFunction = II->getName().equals("nothrow_t"); + } + else + // Placement forms are considered non-standard. + IsStandardGlobalOpNewFunction = (FD->getNumParams() == 1); + } + // We assume all standard global 'operator new' functions allocate memory in + // heap. We realize this is an approximation that might not correctly model + // a custom global allocator. + if (IsStandardGlobalOpNewFunction) + symVal = svalBuilder.getConjuredHeapSymbolVal(CNE, LCtx, blockCount); + else + symVal = svalBuilder.conjureSymbolVal(0, CNE, LCtx, CNE->getType(), + blockCount); + + ProgramStateRef State = Pred->getState(); CallEventManager &CEMgr = getStateManager().getCallEventManager(); CallEventRef<CXXAllocatorCall> Call = CEMgr.getCXXAllocatorCall(CNE, State, LCtx); @@ -228,12 +329,13 @@ void ExprEngine::VisitCXXNewExpr(const CXXNewExpr *CNE, ExplodedNode *Pred, // FIXME: Once we figure out how we want allocators to work, // we should be using the usual pre-/(default-)eval-/post-call checks here. State = Call->invalidateRegions(blockCount); + if (!State) + return; // If we're compiling with exceptions enabled, and this allocation function // is not declared as non-throwing, failures /must/ be signalled by // exceptions, and thus the return value will never be NULL. // C++11 [basic.stc.dynamic.allocation]p3. - FunctionDecl *FD = CNE->getOperatorNew(); if (FD && getContext().getLangOpts().CXXExceptions) { QualType Ty = FD->getType(); if (const FunctionProtoType *ProtoType = Ty->getAs<FunctionProtoType>()) @@ -241,10 +343,12 @@ void ExprEngine::VisitCXXNewExpr(const CXXNewExpr *CNE, ExplodedNode *Pred, State = State->assume(symVal, true); } + StmtNodeBuilder Bldr(Pred, Dst, *currBldrCtx); + if (CNE->isArray()) { // FIXME: allocating an array requires simulating the constructors. // For now, just return a symbolicated region. - const MemRegion *NewReg = cast<loc::MemRegionVal>(symVal).getRegion(); + const MemRegion *NewReg = symVal.castAs<loc::MemRegionVal>().getRegion(); QualType ObjTy = CNE->getType()->getAs<PointerType>()->getPointeeType(); const ElementRegion *EleReg = getStoreManager().GetElementZeroRegion(NewReg, ObjTy); @@ -258,30 +362,32 @@ void ExprEngine::VisitCXXNewExpr(const CXXNewExpr *CNE, ExplodedNode *Pred, // CXXNewExpr, we need to make sure that the constructed object is not // immediately invalidated here. (The placement call should happen before // the constructor call anyway.) + SVal Result = symVal; if (FD && FD->isReservedGlobalPlacementOperator()) { // Non-array placement new should always return the placement location. SVal PlacementLoc = State->getSVal(CNE->getPlacementArg(0), LCtx); - SVal Result = svalBuilder.evalCast(PlacementLoc, CNE->getType(), - CNE->getPlacementArg(0)->getType()); - State = State->BindExpr(CNE, LCtx, Result); - } else { - State = State->BindExpr(CNE, LCtx, symVal); + Result = svalBuilder.evalCast(PlacementLoc, CNE->getType(), + CNE->getPlacementArg(0)->getType()); } + // Bind the address of the object, then check to see if we cached out. + State = State->BindExpr(CNE, LCtx, Result); + ExplodedNode *NewN = Bldr.generateNode(CNE, Pred, State); + if (!NewN) + return; + // If the type is not a record, we won't have a CXXConstructExpr as an // initializer. Copy the value over. if (const Expr *Init = CNE->getInitializer()) { if (!isa<CXXConstructExpr>(Init)) { - QualType ObjTy = CNE->getType()->getAs<PointerType>()->getPointeeType(); - (void)ObjTy; - assert(!ObjTy->isRecordType()); - SVal Location = State->getSVal(CNE, LCtx); - if (isa<Loc>(Location)) - State = State->bindLoc(cast<Loc>(Location), State->getSVal(Init, LCtx)); + assert(Bldr.getResults().size() == 1); + Bldr.takeNodes(NewN); + + assert(!CNE->getType()->getPointeeCXXRecordDecl()); + evalBind(Dst, CNE, NewN, Result, State->getSVal(Init, LCtx), + /*FirstInit=*/IsStandardGlobalOpNewFunction); } } - - Bldr.generateNode(CNE, Pred, State); } void ExprEngine::VisitCXXDeleteExpr(const CXXDeleteExpr *CDE, diff --git a/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp b/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp index 3ead0817f71b..f01e4e764014 100644 --- a/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp @@ -13,13 +13,13 @@ #define DEBUG_TYPE "ExprEngine" -#include "clang/Analysis/Analyses/LiveVariables.h" -#include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/AST/CXXInheritance.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/ParentMap.h" +#include "clang/Analysis/Analyses/LiveVariables.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "llvm/ADT/SmallSet.h" #include "llvm/ADT/Statistic.h" #include "llvm/Support/SaveAndRestore.h" @@ -33,6 +33,9 @@ STATISTIC(NumOfDynamicDispatchPathSplits, STATISTIC(NumInlinedCalls, "The # of times we inlined a call"); +STATISTIC(NumReachedInlineCountMax, + "The # of times we reached inline count maximum"); + void ExprEngine::processCallEnter(CallEnter CE, ExplodedNode *Pred) { // Get the entry block in the CFG of the callee. const StackFrameContext *calleeCtx = CE.getCalleeContext(); @@ -64,6 +67,7 @@ void ExprEngine::processCallEnter(CallEnter CE, ExplodedNode *Pred) { static std::pair<const Stmt*, const CFGBlock*> getLastStmt(const ExplodedNode *Node) { const Stmt *S = 0; + const CFGBlock *Blk = 0; const StackFrameContext *SF = Node->getLocation().getLocationContext()->getCurrentStackFrame(); @@ -73,10 +77,10 @@ static std::pair<const Stmt*, const ProgramPoint &PP = Node->getLocation(); if (PP.getLocationContext()->getCurrentStackFrame() == SF) { - if (const StmtPoint *SP = dyn_cast<StmtPoint>(&PP)) { + if (Optional<StmtPoint> SP = PP.getAs<StmtPoint>()) { S = SP->getStmt(); break; - } else if (const CallExitEnd *CEE = dyn_cast<CallExitEnd>(&PP)) { + } else if (Optional<CallExitEnd> CEE = PP.getAs<CallExitEnd>()) { S = CEE->getCalleeContext()->getCallSite(); if (S) break; @@ -84,15 +88,17 @@ static std::pair<const Stmt*, // If there is no statement, this is an implicitly-generated call. // We'll walk backwards over it and then continue the loop to find // an actual statement. - const CallEnter *CE; + Optional<CallEnter> CE; do { Node = Node->getFirstPred(); CE = Node->getLocationAs<CallEnter>(); } while (!CE || CE->getCalleeContext() != CEE->getCalleeContext()); // Continue searching the graph. + } else if (Optional<BlockEdge> BE = PP.getAs<BlockEdge>()) { + Blk = BE->getSrc(); } - } else if (const CallEnter *CE = dyn_cast<CallEnter>(&PP)) { + } else if (Optional<CallEnter> CE = PP.getAs<CallEnter>()) { // If we reached the CallEnter for this function, it has no statements. if (CE->getCalleeContext() == SF) break; @@ -104,24 +110,6 @@ static std::pair<const Stmt*, Node = *Node->pred_begin(); } - const CFGBlock *Blk = 0; - if (S) { - // Now, get the enclosing basic block. - while (Node) { - const ProgramPoint &PP = Node->getLocation(); - if (isa<BlockEdge>(PP) && - (PP.getLocationContext()->getCurrentStackFrame() == SF)) { - BlockEdge &EPP = cast<BlockEdge>(PP); - Blk = EPP.getDst(); - break; - } - if (Node->pred_empty()) - return std::pair<const Stmt*, const CFGBlock*>(S, (CFGBlock*)0); - - Node = *Node->pred_begin(); - } - } - return std::pair<const Stmt*, const CFGBlock*>(S, Blk); } @@ -133,7 +121,7 @@ static std::pair<const Stmt*, static SVal adjustReturnValue(SVal V, QualType ExpectedTy, QualType ActualTy, StoreManager &StoreMgr) { // For now, the only adjustments we handle apply only to locations. - if (!isa<Loc>(V)) + if (!V.getAs<Loc>()) return V; // If the types already match, don't do any unnecessary work. @@ -168,27 +156,25 @@ static SVal adjustReturnValue(SVal V, QualType ExpectedTy, QualType ActualTy, void ExprEngine::removeDeadOnEndOfFunction(NodeBuilderContext& BC, ExplodedNode *Pred, ExplodedNodeSet &Dst) { - NodeBuilder Bldr(Pred, Dst, BC); - // Find the last statement in the function and the corresponding basic block. const Stmt *LastSt = 0; const CFGBlock *Blk = 0; llvm::tie(LastSt, Blk) = getLastStmt(Pred); if (!Blk || !LastSt) { + Dst.Add(Pred); return; } - - // If the last statement is return, everything it references should stay live. - if (isa<ReturnStmt>(LastSt)) - return; - // Here, we call the Symbol Reaper with 0 stack context telling it to clean up - // everything on the stack. We use LastStmt as a diagnostic statement, with - // which the PreStmtPurgeDead point will be associated. - currBldrCtx = &BC; - removeDead(Pred, Dst, 0, 0, LastSt, + // Here, we destroy the current location context. We use the current + // function's entire body as a diagnostic statement, with which the program + // point will be associated. However, we only want to use LastStmt as a + // reference for what to clean up if it's a ReturnStmt; otherwise, everything + // is dead. + SaveAndRestore<const NodeBuilderContext *> NodeContextRAII(currBldrCtx, &BC); + const LocationContext *LCtx = Pred->getLocationContext(); + removeDead(Pred, Dst, dyn_cast<ReturnStmt>(LastSt), LCtx, + LCtx->getAnalysisDeclContext()->getBody(), ProgramPoint::PostStmtPurgeDeadSymbolsKind); - currBldrCtx = 0; } static bool wasDifferentDeclUsedForInlining(CallEventRef<> Call, @@ -201,6 +187,23 @@ static bool wasDifferentDeclUsedForInlining(CallEventRef<> Call, return RuntimeCallee->getCanonicalDecl() != StaticDecl->getCanonicalDecl(); } +/// Returns true if the CXXConstructExpr \p E was intended to construct a +/// prvalue for the region in \p V. +/// +/// Note that we can't just test for rvalue vs. glvalue because +/// CXXConstructExprs embedded in DeclStmts and initializers are considered +/// rvalues by the AST, and the analyzer would like to treat them as lvalues. +static bool isTemporaryPRValue(const CXXConstructExpr *E, SVal V) { + if (E->isGLValue()) + return false; + + const MemRegion *MR = V.getAsRegion(); + if (!MR) + return false; + + return isa<CXXTempObjectRegion>(MR); +} + /// The call exit is simulated with a sequence of nodes, which occur between /// CallExitBegin and CallExitEnd. The following operations occur between the /// two program points: @@ -261,13 +264,9 @@ void ExprEngine::processCallExit(ExplodedNode *CEBNode) { svalBuilder.getCXXThis(CCE->getConstructor()->getParent(), calleeCtx); SVal ThisV = state->getSVal(This); - // If the constructed object is a prvalue, get its bindings. - // Note that we have to be careful here because constructors embedded - // in DeclStmts are not marked as lvalues. - if (!CCE->isGLValue()) - if (const MemRegion *MR = ThisV.getAsRegion()) - if (isa<CXXTempObjectRegion>(MR)) - ThisV = state->getSVal(cast<Loc>(ThisV)); + // If the constructed object is a temporary prvalue, get its bindings. + if (isTemporaryPRValue(CCE, ThisV)) + ThisV = state->getSVal(ThisV.castAs<Loc>()); state = state->BindExpr(CCE, callerCtx, ThisV); } @@ -290,11 +289,12 @@ void ExprEngine::processCallExit(ExplodedNode *CEBNode) { NodeBuilderContext Ctx(getCoreEngine(), Blk, BindedRetNode); currBldrCtx = &Ctx; - // Here, we call the Symbol Reaper with 0 statement and caller location + // Here, we call the Symbol Reaper with 0 statement and callee location // context, telling it to clean up everything in the callee's context - // (and it's children). We use LastStmt as a diagnostic statement, which - // which the PreStmtPurge Dead point will be associated. - removeDead(BindedRetNode, CleanedNodes, 0, callerCtx, LastSt, + // (and its children). We use the callee's function body as a diagnostic + // statement, with which the program point will be associated. + removeDead(BindedRetNode, CleanedNodes, 0, calleeCtx, + calleeCtx->getAnalysisDeclContext()->getBody(), ProgramPoint::PostStmtPurgeDeadSymbolsKind); currBldrCtx = 0; } else { @@ -394,63 +394,6 @@ static bool IsInStdNamespace(const FunctionDecl *FD) { return ND->getName() == "std"; } -// Determine if we should inline the call. -bool ExprEngine::shouldInlineDecl(const Decl *D, ExplodedNode *Pred) { - AnalysisDeclContext *CalleeADC = AMgr.getAnalysisDeclContext(D); - const CFG *CalleeCFG = CalleeADC->getCFG(); - - // It is possible that the CFG cannot be constructed. - // Be safe, and check if the CalleeCFG is valid. - if (!CalleeCFG) - return false; - - bool IsRecursive = false; - unsigned StackDepth = 0; - examineStackFrames(D, Pred->getLocationContext(), IsRecursive, StackDepth); - if ((StackDepth >= AMgr.options.InlineMaxStackDepth) && - ((CalleeCFG->getNumBlockIDs() > AMgr.options.getAlwaysInlineSize()) - || IsRecursive)) - return false; - - if (Engine.FunctionSummaries->hasReachedMaxBlockCount(D)) - return false; - - if (CalleeCFG->getNumBlockIDs() > AMgr.options.InlineMaxFunctionSize) - return false; - - // Do not inline variadic calls (for now). - if (const BlockDecl *BD = dyn_cast<BlockDecl>(D)) { - if (BD->isVariadic()) - return false; - } - else if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) { - if (FD->isVariadic()) - return false; - } - - if (getContext().getLangOpts().CPlusPlus) { - if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) { - // Conditionally allow the inlining of template functions. - if (!getAnalysisManager().options.mayInlineTemplateFunctions()) - if (FD->getTemplatedKind() != FunctionDecl::TK_NonTemplate) - return false; - - // Conditionally allow the inlining of C++ standard library functions. - if (!getAnalysisManager().options.mayInlineCXXStandardLibrary()) - if (getContext().getSourceManager().isInSystemHeader(FD->getLocation())) - if (IsInStdNamespace(FD)) - return false; - } - } - - // It is possible that the live variables analysis cannot be - // run. If so, bail out. - if (!CalleeADC->getAnalysis<RelaxedLiveVariables>()) - return false; - - return true; -} - // The GDM component containing the dynamic dispatch bifurcation info. When // the exact type of the receiver is not known, we want to explore both paths - // one on which we do inline it and the other one on which we don't. This is @@ -474,107 +417,16 @@ bool ExprEngine::inlineCall(const CallEvent &Call, const Decl *D, const LocationContext *CurLC = Pred->getLocationContext(); const StackFrameContext *CallerSFC = CurLC->getCurrentStackFrame(); - const LocationContext *ParentOfCallee = 0; - - AnalyzerOptions &Opts = getAnalysisManager().options; - - // FIXME: Refactor this check into a hypothetical CallEvent::canInline. - switch (Call.getKind()) { - case CE_Function: - break; - case CE_CXXMember: - case CE_CXXMemberOperator: - if (!Opts.mayInlineCXXMemberFunction(CIMK_MemberFunctions)) - return false; - break; - case CE_CXXConstructor: { - if (!Opts.mayInlineCXXMemberFunction(CIMK_Constructors)) - return false; - - const CXXConstructorCall &Ctor = cast<CXXConstructorCall>(Call); - - // FIXME: We don't handle constructors or destructors for arrays properly. - const MemRegion *Target = Ctor.getCXXThisVal().getAsRegion(); - if (Target && isa<ElementRegion>(Target)) - return false; - - // FIXME: This is a hack. We don't use the correct region for a new - // expression, so if we inline the constructor its result will just be - // thrown away. This short-term hack is tracked in <rdar://problem/12180598> - // and the longer-term possible fix is discussed in PR12014. - const CXXConstructExpr *CtorExpr = Ctor.getOriginExpr(); - if (const Stmt *Parent = CurLC->getParentMap().getParent(CtorExpr)) - if (isa<CXXNewExpr>(Parent)) - return false; - - // Inlining constructors requires including initializers in the CFG. - const AnalysisDeclContext *ADC = CallerSFC->getAnalysisDeclContext(); - assert(ADC->getCFGBuildOptions().AddInitializers && "No CFG initializers"); - (void)ADC; - - // If the destructor is trivial, it's always safe to inline the constructor. - if (Ctor.getDecl()->getParent()->hasTrivialDestructor()) - break; - - // For other types, only inline constructors if destructor inlining is - // also enabled. - if (!Opts.mayInlineCXXMemberFunction(CIMK_Destructors)) - return false; - - // FIXME: This is a hack. We don't handle temporary destructors - // right now, so we shouldn't inline their constructors. - if (CtorExpr->getConstructionKind() == CXXConstructExpr::CK_Complete) - if (!Target || !isa<DeclRegion>(Target)) - return false; - - break; - } - case CE_CXXDestructor: { - if (!Opts.mayInlineCXXMemberFunction(CIMK_Destructors)) - return false; - - // Inlining destructors requires building the CFG correctly. - const AnalysisDeclContext *ADC = CallerSFC->getAnalysisDeclContext(); - assert(ADC->getCFGBuildOptions().AddImplicitDtors && "No CFG destructors"); - (void)ADC; - - const CXXDestructorCall &Dtor = cast<CXXDestructorCall>(Call); - - // FIXME: We don't handle constructors or destructors for arrays properly. - const MemRegion *Target = Dtor.getCXXThisVal().getAsRegion(); - if (Target && isa<ElementRegion>(Target)) - return false; - - break; - } - case CE_CXXAllocator: - // Do not inline allocators until we model deallocators. - // This is unfortunate, but basically necessary for smart pointers and such. - return false; - case CE_Block: { + const LocationContext *ParentOfCallee = CallerSFC; + if (Call.getKind() == CE_Block) { const BlockDataRegion *BR = cast<BlockCall>(Call).getBlockRegion(); assert(BR && "If we have the block definition we should have its region"); AnalysisDeclContext *BlockCtx = AMgr.getAnalysisDeclContext(D); ParentOfCallee = BlockCtx->getBlockInvocationContext(CallerSFC, cast<BlockDecl>(D), BR); - break; - } - case CE_ObjCMessage: - if (!Opts.mayInlineObjCMethod()) - return false; - if (!(getAnalysisManager().options.IPAMode == DynamicDispatch || - getAnalysisManager().options.IPAMode == DynamicDispatchBifurcate)) - return false; - break; } - - if (!shouldInlineDecl(D, Pred)) - return false; - if (!ParentOfCallee) - ParentOfCallee = CallerSFC; - // This may be NULL, but that's fine. const Expr *CallE = Call.getOriginExpr(); @@ -585,6 +437,7 @@ bool ExprEngine::inlineCall(const CallEvent &Call, const Decl *D, currBldrCtx->getBlock(), currStmtIdx); + CallEnter Loc(CallE, CalleeSFC, CurLC); // Construct a new state which contains the mapping from actual to @@ -613,11 +466,11 @@ bool ExprEngine::inlineCall(const CallEvent &Call, const Decl *D, static ProgramStateRef getInlineFailedState(ProgramStateRef State, const Stmt *CallE) { - void *ReplayState = State->get<ReplayWithoutInlining>(); + const void *ReplayState = State->get<ReplayWithoutInlining>(); if (!ReplayState) return 0; - assert(ReplayState == (const void*)CallE && "Backtracked to the wrong call."); + assert(ReplayState == CallE && "Backtracked to the wrong call."); (void)CallE; return State->remove<ReplayWithoutInlining>(); @@ -696,7 +549,13 @@ ProgramStateRef ExprEngine::bindReturnValue(const CallEvent &Call, } } } else if (const CXXConstructorCall *C = dyn_cast<CXXConstructorCall>(&Call)){ - return State->BindExpr(E, LCtx, C->getCXXThisVal()); + SVal ThisV = C->getCXXThisVal(); + + // If the constructed object is a temporary prvalue, get its bindings. + if (isTemporaryPRValue(cast<CXXConstructExpr>(E), ThisV)) + ThisV = State->getSVal(ThisV.castAs<Loc>()); + + return State->BindExpr(E, LCtx, ThisV); } // Conjure a symbol if the return value is unknown. @@ -710,7 +569,8 @@ ProgramStateRef ExprEngine::bindReturnValue(const CallEvent &Call, // Conservatively evaluate call by invalidating regions and binding // a conjured return value. void ExprEngine::conservativeEvalCall(const CallEvent &Call, NodeBuilder &Bldr, - ExplodedNode *Pred, ProgramStateRef State) { + ExplodedNode *Pred, + ProgramStateRef State) { State = Call.invalidateRegions(currBldrCtx->blockCount(), State); State = bindReturnValue(Call, Pred->getLocationContext(), State); @@ -718,38 +578,332 @@ void ExprEngine::conservativeEvalCall(const CallEvent &Call, NodeBuilder &Bldr, Bldr.generateNode(Call.getProgramPoint(), State, Pred); } +enum CallInlinePolicy { + CIP_Allowed, + CIP_DisallowedOnce, + CIP_DisallowedAlways +}; + +static CallInlinePolicy mayInlineCallKind(const CallEvent &Call, + const ExplodedNode *Pred, + AnalyzerOptions &Opts) { + const LocationContext *CurLC = Pred->getLocationContext(); + const StackFrameContext *CallerSFC = CurLC->getCurrentStackFrame(); + switch (Call.getKind()) { + case CE_Function: + case CE_Block: + break; + case CE_CXXMember: + case CE_CXXMemberOperator: + if (!Opts.mayInlineCXXMemberFunction(CIMK_MemberFunctions)) + return CIP_DisallowedAlways; + break; + case CE_CXXConstructor: { + if (!Opts.mayInlineCXXMemberFunction(CIMK_Constructors)) + return CIP_DisallowedAlways; + + const CXXConstructorCall &Ctor = cast<CXXConstructorCall>(Call); + + // FIXME: We don't handle constructors or destructors for arrays properly. + // Even once we do, we still need to be careful about implicitly-generated + // initializers for array fields in default move/copy constructors. + const MemRegion *Target = Ctor.getCXXThisVal().getAsRegion(); + if (Target && isa<ElementRegion>(Target)) + return CIP_DisallowedOnce; + + // FIXME: This is a hack. We don't use the correct region for a new + // expression, so if we inline the constructor its result will just be + // thrown away. This short-term hack is tracked in <rdar://problem/12180598> + // and the longer-term possible fix is discussed in PR12014. + const CXXConstructExpr *CtorExpr = Ctor.getOriginExpr(); + if (const Stmt *Parent = CurLC->getParentMap().getParent(CtorExpr)) + if (isa<CXXNewExpr>(Parent)) + return CIP_DisallowedOnce; + + // Inlining constructors requires including initializers in the CFG. + const AnalysisDeclContext *ADC = CallerSFC->getAnalysisDeclContext(); + assert(ADC->getCFGBuildOptions().AddInitializers && "No CFG initializers"); + (void)ADC; + + // If the destructor is trivial, it's always safe to inline the constructor. + if (Ctor.getDecl()->getParent()->hasTrivialDestructor()) + break; + + // For other types, only inline constructors if destructor inlining is + // also enabled. + if (!Opts.mayInlineCXXMemberFunction(CIMK_Destructors)) + return CIP_DisallowedAlways; + + // FIXME: This is a hack. We don't handle temporary destructors + // right now, so we shouldn't inline their constructors. + if (CtorExpr->getConstructionKind() == CXXConstructExpr::CK_Complete) + if (!Target || !isa<DeclRegion>(Target)) + return CIP_DisallowedOnce; + + break; + } + case CE_CXXDestructor: { + if (!Opts.mayInlineCXXMemberFunction(CIMK_Destructors)) + return CIP_DisallowedAlways; + + // Inlining destructors requires building the CFG correctly. + const AnalysisDeclContext *ADC = CallerSFC->getAnalysisDeclContext(); + assert(ADC->getCFGBuildOptions().AddImplicitDtors && "No CFG destructors"); + (void)ADC; + + const CXXDestructorCall &Dtor = cast<CXXDestructorCall>(Call); + + // FIXME: We don't handle constructors or destructors for arrays properly. + const MemRegion *Target = Dtor.getCXXThisVal().getAsRegion(); + if (Target && isa<ElementRegion>(Target)) + return CIP_DisallowedOnce; + + break; + } + case CE_CXXAllocator: + // Do not inline allocators until we model deallocators. + // This is unfortunate, but basically necessary for smart pointers and such. + return CIP_DisallowedAlways; + case CE_ObjCMessage: + if (!Opts.mayInlineObjCMethod()) + return CIP_DisallowedAlways; + if (!(Opts.getIPAMode() == IPAK_DynamicDispatch || + Opts.getIPAMode() == IPAK_DynamicDispatchBifurcate)) + return CIP_DisallowedAlways; + break; + } + + return CIP_Allowed; +} + +/// Returns true if the given C++ class is a container. +/// +/// Our heuristic for this is whether it contains a method named 'begin()' or a +/// nested type named 'iterator'. +static bool isContainerClass(const ASTContext &Ctx, const CXXRecordDecl *RD) { + // Don't record any path information. + CXXBasePaths Paths(false, false, false); + + const IdentifierInfo &BeginII = Ctx.Idents.get("begin"); + DeclarationName BeginName = Ctx.DeclarationNames.getIdentifier(&BeginII); + DeclContext::lookup_const_result BeginDecls = RD->lookup(BeginName); + if (!BeginDecls.empty()) + return true; + if (RD->lookupInBases(&CXXRecordDecl::FindOrdinaryMember, + BeginName.getAsOpaquePtr(), + Paths)) + return true; + + const IdentifierInfo &IterII = Ctx.Idents.get("iterator"); + DeclarationName IteratorName = Ctx.DeclarationNames.getIdentifier(&IterII); + DeclContext::lookup_const_result IterDecls = RD->lookup(IteratorName); + if (!IterDecls.empty()) + return true; + if (RD->lookupInBases(&CXXRecordDecl::FindOrdinaryMember, + IteratorName.getAsOpaquePtr(), + Paths)) + return true; + + return false; +} + +/// Returns true if the given function refers to a constructor or destructor of +/// a C++ container. +/// +/// We generally do a poor job modeling most containers right now, and would +/// prefer not to inline their methods. +static bool isContainerCtorOrDtor(const ASTContext &Ctx, + const FunctionDecl *FD) { + // Heuristic: a type is a container if it contains a "begin()" method + // or a type named "iterator". + if (!(isa<CXXConstructorDecl>(FD) || isa<CXXDestructorDecl>(FD))) + return false; + + const CXXRecordDecl *RD = cast<CXXMethodDecl>(FD)->getParent(); + return isContainerClass(Ctx, RD); +} + +/// Returns true if the function in \p CalleeADC may be inlined in general. +/// +/// This checks static properties of the function, such as its signature and +/// CFG, to determine whether the analyzer should ever consider inlining it, +/// in any context. +static bool mayInlineDecl(const CallEvent &Call, AnalysisDeclContext *CalleeADC, + AnalyzerOptions &Opts) { + // FIXME: Do not inline variadic calls. + if (Call.isVariadic()) + return false; + + // Check certain C++-related inlining policies. + ASTContext &Ctx = CalleeADC->getASTContext(); + if (Ctx.getLangOpts().CPlusPlus) { + if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(CalleeADC->getDecl())) { + // Conditionally control the inlining of template functions. + if (!Opts.mayInlineTemplateFunctions()) + if (FD->getTemplatedKind() != FunctionDecl::TK_NonTemplate) + return false; + + // Conditionally control the inlining of C++ standard library functions. + if (!Opts.mayInlineCXXStandardLibrary()) + if (Ctx.getSourceManager().isInSystemHeader(FD->getLocation())) + if (IsInStdNamespace(FD)) + return false; + + // Conditionally control the inlining of methods on objects that look + // like C++ containers. + if (!Opts.mayInlineCXXContainerCtorsAndDtors()) + if (!Ctx.getSourceManager().isFromMainFile(FD->getLocation())) + if (isContainerCtorOrDtor(Ctx, FD)) + return false; + } + } + + // It is possible that the CFG cannot be constructed. + // Be safe, and check if the CalleeCFG is valid. + const CFG *CalleeCFG = CalleeADC->getCFG(); + if (!CalleeCFG) + return false; + + // Do not inline large functions. + if (CalleeCFG->getNumBlockIDs() > Opts.getMaxInlinableSize()) + return false; + + // It is possible that the live variables analysis cannot be + // run. If so, bail out. + if (!CalleeADC->getAnalysis<RelaxedLiveVariables>()) + return false; + + return true; +} + +bool ExprEngine::shouldInlineCall(const CallEvent &Call, const Decl *D, + const ExplodedNode *Pred) { + if (!D) + return false; + + AnalysisManager &AMgr = getAnalysisManager(); + AnalyzerOptions &Opts = AMgr.options; + AnalysisDeclContextManager &ADCMgr = AMgr.getAnalysisDeclContextManager(); + AnalysisDeclContext *CalleeADC = ADCMgr.getContext(D); + + // The auto-synthesized bodies are essential to inline as they are + // usually small and commonly used. Note: we should do this check early on to + // ensure we always inline these calls. + if (CalleeADC->isBodyAutosynthesized()) + return true; + + if (!AMgr.shouldInlineCall()) + return false; + + // Check if this function has been marked as non-inlinable. + Optional<bool> MayInline = Engine.FunctionSummaries->mayInline(D); + if (MayInline.hasValue()) { + if (!MayInline.getValue()) + return false; + + } else { + // We haven't actually checked the static properties of this function yet. + // Do that now, and record our decision in the function summaries. + if (mayInlineDecl(Call, CalleeADC, Opts)) { + Engine.FunctionSummaries->markMayInline(D); + } else { + Engine.FunctionSummaries->markShouldNotInline(D); + return false; + } + } + + // Check if we should inline a call based on its kind. + // FIXME: this checks both static and dynamic properties of the call, which + // means we're redoing a bit of work that could be cached in the function + // summary. + CallInlinePolicy CIP = mayInlineCallKind(Call, Pred, Opts); + if (CIP != CIP_Allowed) { + if (CIP == CIP_DisallowedAlways) { + assert(!MayInline.hasValue() || MayInline.getValue()); + Engine.FunctionSummaries->markShouldNotInline(D); + } + return false; + } + + const CFG *CalleeCFG = CalleeADC->getCFG(); + + // Do not inline if recursive or we've reached max stack frame count. + bool IsRecursive = false; + unsigned StackDepth = 0; + examineStackFrames(D, Pred->getLocationContext(), IsRecursive, StackDepth); + if ((StackDepth >= Opts.InlineMaxStackDepth) && + ((CalleeCFG->getNumBlockIDs() > Opts.getAlwaysInlineSize()) + || IsRecursive)) + return false; + + // Do not inline large functions too many times. + if ((Engine.FunctionSummaries->getNumTimesInlined(D) > + Opts.getMaxTimesInlineLarge()) && + CalleeCFG->getNumBlockIDs() > 13) { + NumReachedInlineCountMax++; + return false; + } + + if (HowToInline == Inline_Minimal && + (CalleeCFG->getNumBlockIDs() > Opts.getAlwaysInlineSize() + || IsRecursive)) + return false; + + Engine.FunctionSummaries->bumpNumTimesInlined(D); + + return true; +} + +static bool isTrivialObjectAssignment(const CallEvent &Call) { + const CXXInstanceCall *ICall = dyn_cast<CXXInstanceCall>(&Call); + if (!ICall) + return false; + + const CXXMethodDecl *MD = dyn_cast_or_null<CXXMethodDecl>(ICall->getDecl()); + if (!MD) + return false; + if (!(MD->isCopyAssignmentOperator() || MD->isMoveAssignmentOperator())) + return false; + + return MD->isTrivial(); +} + void ExprEngine::defaultEvalCall(NodeBuilder &Bldr, ExplodedNode *Pred, const CallEvent &CallTemplate) { // Make sure we have the most recent state attached to the call. ProgramStateRef State = Pred->getState(); CallEventRef<> Call = CallTemplate.cloneWithState(State); - if (!getAnalysisManager().shouldInlineCall()) { - conservativeEvalCall(*Call, Bldr, Pred, State); + // Special-case trivial assignment operators. + if (isTrivialObjectAssignment(*Call)) { + performTrivialCopy(Bldr, Pred, *Call); return; } + // Try to inline the call. // The origin expression here is just used as a kind of checksum; // this should still be safe even for CallEvents that don't come from exprs. const Expr *E = Call->getOriginExpr(); - ProgramStateRef InlinedFailedState = getInlineFailedState(State, E); + ProgramStateRef InlinedFailedState = getInlineFailedState(State, E); if (InlinedFailedState) { // If we already tried once and failed, make sure we don't retry later. State = InlinedFailedState; } else { RuntimeDefinition RD = Call->getRuntimeDefinition(); const Decl *D = RD.getDecl(); - if (D) { + if (shouldInlineCall(*Call, D, Pred)) { if (RD.mayHaveOtherDefinitions()) { + AnalyzerOptions &Options = getAnalysisManager().options; + // Explore with and without inlining the call. - if (getAnalysisManager().options.IPAMode == DynamicDispatchBifurcate) { + if (Options.getIPAMode() == IPAK_DynamicDispatchBifurcate) { BifurcateCall(RD.getDispatchRegion(), *Call, D, Bldr, Pred); return; } // Don't inline if we're not in any dynamic dispatch mode. - if (getAnalysisManager().options.IPAMode != DynamicDispatch) { + if (Options.getIPAMode() != IPAK_DynamicDispatch) { conservativeEvalCall(*Call, Bldr, Pred, State); return; } diff --git a/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp b/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp index 51dda19b5315..d276d9244614 100644 --- a/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp @@ -103,8 +103,8 @@ void ExprEngine::VisitObjCForCollectionStmt(const ObjCForCollectionStmt *S, // Handle the case where the container has no elements. SVal FalseV = svalBuilder.makeTruthVal(0); ProgramStateRef noElems = state->BindExpr(S, LCtx, FalseV); - - if (loc::MemRegionVal *MV = dyn_cast<loc::MemRegionVal>(&elementV)) + + if (Optional<loc::MemRegionVal> MV = elementV.getAs<loc::MemRegionVal>()) if (const TypedValueRegion *R = dyn_cast<TypedValueRegion>(MV->getRegion())) { // FIXME: The proper thing to do is to really iterate over the @@ -161,8 +161,9 @@ void ExprEngine::VisitObjCMessage(const ObjCMessageExpr *ME, SVal recVal = UpdatedMsg->getReceiverSVal(); if (!recVal.isUndef()) { // Bifurcate the state into nil and non-nil ones. - DefinedOrUnknownSVal receiverVal = cast<DefinedOrUnknownSVal>(recVal); - + DefinedOrUnknownSVal receiverVal = + recVal.castAs<DefinedOrUnknownSVal>(); + ProgramStateRef notNilState, nilState; llvm::tie(notNilState, nilState) = State->assume(receiverVal); @@ -179,13 +180,13 @@ void ExprEngine::VisitObjCMessage(const ObjCMessageExpr *ME, if (ObjCNoRet.isImplicitNoReturn(ME)) { // If we raise an exception, for now treat it as a sink. // Eventually we will want to handle exceptions properly. - Bldr.generateSink(currStmt, Pred, State); + Bldr.generateSink(ME, Pred, State); continue; } // Generate a transition to non-Nil state. if (notNilState != State) { - Pred = Bldr.generateNode(currStmt, Pred, notNilState); + Pred = Bldr.generateNode(ME, Pred, notNilState); assert(Pred && "Should have cached out already!"); } } @@ -195,7 +196,7 @@ void ExprEngine::VisitObjCMessage(const ObjCMessageExpr *ME, if (ObjCNoRet.isImplicitNoReturn(ME)) { // If we raise an exception, for now treat it as a sink. // Eventually we will want to handle exceptions properly. - Bldr.generateSink(currStmt, Pred, Pred->getState()); + Bldr.generateSink(ME, Pred, Pred->getState()); continue; } } diff --git a/lib/StaticAnalyzer/Core/FunctionSummary.cpp b/lib/StaticAnalyzer/Core/FunctionSummary.cpp index c227aac2b4c7..c21735b8b882 100644 --- a/lib/StaticAnalyzer/Core/FunctionSummary.cpp +++ b/lib/StaticAnalyzer/Core/FunctionSummary.cpp @@ -1,4 +1,4 @@ -//== FunctionSummary.h - Stores summaries of functions. ------------*- C++ -*-// +//== FunctionSummary.cpp - Stores summaries of functions. ----------*- C++ -*-// // // The LLVM Compiler Infrastructure // @@ -7,7 +7,7 @@ // //===----------------------------------------------------------------------===// // -// This file defines a summary of a function gathered/used by static analyzes. +// This file defines a summary of a function gathered/used by static analysis. // //===----------------------------------------------------------------------===// @@ -15,16 +15,10 @@ 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; + Total += I->second.TotalBasicBlocks; } return Total; } @@ -32,7 +26,7 @@ unsigned FunctionSummariesTy::getTotalNumBasicBlocks() { unsigned FunctionSummariesTy::getTotalNumVisitedBasicBlocks() { unsigned Total = 0; for (MapTy::iterator I = Map.begin(), E = Map.end(); I != E; ++I) { - Total += I->second->VisitedBasicBlocks.count(); + Total += I->second.VisitedBasicBlocks.count(); } return Total; } diff --git a/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp b/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp index fd875f66d2db..73426da2b4df 100644 --- a/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp +++ b/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp @@ -12,19 +12,19 @@ //===----------------------------------------------------------------------===// #include "clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h" -#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" #include "clang/AST/ASTContext.h" #include "clang/AST/Decl.h" -#include "clang/Basic/SourceManager.h" #include "clang/Basic/FileManager.h" -#include "clang/Rewrite/Core/Rewriter.h" -#include "clang/Rewrite/Core/HTMLRewrite.h" +#include "clang/Basic/SourceManager.h" #include "clang/Lex/Lexer.h" #include "clang/Lex/Preprocessor.h" +#include "clang/Rewrite/Core/HTMLRewrite.h" +#include "clang/Rewrite/Core/Rewriter.h" +#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/MemoryBuffer.h" -#include "llvm/Support/raw_ostream.h" #include "llvm/Support/Path.h" +#include "llvm/Support/raw_ostream.h" using namespace clang; using namespace ento; @@ -76,7 +76,8 @@ HTMLDiagnostics::HTMLDiagnostics(const std::string& prefix, FilePrefix.appendComponent("report"); } -void ento::createHTMLDiagnosticConsumer(PathDiagnosticConsumers &C, +void ento::createHTMLDiagnosticConsumer(AnalyzerOptions &AnalyzerOpts, + PathDiagnosticConsumers &C, const std::string& prefix, const Preprocessor &PP) { C.push_back(new HTMLDiagnostics(prefix, PP)); diff --git a/lib/StaticAnalyzer/Core/MemRegion.cpp b/lib/StaticAnalyzer/Core/MemRegion.cpp index fab10cfd3d04..b3a1e65b19a5 100644 --- a/lib/StaticAnalyzer/Core/MemRegion.cpp +++ b/lib/StaticAnalyzer/Core/MemRegion.cpp @@ -14,13 +14,14 @@ //===----------------------------------------------------------------------===// #include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h" -#include "clang/Analysis/AnalysisContext.h" -#include "clang/Analysis/Support/BumpVector.h" +#include "clang/AST/Attr.h" #include "clang/AST/CharUnits.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/RecordLayout.h" +#include "clang/Analysis/AnalysisContext.h" +#include "clang/Analysis/Support/BumpVector.h" #include "clang/Basic/SourceManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h" #include "llvm/Support/raw_ostream.h" using namespace clang; @@ -194,6 +195,10 @@ DefinedOrUnknownSVal TypedValueRegion::getExtent(SValBuilder &svalBuilder) const } DefinedOrUnknownSVal FieldRegion::getExtent(SValBuilder &svalBuilder) const { + // Force callers to deal with bitfields explicitly. + if (getDecl()->isBitField()) + return UnknownVal(); + DefinedOrUnknownSVal Extent = DeclRegion::getExtent(svalBuilder); // A zero-length array at the end of a struct often stands for dynamically- @@ -233,7 +238,7 @@ QualType ObjCIvarRegion::getValueType() const { } QualType CXXBaseObjectRegion::getValueType() const { - return QualType(decl->getTypeForDecl(), 0); + return QualType(getDecl()->getTypeForDecl(), 0); } //===----------------------------------------------------------------------===// @@ -272,10 +277,11 @@ void ObjCStringRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, void AllocaRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, const Expr *Ex, unsigned cnt, - const MemRegion *) { + const MemRegion *superRegion) { ID.AddInteger((unsigned) AllocaRegionKind); ID.AddPointer(Ex); ID.AddInteger(cnt); + ID.AddPointer(superRegion); } void AllocaRegion::Profile(llvm::FoldingSetNodeID& ID) const { @@ -400,14 +406,16 @@ void CXXTempObjectRegion::Profile(llvm::FoldingSetNodeID &ID) const { } void CXXBaseObjectRegion::ProfileRegion(llvm::FoldingSetNodeID &ID, - const CXXRecordDecl *decl, - const MemRegion *sReg) { - ID.AddPointer(decl); - ID.AddPointer(sReg); + const CXXRecordDecl *RD, + bool IsVirtual, + const MemRegion *SReg) { + ID.AddPointer(RD); + ID.AddBoolean(IsVirtual); + ID.AddPointer(SReg); } void CXXBaseObjectRegion::Profile(llvm::FoldingSetNodeID &ID) const { - ProfileRegion(ID, decl, superRegion); + ProfileRegion(ID, getDecl(), isVirtual(), superRegion); } //===----------------------------------------------------------------------===// @@ -470,7 +478,7 @@ void CXXTempObjectRegion::dumpToStream(raw_ostream &os) const { } void CXXBaseObjectRegion::dumpToStream(raw_ostream &os) const { - os << "base{" << superRegion << ',' << decl->getName() << '}'; + os << "base{" << superRegion << ',' << getDecl()->getName() << '}'; } void CXXThisRegion::dumpToStream(raw_ostream &os) const { @@ -562,6 +570,14 @@ void VarRegion::printPretty(raw_ostream &os) const { os << getDecl()->getName(); } +bool ObjCIvarRegion::canPrintPretty() const { + return true; +} + +void ObjCIvarRegion::printPretty(raw_ostream &os) const { + os << getDecl()->getName(); +} + bool FieldRegion::canPrintPretty() const { return superRegion->canPrintPretty(); } @@ -883,41 +899,50 @@ MemRegionManager::getCXXTempObjectRegion(Expr const *E, return getSubRegion<CXXTempObjectRegion>(E, getStackLocalsRegion(SFC)); } -const CXXBaseObjectRegion * -MemRegionManager::getCXXBaseObjectRegion(const CXXRecordDecl *decl, - const MemRegion *superRegion) { - // Check that the base class is actually a direct base of this region. - if (const TypedValueRegion *TVR = dyn_cast<TypedValueRegion>(superRegion)) { - if (const CXXRecordDecl *Class = TVR->getValueType()->getAsCXXRecordDecl()){ - if (Class->isVirtuallyDerivedFrom(decl)) { - // Virtual base regions should not be layered, since the layout rules - // are different. - while (const CXXBaseObjectRegion *Base = - dyn_cast<CXXBaseObjectRegion>(superRegion)) { - superRegion = Base->getSuperRegion(); - } - assert(superRegion && !isa<MemSpaceRegion>(superRegion)); +/// Checks whether \p BaseClass is a valid virtual or direct non-virtual base +/// class of the type of \p Super. +static bool isValidBaseClass(const CXXRecordDecl *BaseClass, + const TypedValueRegion *Super, + bool IsVirtual) { + BaseClass = BaseClass->getCanonicalDecl(); - } else { - // Non-virtual bases should always be direct bases. -#ifndef NDEBUG - bool FoundBase = false; - for (CXXRecordDecl::base_class_const_iterator I = Class->bases_begin(), - E = Class->bases_end(); - I != E; ++I) { - if (I->getType()->getAsCXXRecordDecl() == decl) { - FoundBase = true; - break; - } - } + const CXXRecordDecl *Class = Super->getValueType()->getAsCXXRecordDecl(); + if (!Class) + return true; + + if (IsVirtual) + return Class->isVirtuallyDerivedFrom(BaseClass); + + for (CXXRecordDecl::base_class_const_iterator I = Class->bases_begin(), + E = Class->bases_end(); + I != E; ++I) { + if (I->getType()->getAsCXXRecordDecl()->getCanonicalDecl() == BaseClass) + return true; + } + + return false; +} - assert(FoundBase && "Not a direct base class of this region"); -#endif +const CXXBaseObjectRegion * +MemRegionManager::getCXXBaseObjectRegion(const CXXRecordDecl *RD, + const MemRegion *Super, + bool IsVirtual) { + if (isa<TypedValueRegion>(Super)) { + assert(isValidBaseClass(RD, dyn_cast<TypedValueRegion>(Super), IsVirtual)); + (void)isValidBaseClass; + + if (IsVirtual) { + // Virtual base regions should not be layered, since the layout rules + // are different. + while (const CXXBaseObjectRegion *Base = + dyn_cast<CXXBaseObjectRegion>(Super)) { + Super = Base->getSuperRegion(); } + assert(Super && !isa<MemSpaceRegion>(Super)); } } - return getSubRegion<CXXBaseObjectRegion>(decl, superRegion); + return getSubRegion<CXXBaseObjectRegion>(RD, IsVirtual, Super); } const CXXThisRegion* @@ -1042,7 +1067,7 @@ RegionRawOffset ElementRegion::getAsArrayOffset() const { // FIXME: generalize to symbolic offsets. SVal index = ER->getIndex(); - if (nonloc::ConcreteInt *CI = dyn_cast<nonloc::ConcreteInt>(&index)) { + if (Optional<nonloc::ConcreteInt> CI = index.getAs<nonloc::ConcreteInt>()) { // Update the offset. int64_t i = CI->getValue().getSExtValue(); @@ -1071,6 +1096,23 @@ RegionRawOffset ElementRegion::getAsArrayOffset() const { return RegionRawOffset(superR, offset); } + +/// Returns true if \p Base is an immediate base class of \p Child +static bool isImmediateBase(const CXXRecordDecl *Child, + const CXXRecordDecl *Base) { + // Note that we do NOT canonicalize the base class here, because + // ASTRecordLayout doesn't either. If that leads us down the wrong path, + // so be it; at least we won't crash. + for (CXXRecordDecl::base_class_const_iterator I = Child->bases_begin(), + E = Child->bases_end(); + I != E; ++I) { + if (I->getType()->getAsCXXRecordDecl() == Base) + return true; + } + + return false; +} + RegionOffset MemRegion::getAsOffset() const { const MemRegion *R = this; const MemRegion *SymbolicOffsetBase = 0; @@ -1078,16 +1120,37 @@ RegionOffset MemRegion::getAsOffset() const { while (1) { switch (R->getKind()) { - default: - return RegionOffset(R, RegionOffset::Symbolic); + case GenericMemSpaceRegionKind: + case StackLocalsSpaceRegionKind: + case StackArgumentsSpaceRegionKind: + case HeapSpaceRegionKind: + case UnknownSpaceRegionKind: + case StaticGlobalSpaceRegionKind: + case GlobalInternalSpaceRegionKind: + case GlobalSystemSpaceRegionKind: + case GlobalImmutableSpaceRegionKind: + // Stores can bind directly to a region space to set a default value. + assert(Offset == 0 && !SymbolicOffsetBase); + goto Finish; + + case FunctionTextRegionKind: + case BlockTextRegionKind: + case BlockDataRegionKind: + // These will never have bindings, but may end up having values requested + // if the user does some strange casting. + if (Offset != 0) + SymbolicOffsetBase = R; + goto Finish; case SymbolicRegionKind: case AllocaRegionKind: case CompoundLiteralRegionKind: case CXXThisRegionKind: case StringRegionKind: + case ObjCStringRegionKind: case VarRegionKind: case CXXTempObjectRegionKind: + // Usual base regions. goto Finish; case ObjCIvarRegionKind: @@ -1103,6 +1166,7 @@ RegionOffset MemRegion::getAsOffset() const { R = BOR->getSuperRegion(); QualType Ty; + bool RootIsSymbolic = false; if (const TypedValueRegion *TVR = dyn_cast<TypedValueRegion>(R)) { Ty = TVR->getDesugaredValueType(getContext()); } else if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(R)) { @@ -1110,6 +1174,7 @@ RegionOffset MemRegion::getAsOffset() const { // Pretend the type of the symbol is the true dynamic type. // (This will at least be self-consistent for the life of the symbol.) Ty = SR->getSymbol()->getType()->getPointeeType(); + RootIsSymbolic = true; } const CXXRecordDecl *Child = Ty->getAsCXXRecordDecl(); @@ -1118,19 +1183,30 @@ RegionOffset MemRegion::getAsOffset() const { SymbolicOffsetBase = R; } + if (RootIsSymbolic) { + // Base layers on symbolic regions may not be type-correct. + // Double-check the inheritance here, and revert to a symbolic offset + // if it's invalid (e.g. due to a reinterpret_cast). + if (BOR->isVirtual()) { + if (!Child->isVirtuallyDerivedFrom(BOR->getDecl())) + SymbolicOffsetBase = R; + } else { + if (!isImmediateBase(Child, BOR->getDecl())) + SymbolicOffsetBase = R; + } + } + // Don't bother calculating precise offsets if we already have a // symbolic offset somewhere in the chain. if (SymbolicOffsetBase) continue; - const ASTRecordLayout &Layout = getContext().getASTRecordLayout(Child); - CharUnits BaseOffset; - const CXXRecordDecl *Base = BOR->getDecl(); - if (Child->isVirtuallyDerivedFrom(Base)) - BaseOffset = Layout.getVBaseClassOffset(Base); + const ASTRecordLayout &Layout = getContext().getASTRecordLayout(Child); + if (BOR->isVirtual()) + BaseOffset = Layout.getVBaseClassOffset(BOR->getDecl()); else - BaseOffset = Layout.getBaseClassOffset(Base); + BaseOffset = Layout.getBaseClassOffset(BOR->getDecl()); // The base offset is in chars, not in bits. Offset += BaseOffset.getQuantity() * getContext().getCharWidth(); @@ -1148,7 +1224,8 @@ RegionOffset MemRegion::getAsOffset() const { } SVal Index = ER->getIndex(); - if (const nonloc::ConcreteInt *CI=dyn_cast<nonloc::ConcreteInt>(&Index)) { + if (Optional<nonloc::ConcreteInt> CI = + Index.getAs<nonloc::ConcreteInt>()) { // Don't bother calculating precise offsets if we already have a // symbolic offset somewhere in the chain. if (SymbolicOffsetBase) @@ -1207,6 +1284,29 @@ RegionOffset MemRegion::getAsOffset() const { // BlockDataRegion //===----------------------------------------------------------------------===// +std::pair<const VarRegion *, const VarRegion *> +BlockDataRegion::getCaptureRegions(const VarDecl *VD) { + MemRegionManager &MemMgr = *getMemRegionManager(); + const VarRegion *VR = 0; + const VarRegion *OriginalVR = 0; + + if (!VD->getAttr<BlocksAttr>() && VD->hasLocalStorage()) { + VR = MemMgr.getVarRegion(VD, this); + OriginalVR = MemMgr.getVarRegion(VD, LC); + } + else { + if (LC) { + VR = MemMgr.getVarRegion(VD, LC); + OriginalVR = VR; + } + else { + VR = MemMgr.getVarRegion(VD, MemMgr.getUnknownRegion()); + OriginalVR = MemMgr.getVarRegion(VD, LC); + } + } + return std::make_pair(VR, OriginalVR); +} + void BlockDataRegion::LazyInitializeReferencedVars() { if (ReferencedVars) return; @@ -1231,25 +1331,9 @@ void BlockDataRegion::LazyInitializeReferencedVars() { new (BVOriginal) VarVec(BC, E - I); for ( ; I != E; ++I) { - const VarDecl *VD = *I; const VarRegion *VR = 0; const VarRegion *OriginalVR = 0; - - if (!VD->getAttr<BlocksAttr>() && VD->hasLocalStorage()) { - VR = MemMgr.getVarRegion(VD, this); - OriginalVR = MemMgr.getVarRegion(VD, LC); - } - else { - if (LC) { - VR = MemMgr.getVarRegion(VD, LC); - OriginalVR = VR; - } - else { - VR = MemMgr.getVarRegion(VD, MemMgr.getUnknownRegion()); - OriginalVR = MemMgr.getVarRegion(VD, LC); - } - } - + llvm::tie(VR, OriginalVR) = getCaptureRegions(*I); assert(VR); assert(OriginalVR); BV->push_back(VR, BC); @@ -1293,3 +1377,13 @@ BlockDataRegion::referenced_vars_end() const { return BlockDataRegion::referenced_vars_iterator(Vec->end(), VecOriginal->end()); } + +const VarRegion *BlockDataRegion::getOriginalRegion(const VarRegion *R) const { + for (referenced_vars_iterator I = referenced_vars_begin(), + E = referenced_vars_end(); + I != E; ++I) { + if (I.getCapturedRegion() == R) + return I.getOriginalRegion(); + } + return 0; +} diff --git a/lib/StaticAnalyzer/Core/PathDiagnostic.cpp b/lib/StaticAnalyzer/Core/PathDiagnostic.cpp index 0f48d1e1c798..7c0fb14a5c82 100644 --- a/lib/StaticAnalyzer/Core/PathDiagnostic.cpp +++ b/lib/StaticAnalyzer/Core/PathDiagnostic.cpp @@ -12,16 +12,17 @@ //===----------------------------------------------------------------------===// #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/DeclCXX.h" #include "clang/AST/DeclObjC.h" +#include "clang/AST/Expr.h" #include "clang/AST/ParentMap.h" #include "clang/AST/StmtCXX.h" +#include "clang/Basic/SourceManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringExtras.h" +#include "llvm/Support/raw_ostream.h" using namespace clang; using namespace ento; @@ -106,12 +107,16 @@ PathDiagnostic::~PathDiagnostic() {} PathDiagnostic::PathDiagnostic(const Decl *declWithIssue, StringRef bugtype, StringRef verboseDesc, - StringRef shortDesc, StringRef category) + StringRef shortDesc, StringRef category, + PathDiagnosticLocation LocationToUnique, + const Decl *DeclToUnique) : DeclWithIssue(declWithIssue), BugType(StripTrailingDots(bugtype)), VerboseDesc(StripTrailingDots(verboseDesc)), ShortDesc(StripTrailingDots(shortDesc)), Category(StripTrailingDots(category)), + UniqueingLoc(LocationToUnique), + UniqueingDecl(DeclToUnique), path(pathImpl) {} void PathDiagnosticConsumer::anchor() { } @@ -125,7 +130,7 @@ PathDiagnosticConsumer::~PathDiagnosticConsumer() { } void PathDiagnosticConsumer::HandlePathDiagnostic(PathDiagnostic *D) { - llvm::OwningPtr<PathDiagnostic> OwningD(D); + OwningPtr<PathDiagnostic> OwningD(D); if (!D || D->path.empty()) return; @@ -141,7 +146,7 @@ void PathDiagnosticConsumer::HandlePathDiagnostic(PathDiagnostic *D) { // 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; + SmallVector<const PathPieces *, 5> WorkList; WorkList.push_back(&D->path); while (!WorkList.empty()) { @@ -208,9 +213,8 @@ void PathDiagnosticConsumer::HandlePathDiagnostic(PathDiagnostic *D) { Diags.InsertNode(OwningD.take()); } -static llvm::Optional<bool> comparePath(const PathPieces &X, - const PathPieces &Y); -static llvm::Optional<bool> +static Optional<bool> comparePath(const PathPieces &X, const PathPieces &Y); +static Optional<bool> compareControlFlow(const PathDiagnosticControlFlowPiece &X, const PathDiagnosticControlFlowPiece &Y) { FullSourceLoc XSL = X.getStartLocation().asLocation(); @@ -221,18 +225,16 @@ compareControlFlow(const PathDiagnosticControlFlowPiece &X, FullSourceLoc YEL = Y.getEndLocation().asLocation(); if (XEL != YEL) return XEL.isBeforeInTranslationUnitThan(YEL); - return llvm::Optional<bool>(); + return None; } -static llvm::Optional<bool> -compareMacro(const PathDiagnosticMacroPiece &X, - const PathDiagnosticMacroPiece &Y) { +static Optional<bool> compareMacro(const PathDiagnosticMacroPiece &X, + const PathDiagnosticMacroPiece &Y) { return comparePath(X.subPieces, Y.subPieces); } -static llvm::Optional<bool> -compareCall(const PathDiagnosticCallPiece &X, - const PathDiagnosticCallPiece &Y) { +static Optional<bool> compareCall(const PathDiagnosticCallPiece &X, + const PathDiagnosticCallPiece &Y) { FullSourceLoc X_CEL = X.callEnter.asLocation(); FullSourceLoc Y_CEL = Y.callEnter.asLocation(); if (X_CEL != Y_CEL) @@ -248,8 +250,8 @@ compareCall(const PathDiagnosticCallPiece &X, return comparePath(X.path, Y.path); } -static llvm::Optional<bool> comparePiece(const PathDiagnosticPiece &X, - const PathDiagnosticPiece &Y) { +static Optional<bool> comparePiece(const PathDiagnosticPiece &X, + const PathDiagnosticPiece &Y) { if (X.getKind() != Y.getKind()) return X.getKind() < Y.getKind(); @@ -281,7 +283,7 @@ static llvm::Optional<bool> comparePiece(const PathDiagnosticPiece &X, return compareControlFlow(cast<PathDiagnosticControlFlowPiece>(X), cast<PathDiagnosticControlFlowPiece>(Y)); case clang::ento::PathDiagnosticPiece::Event: - return llvm::Optional<bool>(); + return None; case clang::ento::PathDiagnosticPiece::Macro: return compareMacro(cast<PathDiagnosticMacroPiece>(X), cast<PathDiagnosticMacroPiece>(Y)); @@ -292,16 +294,15 @@ static llvm::Optional<bool> comparePiece(const PathDiagnosticPiece &X, llvm_unreachable("all cases handled"); } -static llvm::Optional<bool> comparePath(const PathPieces &X, - const PathPieces &Y) { +static Optional<bool> comparePath(const PathPieces &X, const PathPieces &Y) { if (X.size() != Y.size()) return X.size() < Y.size(); for (unsigned i = 0, n = X.size(); i != n; ++i) { - llvm::Optional<bool> b = comparePiece(*X[i], *Y[i]); + Optional<bool> b = comparePiece(*X[i], *Y[i]); if (b.hasValue()) return b.getValue(); } - return llvm::Optional<bool>(); + return None; } static bool compare(const PathDiagnostic &X, const PathDiagnostic &Y) { @@ -339,7 +340,7 @@ static bool compare(const PathDiagnostic &X, const PathDiagnostic &Y) { if (*XI != *YI) return (*XI) < (*YI); } - llvm::Optional<bool> b = comparePath(X.path, Y.path); + Optional<bool> b = comparePath(X.path, Y.path); assert(b.hasValue()); return b.getValue(); } @@ -475,18 +476,16 @@ getLocationForCaller(const StackFrameContext *SFC, CFGElement Source = Block[SFC->getIndex()]; switch (Source.getKind()) { - case CFGElement::Invalid: - llvm_unreachable("Invalid CFGElement"); case CFGElement::Statement: - return PathDiagnosticLocation(cast<CFGStmt>(Source).getStmt(), + return PathDiagnosticLocation(Source.castAs<CFGStmt>().getStmt(), SM, CallerCtx); case CFGElement::Initializer: { - const CFGInitializer &Init = cast<CFGInitializer>(Source); + const CFGInitializer &Init = Source.castAs<CFGInitializer>(); return PathDiagnosticLocation(Init.getInitializer()->getInit(), SM, CallerCtx); } case CFGElement::AutomaticObjectDtor: { - const CFGAutomaticObjDtor &Dtor = cast<CFGAutomaticObjDtor>(Source); + const CFGAutomaticObjDtor &Dtor = Source.castAs<CFGAutomaticObjDtor>(); return PathDiagnosticLocation::createEnd(Dtor.getTriggerStmt(), SM, CallerCtx); } @@ -582,27 +581,27 @@ PathDiagnosticLocation const SourceManager &SMng) { const Stmt* S = 0; - if (const BlockEdge *BE = dyn_cast<BlockEdge>(&P)) { + if (Optional<BlockEdge> BE = P.getAs<BlockEdge>()) { const CFGBlock *BSrc = BE->getSrc(); S = BSrc->getTerminatorCondition(); - } - else if (const StmtPoint *SP = dyn_cast<StmtPoint>(&P)) { + } else if (Optional<StmtPoint> SP = P.getAs<StmtPoint>()) { S = SP->getStmt(); - } - else if (const PostImplicitCall *PIE = dyn_cast<PostImplicitCall>(&P)) { + if (P.getAs<PostStmtPurgeDeadSymbols>()) + return PathDiagnosticLocation::createEnd(S, SMng, P.getLocationContext()); + } else if (Optional<PostInitializer> PIP = P.getAs<PostInitializer>()) { + return PathDiagnosticLocation(PIP->getInitializer()->getSourceLocation(), + SMng); + } else if (Optional<PostImplicitCall> PIE = P.getAs<PostImplicitCall>()) { return PathDiagnosticLocation(PIE->getLocation(), SMng); - } - else if (const CallEnter *CE = dyn_cast<CallEnter>(&P)) { + } else if (Optional<CallEnter> CE = P.getAs<CallEnter>()) { return getLocationForCaller(CE->getCalleeContext(), CE->getLocationContext(), SMng); - } - else if (const CallExitEnd *CEE = dyn_cast<CallExitEnd>(&P)) { + } else if (Optional<CallExitEnd> CEE = P.getAs<CallExitEnd>()) { return getLocationForCaller(CEE->getCalleeContext(), CEE->getLocationContext(), SMng); - } - else { + } else { llvm_unreachable("Unexpected ProgramPoint"); } @@ -619,12 +618,16 @@ PathDiagnosticLocation while (NI) { ProgramPoint P = NI->getLocation(); - if (const StmtPoint *PS = dyn_cast<StmtPoint>(&P)) + if (Optional<StmtPoint> PS = P.getAs<StmtPoint>()) { S = PS->getStmt(); - else if (const BlockEdge *BE = dyn_cast<BlockEdge>(&P)) + if (P.getAs<PostStmtPurgeDeadSymbols>()) + return PathDiagnosticLocation::createEnd(S, SM, + NI->getLocationContext()); + break; + } else if (Optional<BlockEdge> BE = P.getAs<BlockEdge>()) { S = BE->getSrc()->getTerminator(); - if (S) break; + } NI = NI->succ_empty() ? 0 : *(NI->succ_begin()); } @@ -777,48 +780,129 @@ void PathDiagnosticCallPiece::setCallee(const CallEnter &CE, callEnter = getLocationForCaller(CalleeCtx, CE.getLocationContext(), SM); } +static inline void describeClass(raw_ostream &Out, const CXXRecordDecl *D, + StringRef Prefix = StringRef()) { + if (!D->getIdentifier()) + return; + Out << Prefix << '\'' << *D << '\''; +} + +static bool describeCodeDecl(raw_ostream &Out, const Decl *D, + bool ExtendedDescription, + StringRef Prefix = StringRef()) { + if (!D) + return false; + + if (isa<BlockDecl>(D)) { + if (ExtendedDescription) + Out << Prefix << "anonymous block"; + return ExtendedDescription; + } + + if (const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(D)) { + Out << Prefix; + if (ExtendedDescription && !MD->isUserProvided()) { + if (MD->isExplicitlyDefaulted()) + Out << "defaulted "; + else + Out << "implicit "; + } + + if (const CXXConstructorDecl *CD = dyn_cast<CXXConstructorDecl>(MD)) { + if (CD->isDefaultConstructor()) + Out << "default "; + else if (CD->isCopyConstructor()) + Out << "copy "; + else if (CD->isMoveConstructor()) + Out << "move "; + + Out << "constructor"; + describeClass(Out, MD->getParent(), " for "); + + } else if (isa<CXXDestructorDecl>(MD)) { + if (!MD->isUserProvided()) { + Out << "destructor"; + describeClass(Out, MD->getParent(), " for "); + } else { + // Use ~Foo for explicitly-written destructors. + Out << "'" << *MD << "'"; + } + + } else if (MD->isCopyAssignmentOperator()) { + Out << "copy assignment operator"; + describeClass(Out, MD->getParent(), " for "); + + } else if (MD->isMoveAssignmentOperator()) { + Out << "move assignment operator"; + describeClass(Out, MD->getParent(), " for "); + + } else { + if (MD->getParent()->getIdentifier()) + Out << "'" << *MD->getParent() << "::" << *MD << "'"; + else + Out << "'" << *MD << "'"; + } + + return true; + } + + Out << Prefix << '\'' << cast<NamedDecl>(*D) << '\''; + return true; +} + 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); + + Out << "Calling "; + describeCodeDecl(Out, Callee, /*ExtendedDescription=*/true); + + assert(callEnter.asLocation().isValid()); + return new PathDiagnosticEventPiece(callEnter, Out.str()); } IntrusiveRefCntPtr<PathDiagnosticEventPiece> PathDiagnosticCallPiece::getCallEnterWithinCallerEvent() const { + if (!callEnterWithin.asLocation().isValid()) + return 0; + if (Callee->isImplicit()) + return 0; + if (const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(Callee)) + if (MD->isDefaulted()) + return 0; + 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); + + Out << "Entered call"; + describeCodeDecl(Out, Caller, /*ExtendedDescription=*/false, " from "); + + return new PathDiagnosticEventPiece(callEnterWithin, Out.str()); } IntrusiveRefCntPtr<PathDiagnosticEventPiece> PathDiagnosticCallPiece::getCallExitEvent() const { if (NoExit) return 0; + SmallString<256> buf; llvm::raw_svector_ostream Out(buf); - if (!CallStackMessage.empty()) + + 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"; + } else { + bool DidDescribe = describeCodeDecl(Out, Callee, + /*ExtendedDescription=*/false, + "Returning from "); + if (!DidDescribe) + Out << "Returning to caller"; + } + + assert(callReturn.asLocation().isValid()); return new PathDiagnosticEventPiece(callReturn, Out.str()); } @@ -910,11 +994,10 @@ StackHintGenerator::~StackHintGenerator() {} std::string StackHintGeneratorForSymbol::getMessage(const ExplodedNode *N){ ProgramPoint P = N->getLocation(); - const CallExitEnd *CExit = dyn_cast<CallExitEnd>(&P); - assert(CExit && "Stack Hints should be constructed at CallExitEnd points."); + CallExitEnd CExit = P.castAs<CallExitEnd>(); // FIXME: Use CallEvent to abstract this over all calls. - const Stmt *CallSite = CExit->getCalleeContext()->getCallSite(); + const Stmt *CallSite = CExit.getCalleeContext()->getCallSite(); const CallExpr *CE = dyn_cast_or_null<CallExpr>(CallSite); if (!CE) return ""; @@ -937,7 +1020,7 @@ std::string StackHintGeneratorForSymbol::getMessage(const ExplodedNode *N){ } // Check if the parameter is a pointer to the symbol. - if (const loc::MemRegionVal *Reg = dyn_cast<loc::MemRegionVal>(&SV)) { + if (Optional<loc::MemRegionVal> Reg = SV.getAs<loc::MemRegionVal>()) { SVal PSV = State->getSVal(Reg->getRegion()); SymbolRef AS = PSV.getAsLocSymbol(); if (AS == Sym) { diff --git a/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp b/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp index 17ef4cf571e8..7dcc088d18a6 100644 --- a/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp +++ b/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp @@ -11,16 +11,17 @@ // //===----------------------------------------------------------------------===// -#include "clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h" -#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" +#include "clang/StaticAnalyzer/Core/AnalyzerOptions.h" #include "clang/Basic/FileManager.h" #include "clang/Basic/SourceManager.h" #include "clang/Basic/Version.h" #include "clang/Lex/Preprocessor.h" -#include "llvm/Support/raw_ostream.h" -#include "llvm/Support/Casting.h" +#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" +#include "clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/SmallVector.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/raw_ostream.h" using namespace clang; using namespace ento; @@ -33,7 +34,9 @@ namespace { const LangOptions &LangOpts; const bool SupportsCrossFileDiagnostics; public: - PlistDiagnostics(const std::string& prefix, const LangOptions &LangOpts, + PlistDiagnostics(AnalyzerOptions &AnalyzerOpts, + const std::string& prefix, + const LangOptions &LangOpts, bool supportsMultipleFiles); virtual ~PlistDiagnostics() {} @@ -54,22 +57,28 @@ namespace { }; } // end anonymous namespace -PlistDiagnostics::PlistDiagnostics(const std::string& output, +PlistDiagnostics::PlistDiagnostics(AnalyzerOptions &AnalyzerOpts, + const std::string& output, const LangOptions &LO, bool supportsMultipleFiles) - : OutputFile(output), LangOpts(LO), + : OutputFile(output), + LangOpts(LO), SupportsCrossFileDiagnostics(supportsMultipleFiles) {} -void ento::createPlistDiagnosticConsumer(PathDiagnosticConsumers &C, +void ento::createPlistDiagnosticConsumer(AnalyzerOptions &AnalyzerOpts, + PathDiagnosticConsumers &C, const std::string& s, const Preprocessor &PP) { - C.push_back(new PlistDiagnostics(s, PP.getLangOpts(), false)); + C.push_back(new PlistDiagnostics(AnalyzerOpts, s, + PP.getLangOpts(), false)); } -void ento::createPlistMultiFileDiagnosticConsumer(PathDiagnosticConsumers &C, +void ento::createPlistMultiFileDiagnosticConsumer(AnalyzerOptions &AnalyzerOpts, + PathDiagnosticConsumers &C, const std::string &s, const Preprocessor &PP) { - C.push_back(new PlistDiagnostics(s, PP.getLangOpts(), true)); + C.push_back(new PlistDiagnostics(AnalyzerOpts, s, + PP.getLangOpts(), true)); } static void AddFID(FIDMap &FIDs, SmallVectorImpl<FileID> &V, @@ -360,7 +369,7 @@ void PlistDiagnostics::FlushDiagnosticsImpl( const PathDiagnostic *D = *DI; - llvm::SmallVector<const PathPieces *, 5> WorkList; + SmallVector<const PathPieces *, 5> WorkList; WorkList.push_back(&D->path); while (!WorkList.empty()) { @@ -486,12 +495,32 @@ void PlistDiagnostics::FlushDiagnosticsImpl( // Output the bug hash for issue unique-ing. Currently, it's just an // offset from the beginning of the function. if (const Stmt *Body = DeclWithIssue->getBody()) { - FullSourceLoc Loc(SM->getExpansionLoc(D->getLocation().asLocation()), + + // If the bug uniqueing location exists, use it for the hash. + // For example, this ensures that two leaks reported on the same line + // will have different issue_hashes and that the hash will identify + // the leak location even after code is added between the allocation + // site and the end of scope (leak report location). + PathDiagnosticLocation UPDLoc = D->getUniqueingLoc(); + if (UPDLoc.isValid()) { + FullSourceLoc UL(SM->getExpansionLoc(UPDLoc.asLocation()), + *SM); + FullSourceLoc UFunL(SM->getExpansionLoc( + D->getUniqueingDecl()->getBody()->getLocStart()), *SM); + o << " <key>issue_hash</key><string>" + << UL.getExpansionLineNumber() - UFunL.getExpansionLineNumber() + << "</string>\n"; + + // Otherwise, use the location on which the bug is reported. + } else { + FullSourceLoc L(SM->getExpansionLoc(D->getLocation().asLocation()), *SM); - FullSourceLoc FunLoc(SM->getExpansionLoc(Body->getLocStart()), *SM); - o << " <key>issue_hash</key><integer>" - << Loc.getExpansionLineNumber() - FunLoc.getExpansionLineNumber() - << "</integer>\n"; + FullSourceLoc FunL(SM->getExpansionLoc(Body->getLocStart()), *SM); + o << " <key>issue_hash</key><string>" + << L.getExpansionLineNumber() - FunL.getExpansionLineNumber() + << "</string>\n"; + } + } } } diff --git a/lib/StaticAnalyzer/Core/ProgramState.cpp b/lib/StaticAnalyzer/Core/ProgramState.cpp index b49a11e64214..bff2242925e5 100644 --- a/lib/StaticAnalyzer/Core/ProgramState.cpp +++ b/lib/StaticAnalyzer/Core/ProgramState.cpp @@ -11,10 +11,10 @@ // //===----------------------------------------------------------------------===// +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" #include "clang/Analysis/CFG.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #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" @@ -132,7 +132,7 @@ ProgramStateRef ProgramState::bindLoc(Loc LV, SVal V, bool notifyChanges) const ProgramStateRef ProgramState::bindDefault(SVal loc, SVal V) const { ProgramStateManager &Mgr = getStateManager(); - const MemRegion *R = cast<loc::MemRegionVal>(loc).getRegion(); + const MemRegion *R = loc.castAs<loc::MemRegionVal>().getRegion(); const StoreRef &newStore = Mgr.StoreMgr->BindDefault(getStore(), R, V); ProgramStateRef new_state = makeWithStore(newStore); return Mgr.getOwningEngine() ? @@ -140,46 +140,108 @@ ProgramStateRef ProgramState::bindDefault(SVal loc, SVal V) const { new_state; } +typedef ArrayRef<const MemRegion *> RegionList; +typedef ArrayRef<SVal> ValueList; + ProgramStateRef -ProgramState::invalidateRegions(ArrayRef<const MemRegion *> Regions, +ProgramState::invalidateRegions(RegionList Regions, const Expr *E, unsigned Count, const LocationContext *LCtx, - StoreManager::InvalidatedSymbols *IS, - const CallEvent *Call) const { + bool CausedByPointerEscape, + InvalidatedSymbols *IS, + const CallEvent *Call, + RegionList ConstRegions) const { + SmallVector<SVal, 8> Values; + for (RegionList::const_iterator I = Regions.begin(), + End = Regions.end(); I != End; ++I) + Values.push_back(loc::MemRegionVal(*I)); + + SmallVector<SVal, 8> ConstValues; + for (RegionList::const_iterator I = ConstRegions.begin(), + End = ConstRegions.end(); I != End; ++I) + ConstValues.push_back(loc::MemRegionVal(*I)); + if (!IS) { - StoreManager::InvalidatedSymbols invalidated; - return invalidateRegionsImpl(Regions, E, Count, LCtx, - invalidated, Call); + InvalidatedSymbols invalidated; + return invalidateRegionsImpl(Values, E, Count, LCtx, + CausedByPointerEscape, + invalidated, Call, ConstValues); } - return invalidateRegionsImpl(Regions, E, Count, LCtx, *IS, Call); + return invalidateRegionsImpl(Values, E, Count, LCtx, CausedByPointerEscape, + *IS, Call, ConstValues); } -ProgramStateRef -ProgramState::invalidateRegionsImpl(ArrayRef<const MemRegion *> Regions, +ProgramStateRef +ProgramState::invalidateRegions(ValueList Values, + const Expr *E, unsigned Count, + const LocationContext *LCtx, + bool CausedByPointerEscape, + InvalidatedSymbols *IS, + const CallEvent *Call, + ValueList ConstValues) const { + if (!IS) { + InvalidatedSymbols invalidated; + return invalidateRegionsImpl(Values, E, Count, LCtx, + CausedByPointerEscape, + invalidated, Call, ConstValues); + } + return invalidateRegionsImpl(Values, E, Count, LCtx, CausedByPointerEscape, + *IS, Call, ConstValues); +} + +ProgramStateRef +ProgramState::invalidateRegionsImpl(ValueList Values, const Expr *E, unsigned Count, const LocationContext *LCtx, - StoreManager::InvalidatedSymbols &IS, - const CallEvent *Call) const { + bool CausedByPointerEscape, + InvalidatedSymbols &IS, + const CallEvent *Call, + ValueList ConstValues) const { ProgramStateManager &Mgr = getStateManager(); SubEngine* Eng = Mgr.getOwningEngine(); - - if (Eng && Eng->wantsRegionChangeUpdate(this)) { + InvalidatedSymbols ConstIS; + + if (Eng) { + StoreManager::InvalidatedRegions TopLevelInvalidated; + StoreManager::InvalidatedRegions TopLevelConstInvalidated; StoreManager::InvalidatedRegions Invalidated; const StoreRef &newStore - = Mgr.StoreMgr->invalidateRegions(getStore(), Regions, E, Count, LCtx, IS, - Call, &Invalidated); + = Mgr.StoreMgr->invalidateRegions(getStore(), Values, ConstValues, + E, Count, LCtx, Call, + IS, ConstIS, + &TopLevelInvalidated, + &TopLevelConstInvalidated, + &Invalidated); + ProgramStateRef newState = makeWithStore(newStore); - return Eng->processRegionChanges(newState, &IS, Regions, Invalidated, Call); + + if (CausedByPointerEscape) { + newState = Eng->notifyCheckersOfPointerEscape(newState, &IS, + TopLevelInvalidated, + Invalidated, Call); + if (!ConstValues.empty()) { + StoreManager::InvalidatedRegions Empty; + newState = Eng->notifyCheckersOfPointerEscape(newState, &ConstIS, + TopLevelConstInvalidated, + Empty, Call, + true); + } + } + + return Eng->processRegionChanges(newState, &IS, + TopLevelInvalidated, Invalidated, + Call); } const StoreRef &newStore = - Mgr.StoreMgr->invalidateRegions(getStore(), Regions, E, Count, LCtx, IS, - Call, NULL); + Mgr.StoreMgr->invalidateRegions(getStore(), Values, ConstValues, + E, Count, LCtx, Call, + IS, ConstIS, NULL, NULL, NULL); return makeWithStore(newStore); } ProgramStateRef ProgramState::killBinding(Loc LV) const { - assert(!isa<loc::MemRegionVal>(LV) && "Use invalidateRegion instead."); + assert(!LV.getAs<loc::MemRegionVal>() && "Use invalidateRegion instead."); Store OldStore = getStore(); const StoreRef &newStore = @@ -243,7 +305,7 @@ SVal ProgramState::getSVal(Loc location, QualType T) const { // not unsigned. const llvm::APSInt &NewV = getBasicVals().Convert(T, *Int); - if (isa<Loc>(V)) + if (V.getAs<Loc>()) return loc::ConcreteInt(NewV); else return nonloc::ConcreteInt(NewV); @@ -268,23 +330,6 @@ ProgramStateRef ProgramState::BindExpr(const Stmt *S, return getStateManager().getPersistentState(NewSt); } -ProgramStateRef -ProgramState::bindExprAndLocation(const Stmt *S, const LocationContext *LCtx, - SVal location, - SVal V) const { - Environment NewEnv = - getStateManager().EnvMgr.bindExprAndLocation(Env, - EnvironmentEntry(S, LCtx), - location, V); - - if (NewEnv == Env) - return this; - - ProgramState NewSt = *this; - NewSt.Env = NewEnv; - return getStateManager().getPersistentState(NewSt); -} - ProgramStateRef ProgramState::assumeInBound(DefinedOrUnknownSVal Idx, DefinedOrUnknownSVal UpperBound, bool Assumption, @@ -308,28 +353,41 @@ ProgramStateRef ProgramState::assumeInBound(DefinedOrUnknownSVal Idx, // Adjust the index. SVal newIdx = svalBuilder.evalBinOpNN(this, BO_Add, - cast<NonLoc>(Idx), Min, indexTy); + Idx.castAs<NonLoc>(), Min, indexTy); if (newIdx.isUnknownOrUndef()) return this; // Adjust the upper bound. SVal newBound = - svalBuilder.evalBinOpNN(this, BO_Add, cast<NonLoc>(UpperBound), + svalBuilder.evalBinOpNN(this, BO_Add, UpperBound.castAs<NonLoc>(), Min, indexTy); if (newBound.isUnknownOrUndef()) return this; // Build the actual comparison. - SVal inBound = svalBuilder.evalBinOpNN(this, BO_LT, - cast<NonLoc>(newIdx), cast<NonLoc>(newBound), - Ctx.IntTy); + SVal inBound = svalBuilder.evalBinOpNN(this, BO_LT, newIdx.castAs<NonLoc>(), + newBound.castAs<NonLoc>(), Ctx.IntTy); if (inBound.isUnknownOrUndef()) return this; // Finally, let the constraint manager take care of it. ConstraintManager &CM = SM.getConstraintManager(); - return CM.assume(this, cast<DefinedSVal>(inBound), Assumption); + return CM.assume(this, inBound.castAs<DefinedSVal>(), Assumption); +} + +ConditionTruthVal ProgramState::isNull(SVal V) const { + if (V.isZeroConstant()) + return true; + + if (V.isConstant()) + return false; + + SymbolRef Sym = V.getAsSymbol(); + if (!Sym) + return ConditionTruthVal(); + + return getStateManager().ConstraintMgr->isNull(this, Sym); } ProgramStateRef ProgramStateManager::getInitialState(const LocationContext *InitLoc) { @@ -516,13 +574,22 @@ bool ScanReachableSymbols::scan(const SymExpr *sym) { } bool ScanReachableSymbols::scan(SVal val) { - if (loc::MemRegionVal *X = dyn_cast<loc::MemRegionVal>(&val)) + if (Optional<loc::MemRegionVal> X = val.getAs<loc::MemRegionVal>()) return scan(X->getRegion()); - if (nonloc::LazyCompoundVal *X = dyn_cast<nonloc::LazyCompoundVal>(&val)) - return scan(X->getRegion()); + if (Optional<nonloc::LazyCompoundVal> X = + val.getAs<nonloc::LazyCompoundVal>()) { + StoreManager &StoreMgr = state->getStateManager().getStoreManager(); + // FIXME: We don't really want to use getBaseRegion() here because pointer + // arithmetic doesn't apply, but scanReachableSymbols only accepts base + // regions right now. + if (!StoreMgr.scanReachableSymbols(X->getStore(), + X->getRegion()->getBaseRegion(), + *this)) + return false; + } - if (nonloc::LocAsInteger *X = dyn_cast<nonloc::LocAsInteger>(&val)) + if (Optional<nonloc::LocAsInteger> X = val.getAs<nonloc::LocAsInteger>()) return scan(X->getLoc()); if (SymbolRef Sym = val.getAsSymbol()) @@ -531,7 +598,7 @@ bool ScanReachableSymbols::scan(SVal val) { if (const SymExpr *Sym = val.getAsSymbolicExpression()) return scan(Sym); - if (nonloc::CompoundVal *X = dyn_cast<nonloc::CompoundVal>(&val)) + if (Optional<nonloc::CompoundVal> X = val.getAs<nonloc::CompoundVal>()) return scan(*X); return true; diff --git a/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp b/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp index 411094bc1d14..3606e099cec2 100644 --- a/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp +++ b/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp @@ -16,9 +16,9 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/APSIntType.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" -#include "llvm/Support/Debug.h" #include "llvm/ADT/FoldingSet.h" #include "llvm/ADT/ImmutableSet.h" +#include "llvm/Support/Debug.h" #include "llvm/Support/raw_ostream.h" using namespace clang; @@ -153,8 +153,8 @@ private: // The function returns false if the described range is entirely outside // the range of values for the associated symbol. APSIntType Type(getMinValue()); - APSIntType::RangeTestResultKind LowerTest = Type.testInRange(Lower); - APSIntType::RangeTestResultKind UpperTest = Type.testInRange(Upper); + APSIntType::RangeTestResultKind LowerTest = Type.testInRange(Lower, true); + APSIntType::RangeTestResultKind UpperTest = Type.testInRange(Upper, true); switch (LowerTest) { case APSIntType::RTR_Below: @@ -285,8 +285,8 @@ namespace { class RangeConstraintManager : public SimpleConstraintManager{ RangeSet GetRange(ProgramStateRef state, SymbolRef sym); public: - RangeConstraintManager(SubEngine *subengine, BasicValueFactory &BVF) - : SimpleConstraintManager(subengine, BVF) {} + RangeConstraintManager(SubEngine *subengine, SValBuilder &SVB) + : SimpleConstraintManager(subengine, SVB) {} ProgramStateRef assumeSymNE(ProgramStateRef state, SymbolRef sym, const llvm::APSInt& Int, @@ -328,7 +328,7 @@ private: ConstraintManager * ento::CreateRangeConstraintManager(ProgramStateManager &StMgr, SubEngine *Eng) { - return new RangeConstraintManager(Eng, StMgr.getBasicVals()); + return new RangeConstraintManager(Eng, StMgr.getSValBuilder()); } const llvm::APSInt* RangeConstraintManager::getSymVal(ProgramStateRef St, @@ -419,7 +419,7 @@ RangeConstraintManager::assumeSymNE(ProgramStateRef St, SymbolRef Sym, const llvm::APSInt &Adjustment) { // Before we do any real work, see if the value can even show up. APSIntType AdjustmentType(Adjustment); - if (AdjustmentType.testInRange(Int) != APSIntType::RTR_Within) + if (AdjustmentType.testInRange(Int, true) != APSIntType::RTR_Within) return St; llvm::APSInt Lower = AdjustmentType.convert(Int) - Adjustment; @@ -439,7 +439,7 @@ RangeConstraintManager::assumeSymEQ(ProgramStateRef St, SymbolRef Sym, const llvm::APSInt &Adjustment) { // Before we do any real work, see if the value can even show up. APSIntType AdjustmentType(Adjustment); - if (AdjustmentType.testInRange(Int) != APSIntType::RTR_Within) + if (AdjustmentType.testInRange(Int, true) != APSIntType::RTR_Within) return NULL; // [Int-Adjustment, Int-Adjustment] @@ -454,7 +454,7 @@ RangeConstraintManager::assumeSymLT(ProgramStateRef St, SymbolRef Sym, const llvm::APSInt &Adjustment) { // Before we do any real work, see if the value can even show up. APSIntType AdjustmentType(Adjustment); - switch (AdjustmentType.testInRange(Int)) { + switch (AdjustmentType.testInRange(Int, true)) { case APSIntType::RTR_Below: return NULL; case APSIntType::RTR_Within: @@ -483,7 +483,7 @@ RangeConstraintManager::assumeSymGT(ProgramStateRef St, SymbolRef Sym, const llvm::APSInt &Adjustment) { // Before we do any real work, see if the value can even show up. APSIntType AdjustmentType(Adjustment); - switch (AdjustmentType.testInRange(Int)) { + switch (AdjustmentType.testInRange(Int, true)) { case APSIntType::RTR_Below: return St; case APSIntType::RTR_Within: @@ -512,7 +512,7 @@ RangeConstraintManager::assumeSymGE(ProgramStateRef St, SymbolRef Sym, const llvm::APSInt &Adjustment) { // Before we do any real work, see if the value can even show up. APSIntType AdjustmentType(Adjustment); - switch (AdjustmentType.testInRange(Int)) { + switch (AdjustmentType.testInRange(Int, true)) { case APSIntType::RTR_Below: return St; case APSIntType::RTR_Within: @@ -541,7 +541,7 @@ RangeConstraintManager::assumeSymLE(ProgramStateRef St, SymbolRef Sym, const llvm::APSInt &Adjustment) { // Before we do any real work, see if the value can even show up. APSIntType AdjustmentType(Adjustment); - switch (AdjustmentType.testInRange(Int)) { + switch (AdjustmentType.testInRange(Int, true)) { case APSIntType::RTR_Below: return NULL; case APSIntType::RTR_Within: diff --git a/lib/StaticAnalyzer/Core/RegionStore.cpp b/lib/StaticAnalyzer/Core/RegionStore.cpp index aed994df4110..0f4a6824a24a 100644 --- a/lib/StaticAnalyzer/Core/RegionStore.cpp +++ b/lib/StaticAnalyzer/Core/RegionStore.cpp @@ -14,14 +14,15 @@ // parameters are created lazily. // //===----------------------------------------------------------------------===// +#include "clang/AST/Attr.h" #include "clang/AST/CharUnits.h" #include "clang/Analysis/Analyses/LiveVariables.h" #include "clang/Analysis/AnalysisContext.h" #include "clang/Basic/TargetInfo.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h" #include "llvm/ADT/ImmutableList.h" #include "llvm/ADT/ImmutableMap.h" #include "llvm/ADT/Optional.h" @@ -29,7 +30,6 @@ using namespace clang; using namespace ento; -using llvm::Optional; //===----------------------------------------------------------------------===// // Representation of binding keys. @@ -45,11 +45,15 @@ private: llvm::PointerIntPair<const MemRegion *, 2> P; uint64_t Data; - explicit BindingKey(const MemRegion *r, const MemRegion *Base, Kind k) + /// Create a key for a binding to region \p r, which has a symbolic offset + /// from region \p Base. + explicit BindingKey(const SubRegion *r, const SubRegion *Base, Kind k) : P(r, k | Symbolic), Data(reinterpret_cast<uintptr_t>(Base)) { assert(r && Base && "Must have known regions."); assert(getConcreteOffsetRegion() == Base && "Failed to store base region"); } + + /// Create a key for a binding at \p offset from base region \p r. explicit BindingKey(const MemRegion *r, uint64_t offset, Kind k) : P(r, k), Data(offset) { assert(r && "Must have known regions."); @@ -67,9 +71,9 @@ public: return Data; } - const MemRegion *getConcreteOffsetRegion() const { + const SubRegion *getConcreteOffsetRegion() const { assert(hasSymbolicOffset()); - return reinterpret_cast<const MemRegion *>(static_cast<uintptr_t>(Data)); + return reinterpret_cast<const SubRegion *>(static_cast<uintptr_t>(Data)); } const MemRegion *getBaseRegion() const { @@ -105,7 +109,7 @@ public: BindingKey BindingKey::Make(const MemRegion *R, Kind k) { const RegionOffset &RO = R->getAsOffset(); if (RO.hasSymbolicOffset()) - return BindingKey(R, RO.getRegion(), k); + return BindingKey(cast<SubRegion>(R), cast<SubRegion>(RO.getRegion()), k); return BindingKey(RO.getRegion(), RO.getOffset(), k); } @@ -120,6 +124,11 @@ namespace llvm { << ')'; return os; } + + template <typename T> struct isPodLike; + template <> struct isPodLike<BindingKey> { + static const bool value = true; + }; } // end llvm namespace void BindingKey::dump() const { @@ -130,8 +139,156 @@ void BindingKey::dump() const { // Actual Store type. //===----------------------------------------------------------------------===// -typedef llvm::ImmutableMap<BindingKey, SVal> ClusterBindings; -typedef llvm::ImmutableMap<const MemRegion *, ClusterBindings> RegionBindings; +typedef llvm::ImmutableMap<BindingKey, SVal> ClusterBindings; +typedef llvm::ImmutableMapRef<BindingKey, SVal> ClusterBindingsRef; +typedef std::pair<BindingKey, SVal> BindingPair; + +typedef llvm::ImmutableMap<const MemRegion *, ClusterBindings> + RegionBindings; + +namespace { +class RegionBindingsRef : public llvm::ImmutableMapRef<const MemRegion *, + ClusterBindings> { + ClusterBindings::Factory &CBFactory; +public: + typedef llvm::ImmutableMapRef<const MemRegion *, ClusterBindings> + ParentTy; + + RegionBindingsRef(ClusterBindings::Factory &CBFactory, + const RegionBindings::TreeTy *T, + RegionBindings::TreeTy::Factory *F) + : llvm::ImmutableMapRef<const MemRegion *, ClusterBindings>(T, F), + CBFactory(CBFactory) {} + + RegionBindingsRef(const ParentTy &P, ClusterBindings::Factory &CBFactory) + : llvm::ImmutableMapRef<const MemRegion *, ClusterBindings>(P), + CBFactory(CBFactory) {} + + RegionBindingsRef add(key_type_ref K, data_type_ref D) const { + return RegionBindingsRef(static_cast<const ParentTy*>(this)->add(K, D), + CBFactory); + } + + RegionBindingsRef remove(key_type_ref K) const { + return RegionBindingsRef(static_cast<const ParentTy*>(this)->remove(K), + CBFactory); + } + + RegionBindingsRef addBinding(BindingKey K, SVal V) const; + + RegionBindingsRef addBinding(const MemRegion *R, + BindingKey::Kind k, SVal V) const; + + RegionBindingsRef &operator=(const RegionBindingsRef &X) { + *static_cast<ParentTy*>(this) = X; + return *this; + } + + const SVal *lookup(BindingKey K) const; + const SVal *lookup(const MemRegion *R, BindingKey::Kind k) const; + const ClusterBindings *lookup(const MemRegion *R) const { + return static_cast<const ParentTy*>(this)->lookup(R); + } + + RegionBindingsRef removeBinding(BindingKey K); + + RegionBindingsRef removeBinding(const MemRegion *R, + BindingKey::Kind k); + + RegionBindingsRef removeBinding(const MemRegion *R) { + return removeBinding(R, BindingKey::Direct). + removeBinding(R, BindingKey::Default); + } + + Optional<SVal> getDirectBinding(const MemRegion *R) const; + + /// getDefaultBinding - Returns an SVal* representing an optional default + /// binding associated with a region and its subregions. + Optional<SVal> getDefaultBinding(const MemRegion *R) const; + + /// Return the internal tree as a Store. + Store asStore() const { + return asImmutableMap().getRootWithoutRetain(); + } + + void dump(raw_ostream &OS, const char *nl) const { + for (iterator I = begin(), E = end(); I != E; ++I) { + const ClusterBindings &Cluster = I.getData(); + for (ClusterBindings::iterator CI = Cluster.begin(), CE = Cluster.end(); + CI != CE; ++CI) { + OS << ' ' << CI.getKey() << " : " << CI.getData() << nl; + } + OS << nl; + } + } + + LLVM_ATTRIBUTE_USED void dump() const { + dump(llvm::errs(), "\n"); + } +}; +} // end anonymous namespace + +typedef const RegionBindingsRef& RegionBindingsConstRef; + +Optional<SVal> RegionBindingsRef::getDirectBinding(const MemRegion *R) const { + return Optional<SVal>::create(lookup(R, BindingKey::Direct)); +} + +Optional<SVal> RegionBindingsRef::getDefaultBinding(const MemRegion *R) const { + if (R->isBoundable()) + if (const TypedValueRegion *TR = dyn_cast<TypedValueRegion>(R)) + if (TR->getValueType()->isUnionType()) + return UnknownVal(); + + return Optional<SVal>::create(lookup(R, BindingKey::Default)); +} + +RegionBindingsRef RegionBindingsRef::addBinding(BindingKey K, SVal V) const { + const MemRegion *Base = K.getBaseRegion(); + + const ClusterBindings *ExistingCluster = lookup(Base); + ClusterBindings Cluster = (ExistingCluster ? *ExistingCluster + : CBFactory.getEmptyMap()); + + ClusterBindings NewCluster = CBFactory.add(Cluster, K, V); + return add(Base, NewCluster); +} + + +RegionBindingsRef RegionBindingsRef::addBinding(const MemRegion *R, + BindingKey::Kind k, + SVal V) const { + return addBinding(BindingKey::Make(R, k), V); +} + +const SVal *RegionBindingsRef::lookup(BindingKey K) const { + const ClusterBindings *Cluster = lookup(K.getBaseRegion()); + if (!Cluster) + return 0; + return Cluster->lookup(K); +} + +const SVal *RegionBindingsRef::lookup(const MemRegion *R, + BindingKey::Kind k) const { + return lookup(BindingKey::Make(R, k)); +} + +RegionBindingsRef RegionBindingsRef::removeBinding(BindingKey K) { + const MemRegion *Base = K.getBaseRegion(); + const ClusterBindings *Cluster = lookup(Base); + if (!Cluster) + return *this; + + ClusterBindings NewCluster = CBFactory.remove(*Cluster, K); + if (NewCluster.isEmpty()) + return remove(Base); + return add(Base, NewCluster); +} + +RegionBindingsRef RegionBindingsRef::removeBinding(const MemRegion *R, + BindingKey::Kind k){ + return removeBinding(BindingKey::Make(R, k)); +} //===----------------------------------------------------------------------===// // Fine-grained control of RegionStoreManager. @@ -161,26 +318,38 @@ public: //===----------------------------------------------------------------------===// namespace { +class invalidateRegionsWorker; class RegionStoreManager : public StoreManager { +public: const RegionStoreFeatures Features; RegionBindings::Factory RBFactory; - ClusterBindings::Factory CBFactory; + mutable ClusterBindings::Factory CBFactory; + + typedef std::vector<SVal> SValListTy; +private: + typedef llvm::DenseMap<const LazyCompoundValData *, + SValListTy> LazyBindingsMapTy; + LazyBindingsMapTy LazyBindingsMap; + + /// \brief A helper used to populate the work list with the given set of + /// regions. + void populateWorkList(invalidateRegionsWorker &W, + ArrayRef<SVal> Values, + bool IsArrayOfConstRegions, + InvalidatedRegions *TopLevelRegions); public: RegionStoreManager(ProgramStateManager& mgr, const RegionStoreFeatures &f) : StoreManager(mgr), Features(f), RBFactory(mgr.getAllocator()), CBFactory(mgr.getAllocator()) {} - Optional<SVal> getDirectBinding(RegionBindings B, const MemRegion *R); - /// getDefaultBinding - Returns an SVal* representing an optional default - /// binding associated with a region and its subregions. - Optional<SVal> getDefaultBinding(RegionBindings B, const MemRegion *R); /// setImplicitDefaultValue - Set the default binding for the provided /// MemRegion to the value implicitly defined for compound literals when /// the value is not specified. - StoreRef setImplicitDefaultValue(Store store, const MemRegion *R, QualType T); + RegionBindingsRef setImplicitDefaultValue(RegionBindingsConstRef B, + const MemRegion *R, QualType T); /// ArrayToPointer - Emulates the "decay" of an array to a pointer /// type. 'Array' represents the lvalue of the array being decayed @@ -197,57 +366,47 @@ 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, + RegionBindingsRef invalidateGlobalRegion(MemRegion::Kind K, + const Expr *Ex, + unsigned Count, + const LocationContext *LCtx, + RegionBindingsRef B, + InvalidatedRegions *Invalidated); + + StoreRef invalidateRegions(Store store, + ArrayRef<SVal> Values, + ArrayRef<SVal> ConstValues, const Expr *E, unsigned Count, const LocationContext *LCtx, - InvalidatedSymbols &IS, const CallEvent *Call, - InvalidatedRegions *Invalidated); + InvalidatedSymbols &IS, + InvalidatedSymbols &ConstIS, + InvalidatedRegions *Invalidated, + InvalidatedRegions *InvalidatedTopLevel, + InvalidatedRegions *InvalidatedTopLevelConst); bool scanReachableSymbols(Store S, const MemRegion *R, ScanReachableSymbols &Callbacks); -public: // Made public for helper classes. - - RegionBindings removeSubRegionBindings(RegionBindings B, const SubRegion *R); - - RegionBindings addBinding(RegionBindings B, BindingKey K, SVal V); - - RegionBindings addBinding(RegionBindings B, const MemRegion *R, - BindingKey::Kind k, SVal V); - - const SVal *lookup(RegionBindings B, BindingKey K); - const SVal *lookup(RegionBindings B, const MemRegion *R, BindingKey::Kind k); + RegionBindingsRef removeSubRegionBindings(RegionBindingsConstRef B, + const SubRegion *R); - RegionBindings removeBinding(RegionBindings B, BindingKey K); - RegionBindings removeBinding(RegionBindings B, const MemRegion *R, - BindingKey::Kind k); +public: // Part of public interface to class. - RegionBindings removeBinding(RegionBindings B, const MemRegion *R) { - return removeBinding(removeBinding(B, R, BindingKey::Direct), R, - BindingKey::Default); + virtual StoreRef Bind(Store store, Loc LV, SVal V) { + return StoreRef(bind(getRegionBindings(store), LV, V).asStore(), *this); } - RegionBindings removeCluster(RegionBindings B, const MemRegion *R); - -public: // Part of public interface to class. - - StoreRef Bind(Store store, Loc LV, SVal V); + RegionBindingsRef bind(RegionBindingsConstRef B, Loc LV, SVal V); // BindDefault is only used to initialize a region with a default value. StoreRef BindDefault(Store store, const MemRegion *R, SVal V) { - 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); + RegionBindingsRef B = getRegionBindings(store); + assert(!B.lookup(R, BindingKey::Default)); + assert(!B.lookup(R, BindingKey::Direct)); + return StoreRef(B.addBinding(R, BindingKey::Default, V) + .asImmutableMap() + .getRootWithoutRetain(), *this); } /// \brief Create a new store that binds a value to a compound literal. @@ -265,31 +424,37 @@ public: // Part of public interface to class. const LocationContext *LC, SVal V); /// BindStruct - Bind a compound value to a structure. - StoreRef BindStruct(Store store, const TypedValueRegion* R, SVal V); + RegionBindingsRef bindStruct(RegionBindingsConstRef B, + const TypedValueRegion* R, SVal V); /// BindVector - Bind a compound value to a vector. - StoreRef BindVector(Store store, const TypedValueRegion* R, SVal V); + RegionBindingsRef bindVector(RegionBindingsConstRef B, + const TypedValueRegion* R, SVal V); - StoreRef BindArray(Store store, const TypedValueRegion* R, SVal V); + RegionBindingsRef bindArray(RegionBindingsConstRef B, + const TypedValueRegion* R, + SVal V); /// Clears out all bindings in the given region and assigns a new value /// as a Default binding. - StoreRef BindAggregate(Store store, const TypedRegion *R, SVal DefaultVal); + RegionBindingsRef bindAggregate(RegionBindingsConstRef B, + const TypedRegion *R, + SVal DefaultVal); /// \brief Create a new store with the specified binding removed. /// \param ST the original store, that is the basis for the new store. /// \param L the location whose binding should be removed. - StoreRef killBinding(Store ST, Loc L); + virtual StoreRef killBinding(Store ST, Loc L); void incrementReferenceCount(Store store) { - GetRegionBindings(store).manualRetain(); + getRegionBindings(store).manualRetain(); } /// If the StoreManager supports it, decrement the reference count of /// the specified Store object. If the reference count hits 0, the memory /// associated with the object is recycled. void decrementReferenceCount(Store store) { - GetRegionBindings(store).manualRelease(); + getRegionBindings(store).manualRelease(); } bool includedInBindings(Store store, const MemRegion *region) const; @@ -307,45 +472,64 @@ public: // Part of public interface to class. /// return undefined /// else /// return symbolic - SVal getBinding(Store store, Loc L, QualType T = QualType()); + virtual SVal getBinding(Store S, Loc L, QualType T) { + return getBinding(getRegionBindings(S), L, T); + } - SVal getBindingForElement(Store store, const ElementRegion *R); + SVal getBinding(RegionBindingsConstRef B, Loc L, QualType T = QualType()); - SVal getBindingForField(Store store, const FieldRegion *R); + SVal getBindingForElement(RegionBindingsConstRef B, const ElementRegion *R); - SVal getBindingForObjCIvar(Store store, const ObjCIvarRegion *R); + SVal getBindingForField(RegionBindingsConstRef B, const FieldRegion *R); - SVal getBindingForVar(Store store, const VarRegion *R); + SVal getBindingForObjCIvar(RegionBindingsConstRef B, const ObjCIvarRegion *R); + + SVal getBindingForVar(RegionBindingsConstRef B, const VarRegion *R); SVal getBindingForLazySymbol(const TypedValueRegion *R); - SVal getBindingForFieldOrElementCommon(Store store, const TypedValueRegion *R, - QualType Ty, const MemRegion *superR); + SVal getBindingForFieldOrElementCommon(RegionBindingsConstRef B, + const TypedValueRegion *R, + QualType Ty, + const MemRegion *superR); - SVal getLazyBinding(const MemRegion *lazyBindingRegion, - Store lazyBindingStore); + SVal getLazyBinding(const SubRegion *LazyBindingRegion, + RegionBindingsRef LazyBinding); /// 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 getBindingForStruct(Store store, const TypedValueRegion* R); - - SVal getBindingForArray(Store store, const TypedValueRegion* R); + SVal getBindingForStruct(RegionBindingsConstRef B, const TypedValueRegion *R); + SVal getBindingForArray(RegionBindingsConstRef B, const TypedValueRegion *R); + NonLoc createLazyBinding(RegionBindingsConstRef B, const TypedValueRegion *R); /// Used to lazily generate derived symbols for bindings that are defined - /// implicitly by default bindings in a super region. - Optional<SVal> getBindingForDerivedDefaultValue(RegionBindings B, + /// implicitly by default bindings in a super region. + /// + /// Note that callers may need to specially handle LazyCompoundVals, which + /// are returned as is in case the caller needs to treat them differently. + Optional<SVal> getBindingForDerivedDefaultValue(RegionBindingsConstRef 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*> - GetLazyBinding(RegionBindings B, const MemRegion *R, - const MemRegion *originalRegion, - bool includeSuffix = false); + /// Get the state and region whose binding this region \p R corresponds to. + /// + /// If there is no lazy binding for \p R, the returned value will have a null + /// \c second. Note that a null pointer can represents a valid Store. + std::pair<Store, const SubRegion *> + findLazyBinding(RegionBindingsConstRef B, const SubRegion *R, + const SubRegion *originalRegion); + + /// Returns the cached set of interesting SVals contained within a lazy + /// binding. + /// + /// The precise value of "interesting" is determined for the purposes of + /// RegionStore's internal analysis. It must always contain all regions and + /// symbols, but may omit constants and other kinds of SVal. + const SValListTy &getInterestingValues(nonloc::LazyCompoundVal LCV); //===------------------------------------------------------------------===// // State pruning. @@ -368,16 +552,18 @@ public: // Part of public interface to class. // Utility methods. //===------------------------------------------------------------------===// - static inline RegionBindings GetRegionBindings(Store store) { - return RegionBindings(static_cast<const RegionBindings::TreeTy*>(store)); + RegionBindingsRef getRegionBindings(Store store) const { + return RegionBindingsRef(CBFactory, + static_cast<const RegionBindings::TreeTy*>(store), + RBFactory.getTreeFactory()); } void print(Store store, raw_ostream &Out, const char* nl, const char *sep); void iterBindings(Store store, BindingsHandler& f) { - RegionBindings B = GetRegionBindings(store); - for (RegionBindings::iterator I = B.begin(), E = B.end(); I != E; ++I) { + RegionBindingsRef B = getRegionBindings(store); + for (RegionBindingsRef::iterator I = B.begin(), E = B.end(); I != E; ++I) { const ClusterBindings &Cluster = I.getData(); for (ClusterBindings::iterator CI = Cluster.begin(), CE = Cluster.end(); CI != CE; ++CI) { @@ -422,7 +608,8 @@ template <typename DERIVED> class ClusterAnalysis { protected: typedef llvm::DenseMap<const MemRegion *, const ClusterBindings *> ClusterMap; - typedef SmallVector<const MemRegion *, 10> WorkList; + typedef llvm::PointerIntPair<const MemRegion *, 1, bool> WorkListElement; + typedef SmallVector<WorkListElement, 10> WorkList; llvm::SmallPtrSet<const ClusterBindings *, 16> Visited; @@ -432,7 +619,7 @@ protected: ASTContext &Ctx; SValBuilder &svalBuilder; - RegionBindings B; + RegionBindingsRef B; const bool includeGlobals; @@ -442,12 +629,12 @@ protected: public: ClusterAnalysis(RegionStoreManager &rm, ProgramStateManager &StateMgr, - RegionBindings b, const bool includeGlobals) + RegionBindingsRef b, const bool includeGlobals) : RM(rm), Ctx(StateMgr.getContext()), svalBuilder(StateMgr.getSValBuilder()), B(b), includeGlobals(includeGlobals) {} - RegionBindings getRegionBindings() const { return B; } + RegionBindingsRef getRegionBindings() const { return B; } bool isVisited(const MemRegion *R) { return Visited.count(getCluster(R)); @@ -455,7 +642,8 @@ public: void GenerateClusters() { // Scan the entire set of bindings and record the region clusters. - for (RegionBindings::iterator RI = B.begin(), RE = B.end(); RI != RE; ++RI){ + for (RegionBindingsRef::iterator RI = B.begin(), RE = B.end(); + RI != RE; ++RI){ const MemRegion *Base = RI.getKey(); const ClusterBindings &Cluster = RI.getData(); @@ -468,35 +656,35 @@ public: } } - bool AddToWorkList(const MemRegion *R, const ClusterBindings *C) { + bool AddToWorkList(WorkListElement E, const ClusterBindings *C) { if (C && !Visited.insert(C)) return false; - WL.push_back(R); + WL.push_back(E); return true; } - bool AddToWorkList(const MemRegion *R) { - const MemRegion *baseR = R->getBaseRegion(); - return AddToWorkList(baseR, getCluster(baseR)); + bool AddToWorkList(const MemRegion *R, bool Flag = false) { + const MemRegion *BaseR = R->getBaseRegion(); + return AddToWorkList(WorkListElement(BaseR, Flag), getCluster(BaseR)); } void RunWorkList() { while (!WL.empty()) { - const MemRegion *baseR = WL.pop_back_val(); + WorkListElement E = WL.pop_back_val(); + const MemRegion *BaseR = E.getPointer(); - // First visit the cluster. - if (const ClusterBindings *Cluster = getCluster(baseR)) - static_cast<DERIVED*>(this)->VisitCluster(baseR, *Cluster); - - // Next, visit the base region. - static_cast<DERIVED*>(this)->VisitBaseRegion(baseR); + static_cast<DERIVED*>(this)->VisitCluster(BaseR, getCluster(BaseR), + E.getInt()); } } -public: void VisitAddedToCluster(const MemRegion *baseR, const ClusterBindings &C) {} - void VisitCluster(const MemRegion *baseR, const ClusterBindings &C) {} - void VisitBaseRegion(const MemRegion *baseR) {} + void VisitCluster(const MemRegion *baseR, const ClusterBindings *C) {} + + void VisitCluster(const MemRegion *BaseR, const ClusterBindings *C, + bool Flag) { + static_cast<DERIVED*>(this)->VisitCluster(BaseR, C); + } }; } @@ -507,7 +695,7 @@ public: bool RegionStoreManager::scanReachableSymbols(Store S, const MemRegion *R, ScanReachableSymbols &Callbacks) { assert(R == R->getBaseRegion() && "Should only be called for base regions"); - RegionBindings B = GetRegionBindings(S); + RegionBindingsRef B = getRegionBindings(S); const ClusterBindings *Cluster = B.lookup(R); if (!Cluster) @@ -562,98 +750,141 @@ static bool isCompatibleWithFields(BindingKey K, const FieldVector &Fields) { Fields.begin() - Delta); } -RegionBindings RegionStoreManager::removeSubRegionBindings(RegionBindings B, - const SubRegion *R) { - BindingKey SRKey = BindingKey::Make(R, BindingKey::Default); - const MemRegion *ClusterHead = SRKey.getBaseRegion(); - if (R == ClusterHead) { - // We can remove an entire cluster's bindings all in one go. - return RBFactory.remove(B, R); - } - +/// Collects all bindings in \p Cluster that may refer to bindings within +/// \p Top. +/// +/// Each binding is a pair whose \c first is the key (a BindingKey) and whose +/// \c second is the value (an SVal). +/// +/// The \p IncludeAllDefaultBindings parameter specifies whether to include +/// default bindings that may extend beyond \p Top itself, e.g. if \p Top is +/// an aggregate within a larger aggregate with a default binding. +static void +collectSubRegionBindings(SmallVectorImpl<BindingPair> &Bindings, + SValBuilder &SVB, const ClusterBindings &Cluster, + const SubRegion *Top, BindingKey TopKey, + bool IncludeAllDefaultBindings) { FieldVector FieldsInSymbolicSubregions; - bool HasSymbolicOffset = SRKey.hasSymbolicOffset(); - if (HasSymbolicOffset) { - getSymbolicOffsetFields(SRKey, FieldsInSymbolicSubregions); - R = cast<SubRegion>(SRKey.getConcreteOffsetRegion()); - SRKey = BindingKey::Make(R, BindingKey::Default); + if (TopKey.hasSymbolicOffset()) { + getSymbolicOffsetFields(TopKey, FieldsInSymbolicSubregions); + Top = cast<SubRegion>(TopKey.getConcreteOffsetRegion()); + TopKey = BindingKey::Make(Top, BindingKey::Default); } - // This assumes the region being invalidated is char-aligned. This isn't - // true for bitfields, but since bitfields have no subregions they shouldn't - // be using this function anyway. + // Find the length (in bits) of the region being invalidated. uint64_t Length = UINT64_MAX; - - SVal Extent = R->getExtent(svalBuilder); - if (nonloc::ConcreteInt *ExtentCI = dyn_cast<nonloc::ConcreteInt>(&Extent)) { + SVal Extent = Top->getExtent(SVB); + if (Optional<nonloc::ConcreteInt> ExtentCI = + Extent.getAs<nonloc::ConcreteInt>()) { const llvm::APSInt &ExtentInt = ExtentCI->getValue(); assert(ExtentInt.isNonNegative() || ExtentInt.isUnsigned()); // Extents are in bytes but region offsets are in bits. Be careful! - Length = ExtentInt.getLimitedValue() * Ctx.getCharWidth(); + Length = ExtentInt.getLimitedValue() * SVB.getContext().getCharWidth(); + } else if (const FieldRegion *FR = dyn_cast<FieldRegion>(Top)) { + if (FR->getDecl()->isBitField()) + Length = FR->getDecl()->getBitWidthValue(SVB.getContext()); } - const ClusterBindings *Cluster = B.lookup(ClusterHead); - if (!Cluster) - return B; - - ClusterBindings Result = *Cluster; - - // It is safe to iterate over the bindings as they are being changed - // because they are in an ImmutableMap. - for (ClusterBindings::iterator I = Cluster->begin(), E = Cluster->end(); + for (ClusterBindings::iterator I = Cluster.begin(), E = Cluster.end(); I != E; ++I) { BindingKey NextKey = I.getKey(); - if (NextKey.getRegion() == SRKey.getRegion()) { + if (NextKey.getRegion() == TopKey.getRegion()) { // FIXME: This doesn't catch the case where we're really invalidating a // region with a symbolic offset. Example: // R: points[i].y // Next: points[0].x - if (NextKey.getOffset() > SRKey.getOffset() && - NextKey.getOffset() - SRKey.getOffset() < Length) { + if (NextKey.getOffset() > TopKey.getOffset() && + NextKey.getOffset() - TopKey.getOffset() < Length) { // Case 1: The next binding is inside the region we're invalidating. - // Remove it. - Result = CBFactory.remove(Result, NextKey); + // Include it. + Bindings.push_back(*I); - } else if (NextKey.getOffset() == SRKey.getOffset()) { + } else if (NextKey.getOffset() == TopKey.getOffset()) { // Case 2: The next binding is at the same offset as the region we're // invalidating. In this case, we need to leave default bindings alone, // since they may be providing a default value for a regions beyond what // we're invalidating. // FIXME: This is probably incorrect; consider invalidating an outer // struct whose first field is bound to a LazyCompoundVal. - if (NextKey.isDirect()) - Result = CBFactory.remove(Result, NextKey); + if (IncludeAllDefaultBindings || NextKey.isDirect()) + Bindings.push_back(*I); } - + } else if (NextKey.hasSymbolicOffset()) { const MemRegion *Base = NextKey.getConcreteOffsetRegion(); - if (R->isSubRegionOf(Base)) { + if (Top->isSubRegionOf(Base)) { // Case 3: The next key is symbolic and we just changed something within // its concrete region. We don't know if the binding is still valid, so - // we'll be conservative and remove it. - if (NextKey.isDirect()) + // we'll be conservative and include it. + if (IncludeAllDefaultBindings || NextKey.isDirect()) if (isCompatibleWithFields(NextKey, FieldsInSymbolicSubregions)) - Result = CBFactory.remove(Result, NextKey); + Bindings.push_back(*I); } else if (const SubRegion *BaseSR = dyn_cast<SubRegion>(Base)) { // Case 4: The next key is symbolic, but we changed a known - // super-region. In this case the binding is certainly no longer valid. - if (R == Base || BaseSR->isSubRegionOf(R)) + // super-region. In this case the binding is certainly included. + if (Top == Base || BaseSR->isSubRegionOf(Top)) if (isCompatibleWithFields(NextKey, FieldsInSymbolicSubregions)) - Result = CBFactory.remove(Result, NextKey); + Bindings.push_back(*I); } } } +} + +static void +collectSubRegionBindings(SmallVectorImpl<BindingPair> &Bindings, + SValBuilder &SVB, const ClusterBindings &Cluster, + const SubRegion *Top, bool IncludeAllDefaultBindings) { + collectSubRegionBindings(Bindings, SVB, Cluster, Top, + BindingKey::Make(Top, BindingKey::Default), + IncludeAllDefaultBindings); +} + +RegionBindingsRef +RegionStoreManager::removeSubRegionBindings(RegionBindingsConstRef B, + const SubRegion *Top) { + BindingKey TopKey = BindingKey::Make(Top, BindingKey::Default); + const MemRegion *ClusterHead = TopKey.getBaseRegion(); + + if (Top == ClusterHead) { + // We can remove an entire cluster's bindings all in one go. + return B.remove(Top); + } + + const ClusterBindings *Cluster = B.lookup(ClusterHead); + if (!Cluster) { + // If we're invalidating a region with a symbolic offset, we need to make + // sure we don't treat the base region as uninitialized anymore. + if (TopKey.hasSymbolicOffset()) { + const SubRegion *Concrete = TopKey.getConcreteOffsetRegion(); + return B.addBinding(Concrete, BindingKey::Default, UnknownVal()); + } + return B; + } + + SmallVector<BindingPair, 32> Bindings; + collectSubRegionBindings(Bindings, svalBuilder, *Cluster, Top, TopKey, + /*IncludeAllDefaultBindings=*/false); + + ClusterBindingsRef Result(*Cluster, CBFactory); + for (SmallVectorImpl<BindingPair>::const_iterator I = Bindings.begin(), + E = Bindings.end(); + I != E; ++I) + Result = Result.remove(I->first); // If we're invalidating a region with a symbolic offset, we need to make sure // we don't treat the base region as uninitialized anymore. - // FIXME: This isn't very precise; see the example in the loop. - if (HasSymbolicOffset) - Result = CBFactory.add(Result, SRKey, UnknownVal()); + // FIXME: This isn't very precise; see the example in + // collectSubRegionBindings. + if (TopKey.hasSymbolicOffset()) { + const SubRegion *Concrete = TopKey.getConcreteOffsetRegion(); + Result = Result.add(BindingKey::Make(Concrete, BindingKey::Default), + UnknownVal()); + } if (Result.isEmpty()) - return RBFactory.remove(B, ClusterHead); - return RBFactory.add(B, ClusterHead, Result); + return B.remove(ClusterHead); + return B.add(ClusterHead, Result.asImmutableMap()); } namespace { @@ -662,24 +893,26 @@ class invalidateRegionsWorker : public ClusterAnalysis<invalidateRegionsWorker> const Expr *Ex; unsigned Count; const LocationContext *LCtx; - StoreManager::InvalidatedSymbols &IS; + InvalidatedSymbols &IS; + InvalidatedSymbols &ConstIS; StoreManager::InvalidatedRegions *Regions; public: invalidateRegionsWorker(RegionStoreManager &rm, ProgramStateManager &stateMgr, - RegionBindings b, + RegionBindingsRef b, const Expr *ex, unsigned count, const LocationContext *lctx, - StoreManager::InvalidatedSymbols &is, + InvalidatedSymbols &is, + InvalidatedSymbols &inConstIS, StoreManager::InvalidatedRegions *r, bool includeGlobals) : ClusterAnalysis<invalidateRegionsWorker>(rm, stateMgr, b, includeGlobals), - Ex(ex), Count(count), LCtx(lctx), IS(is), Regions(r) {} + Ex(ex), Count(count), LCtx(lctx), IS(is), ConstIS(inConstIS), Regions(r){} - void VisitCluster(const MemRegion *baseR, const ClusterBindings &C); - void VisitBaseRegion(const MemRegion *baseR); - -private: + /// \param IsConst Specifies if the region we are invalidating is constant. + /// If it is, we invalidate all subregions, but not the base region itself. + void VisitCluster(const MemRegion *baseR, const ClusterBindings *C, + bool IsConst); void VisitBinding(SVal V); }; } @@ -695,43 +928,30 @@ void invalidateRegionsWorker::VisitBinding(SVal V) { } // Is it a LazyCompoundVal? All references get invalidated as well. - if (const nonloc::LazyCompoundVal *LCS = - dyn_cast<nonloc::LazyCompoundVal>(&V)) { + if (Optional<nonloc::LazyCompoundVal> LCS = + V.getAs<nonloc::LazyCompoundVal>()) { - const MemRegion *LazyR = LCS->getRegion(); - RegionBindings B = RegionStoreManager::GetRegionBindings(LCS->getStore()); + const RegionStoreManager::SValListTy &Vals = RM.getInterestingValues(*LCS); - // FIXME: This should not have to walk all bindings in the old store. - for (RegionBindings::iterator RI = B.begin(), RE = B.end(); RI != RE; ++RI){ - const ClusterBindings &Cluster = RI.getData(); - for (ClusterBindings::iterator CI = Cluster.begin(), CE = Cluster.end(); - CI != CE; ++CI) { - BindingKey K = CI.getKey(); - if (const SubRegion *BaseR = dyn_cast<SubRegion>(K.getRegion())) { - if (BaseR == LazyR) - VisitBinding(CI.getData()); - else if (K.hasSymbolicOffset() && BaseR->isSubRegionOf(LazyR)) - VisitBinding(CI.getData()); - } - } - } + for (RegionStoreManager::SValListTy::const_iterator I = Vals.begin(), + E = Vals.end(); + I != E; ++I) + VisitBinding(*I); return; } } -void invalidateRegionsWorker::VisitCluster(const MemRegion *BaseR, - const ClusterBindings &C) { - for (ClusterBindings::iterator I = C.begin(), E = C.end(); I != E; ++I) - VisitBinding(I.getData()); - - B = RM.removeCluster(B, BaseR); -} +void invalidateRegionsWorker::VisitCluster(const MemRegion *baseR, + const ClusterBindings *C, + bool IsConst) { + if (C) { + for (ClusterBindings::iterator I = C->begin(), E = C->end(); I != E; ++I) + VisitBinding(I.getData()); -void invalidateRegionsWorker::VisitBaseRegion(const MemRegion *baseR) { - // Symbolic region? Mark that symbol touched by the invalidation. - if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(baseR)) - IS.insert(SR->getSymbol()); + if (!IsConst) + B = B.remove(baseR); + } // BlockDataRegion? If so, invalidate captured variables that are passed // by reference. @@ -739,7 +959,7 @@ void invalidateRegionsWorker::VisitBaseRegion(const MemRegion *baseR) { for (BlockDataRegion::referenced_vars_iterator BI = BR->referenced_vars_begin(), BE = BR->referenced_vars_end() ; BI != BE; ++BI) { - const VarRegion *VR = *BI; + const VarRegion *VR = BI.getCapturedRegion(); const VarDecl *VD = VR->getDecl(); if (VD->getAttr<BlocksAttr>() || !VD->hasLocalStorage()) { AddToWorkList(VR); @@ -750,9 +970,8 @@ void invalidateRegionsWorker::VisitBaseRegion(const MemRegion *baseR) { // invalidate that region. This is because a block may capture // a pointer value, but the thing pointed by that pointer may // get invalidated. - Store store = B.getRootWithoutRetain(); - SVal V = RM.getBinding(store, loc::MemRegionVal(VR)); - if (const Loc *L = dyn_cast<Loc>(&V)) { + SVal V = RM.getBinding(B, loc::MemRegionVal(VR)); + if (Optional<Loc> L = V.getAs<Loc>()) { if (const MemRegion *LR = L->getAsRegion()) AddToWorkList(LR); } @@ -761,6 +980,20 @@ void invalidateRegionsWorker::VisitBaseRegion(const MemRegion *baseR) { return; } + // Symbolic region? + SymbolRef RegionSym = 0; + if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(baseR)) + RegionSym = SR->getSymbol(); + + if (IsConst) { + // Mark that symbol touched by the invalidation. + ConstIS.insert(RegionSym); + return; + } + + // Mark that symbol touched by the invalidation. + IS.insert(RegionSym); + // Otherwise, we have a normal data region. Record that we touched the region. if (Regions) Regions->push_back(baseR); @@ -770,7 +1003,7 @@ void invalidateRegionsWorker::VisitBaseRegion(const MemRegion *baseR) { // conjured symbol. The type of the symbol is irrelavant. DefinedOrUnknownSVal V = svalBuilder.conjureSymbolVal(baseR, Ex, LCtx, Ctx.IntTy, Count); - B = RM.addBinding(B, baseR, BindingKey::Default, V); + B = B.addBinding(baseR, BindingKey::Default, V); return; } @@ -786,7 +1019,7 @@ void invalidateRegionsWorker::VisitBaseRegion(const MemRegion *baseR) { // conjured symbol. The type of the symbol is irrelavant. DefinedOrUnknownSVal V = svalBuilder.conjureSymbolVal(baseR, Ex, LCtx, Ctx.IntTy, Count); - B = RM.addBinding(B, baseR, BindingKey::Default, V); + B = B.addBinding(baseR, BindingKey::Default, V); return; } @@ -795,7 +1028,7 @@ void invalidateRegionsWorker::VisitBaseRegion(const MemRegion *baseR) { DefinedOrUnknownSVal V = svalBuilder.conjureSymbolVal(baseR, Ex, LCtx, AT->getElementType(), Count); - B = RM.addBinding(B, baseR, BindingKey::Default, V); + B = B.addBinding(baseR, BindingKey::Default, V); return; } @@ -804,7 +1037,7 @@ void invalidateRegionsWorker::VisitBaseRegion(const MemRegion *baseR) { // If the region is a global and we are invalidating all globals, // just erase the entry. This causes all globals to be lazily // symbolicated from the same base symbol. - B = RM.removeBinding(B, baseR); + B = B.removeBinding(baseR); return; } @@ -812,15 +1045,16 @@ void invalidateRegionsWorker::VisitBaseRegion(const MemRegion *baseR) { DefinedOrUnknownSVal V = svalBuilder.conjureSymbolVal(baseR, Ex, LCtx, T,Count); assert(SymbolManager::canSymbolicate(T) || V.isUnknown()); - B = RM.addBinding(B, baseR, BindingKey::Direct, V); + B = B.addBinding(baseR, BindingKey::Direct, V); } -RegionBindings RegionStoreManager::invalidateGlobalRegion(MemRegion::Kind K, - const Expr *Ex, - unsigned Count, - const LocationContext *LCtx, - RegionBindings B, - InvalidatedRegions *Invalidated) { +RegionBindingsRef +RegionStoreManager::invalidateGlobalRegion(MemRegion::Kind K, + const Expr *Ex, + unsigned Count, + const LocationContext *LCtx, + RegionBindingsRef 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); @@ -828,8 +1062,8 @@ RegionBindings RegionStoreManager::invalidateGlobalRegion(MemRegion::Kind K, /* type does not matter */ Ctx.IntTy, Count); - B = removeBinding(B, GS); - B = addBinding(B, BindingKey::Make(GS, BindingKey::Default), V); + B = B.removeBinding(GS) + .addBinding(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. @@ -839,47 +1073,82 @@ RegionBindings RegionStoreManager::invalidateGlobalRegion(MemRegion::Kind K, return B; } -StoreRef RegionStoreManager::invalidateRegions(Store store, - ArrayRef<const MemRegion *> Regions, - const Expr *Ex, unsigned Count, - const LocationContext *LCtx, - InvalidatedSymbols &IS, - const CallEvent *Call, - InvalidatedRegions *Invalidated) { - invalidateRegionsWorker W(*this, StateMgr, - RegionStoreManager::GetRegionBindings(store), - Ex, Count, LCtx, IS, Invalidated, false); +void RegionStoreManager::populateWorkList(invalidateRegionsWorker &W, + ArrayRef<SVal> Values, + bool IsArrayOfConstRegions, + InvalidatedRegions *TopLevelRegions) { + for (ArrayRef<SVal>::iterator I = Values.begin(), + E = Values.end(); I != E; ++I) { + SVal V = *I; + if (Optional<nonloc::LazyCompoundVal> LCS = + V.getAs<nonloc::LazyCompoundVal>()) { + + const SValListTy &Vals = getInterestingValues(*LCS); + + for (SValListTy::const_iterator I = Vals.begin(), + E = Vals.end(); I != E; ++I) { + // Note: the last argument is false here because these are + // non-top-level regions. + if (const MemRegion *R = (*I).getAsRegion()) + W.AddToWorkList(R, /*IsConst=*/ false); + } + continue; + } + + if (const MemRegion *R = V.getAsRegion()) { + if (TopLevelRegions) + TopLevelRegions->push_back(R); + W.AddToWorkList(R, /*IsConst=*/ IsArrayOfConstRegions); + continue; + } + } +} + +StoreRef +RegionStoreManager::invalidateRegions(Store store, + ArrayRef<SVal> Values, + ArrayRef<SVal> ConstValues, + const Expr *Ex, unsigned Count, + const LocationContext *LCtx, + const CallEvent *Call, + InvalidatedSymbols &IS, + InvalidatedSymbols &ConstIS, + InvalidatedRegions *TopLevelRegions, + InvalidatedRegions *TopLevelConstRegions, + InvalidatedRegions *Invalidated) { + RegionBindingsRef B = RegionStoreManager::getRegionBindings(store); + invalidateRegionsWorker W(*this, StateMgr, B, Ex, Count, LCtx, IS, ConstIS, + Invalidated, false); // Scan the bindings and generate the clusters. W.GenerateClusters(); // Add the regions to the worklist. - for (ArrayRef<const MemRegion *>::iterator - I = Regions.begin(), E = Regions.end(); I != E; ++I) - W.AddToWorkList(*I); + populateWorkList(W, Values, /*IsArrayOfConstRegions*/ false, + TopLevelRegions); + populateWorkList(W, ConstValues, /*IsArrayOfConstRegions*/ true, + TopLevelConstRegions); W.RunWorkList(); // Return the new bindings. - RegionBindings B = W.getRegionBindings(); + B = W.getRegionBindings(); - // For all globals which are not static nor immutable: determine which global - // regions should be invalidated and invalidate them. + // For calls, determine which global regions should be invalidated and + // invalidate them. (Note that function-static and immutable globals are never + // invalidated by this.) // TODO: This could possibly be more precise with modules. - // - // System calls invalidate only system globals. - if (Call && Call->isInSystemHeader()) { + if (Call) { 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); + + if (!Call->isInSystemHeader()) { + B = invalidateGlobalRegion(MemRegion::GlobalInternalSpaceRegionKind, + Ex, Count, LCtx, B, Invalidated); + } } - return StoreRef(B.getRootWithoutRetain(), *this); + return StoreRef(B.asStore(), *this); } //===----------------------------------------------------------------------===// @@ -923,10 +1192,10 @@ RegionStoreManager::getSizeInElements(ProgramStateRef state, /// the array). This is called by ExprEngine when evaluating casts /// from arrays to pointers. SVal RegionStoreManager::ArrayToPointer(Loc Array) { - if (!isa<loc::MemRegionVal>(Array)) + if (!Array.getAs<loc::MemRegionVal>()) return UnknownVal(); - const MemRegion* R = cast<loc::MemRegionVal>(&Array)->getRegion(); + const MemRegion* R = Array.castAs<loc::MemRegionVal>().getRegion(); const TypedValueRegion* ArrayR = dyn_cast<TypedValueRegion>(R); if (!ArrayR) @@ -945,31 +1214,9 @@ SVal RegionStoreManager::ArrayToPointer(Loc Array) { // Loading values from regions. //===----------------------------------------------------------------------===// -Optional<SVal> RegionStoreManager::getDirectBinding(RegionBindings B, - const MemRegion *R) { - - if (const SVal *V = lookup(B, R, BindingKey::Direct)) - return *V; - - return Optional<SVal>(); -} - -Optional<SVal> RegionStoreManager::getDefaultBinding(RegionBindings B, - const MemRegion *R) { - if (R->isBoundable()) - if (const TypedValueRegion *TR = dyn_cast<TypedValueRegion>(R)) - if (TR->getValueType()->isUnionType()) - return UnknownVal(); - - if (const SVal *V = lookup(B, R, BindingKey::Default)) - return *V; - - return Optional<SVal>(); -} - -SVal RegionStoreManager::getBinding(Store store, Loc L, QualType T) { - assert(!isa<UnknownVal>(L) && "location unknown"); - assert(!isa<UndefinedVal>(L) && "location undefined"); +SVal RegionStoreManager::getBinding(RegionBindingsConstRef B, Loc L, QualType T) { + assert(!L.getAs<UnknownVal>() && "location unknown"); + assert(!L.getAs<UndefinedVal>() && "location undefined"); // For access to concrete addresses, return UnknownVal. Checks // for null dereferences (and similar errors) are done by checkers, not @@ -977,14 +1224,14 @@ SVal RegionStoreManager::getBinding(Store store, Loc L, QualType T) { // FIXME: We can consider lazily symbolicating such memory, but we really // should defer this when we can reason easily about symbolicating arrays // of bytes. - if (isa<loc::ConcreteInt>(L)) { + if (L.getAs<loc::ConcreteInt>()) { return UnknownVal(); } - if (!isa<loc::MemRegionVal>(L)) { + if (!L.getAs<loc::MemRegionVal>()) { return UnknownVal(); } - const MemRegion *MR = cast<loc::MemRegionVal>(L).getRegion(); + const MemRegion *MR = L.castAs<loc::MemRegionVal>().getRegion(); if (isa<AllocaRegion>(MR) || isa<SymbolicRegion>(MR) || @@ -1005,6 +1252,11 @@ SVal RegionStoreManager::getBinding(Store store, Loc L, QualType T) { const TypedValueRegion *R = cast<TypedValueRegion>(MR); QualType RTy = R->getValueType(); + // FIXME: we do not yet model the parts of a complex type, so treat the + // whole thing as "unknown". + if (RTy->isAnyComplexType()) + return UnknownVal(); + // FIXME: We should eventually handle funny addressing. e.g.: // // int x = ...; @@ -1013,9 +1265,8 @@ SVal RegionStoreManager::getBinding(Store store, Loc L, QualType T) { // char c = *q; // returns the first byte of 'x'. // // Such funny addressing will occur due to layering of regions. - if (RTy->isStructureOrClassType()) - return getBindingForStruct(store, R); + return getBindingForStruct(B, R); // FIXME: Handle unions. if (RTy->isUnionType()) @@ -1023,7 +1274,7 @@ SVal RegionStoreManager::getBinding(Store store, Loc L, QualType T) { if (RTy->isArrayType()) { if (RTy->isConstantArrayType()) - return getBindingForArray(store, R); + return getBindingForArray(B, R); else return UnknownVal(); } @@ -1033,7 +1284,7 @@ SVal RegionStoreManager::getBinding(Store store, Loc L, QualType T) { return UnknownVal(); if (const FieldRegion* FR = dyn_cast<FieldRegion>(R)) - return CastRetrievedVal(getBindingForField(store, FR), FR, T, false); + return CastRetrievedVal(getBindingForField(B, FR), FR, T, false); if (const ElementRegion* ER = dyn_cast<ElementRegion>(R)) { // FIXME: Here we actually perform an implicit conversion from the loaded @@ -1041,7 +1292,7 @@ SVal RegionStoreManager::getBinding(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(getBindingForElement(store, ER), ER, T, false); + return CastRetrievedVal(getBindingForElement(B, ER), ER, T, false); } if (const ObjCIvarRegion *IVR = dyn_cast<ObjCIvarRegion>(R)) { @@ -1051,7 +1302,7 @@ SVal RegionStoreManager::getBinding(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(getBindingForObjCIvar(store, IVR), IVR, T, false); + return CastRetrievedVal(getBindingForObjCIvar(B, IVR), IVR, T, false); } if (const VarRegion *VR = dyn_cast<VarRegion>(R)) { @@ -1061,11 +1312,10 @@ SVal RegionStoreManager::getBinding(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(getBindingForVar(store, VR), VR, T, false); + return CastRetrievedVal(getBindingForVar(B, VR), VR, T, false); } - RegionBindings B = GetRegionBindings(store); - const SVal *V = lookup(B, R, BindingKey::Direct); + const SVal *V = B.lookup(R, BindingKey::Direct); // Check if the region has a binding. if (V) @@ -1086,69 +1336,109 @@ SVal RegionStoreManager::getBinding(Store store, Loc L, QualType T) { return svalBuilder.getRegionValueSymbolVal(R); } -std::pair<Store, const MemRegion *> -RegionStoreManager::GetLazyBinding(RegionBindings B, const MemRegion *R, - const MemRegion *originalRegion, - bool includeSuffix) { - +static QualType getUnderlyingType(const SubRegion *R) { + QualType RegionTy; + if (const TypedValueRegion *TVR = dyn_cast<TypedValueRegion>(R)) + RegionTy = TVR->getValueType(); + + if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(R)) + RegionTy = SR->getSymbol()->getType(); + + return RegionTy; +} + +/// Checks to see if store \p B has a lazy binding for region \p R. +/// +/// If \p AllowSubregionBindings is \c false, a lazy binding will be rejected +/// if there are additional bindings within \p R. +/// +/// Note that unlike RegionStoreManager::findLazyBinding, this will not search +/// for lazy bindings for super-regions of \p R. +static Optional<nonloc::LazyCompoundVal> +getExistingLazyBinding(SValBuilder &SVB, RegionBindingsConstRef B, + const SubRegion *R, bool AllowSubregionBindings) { + Optional<SVal> V = B.getDefaultBinding(R); + if (!V) + return None; + + Optional<nonloc::LazyCompoundVal> LCV = V->getAs<nonloc::LazyCompoundVal>(); + if (!LCV) + return None; + + // If the LCV is for a subregion, the types might not match, and we shouldn't + // reuse the binding. + QualType RegionTy = getUnderlyingType(R); + if (!RegionTy.isNull() && + !RegionTy->isVoidPointerType()) { + QualType SourceRegionTy = LCV->getRegion()->getValueType(); + if (!SVB.getContext().hasSameUnqualifiedType(RegionTy, SourceRegionTy)) + return None; + } + + if (!AllowSubregionBindings) { + // If there are any other bindings within this region, we shouldn't reuse + // the top-level binding. + SmallVector<BindingPair, 16> Bindings; + collectSubRegionBindings(Bindings, SVB, *B.lookup(R->getBaseRegion()), R, + /*IncludeAllDefaultBindings=*/true); + if (Bindings.size() > 1) + return None; + } + + return *LCV; +} + + +std::pair<Store, const SubRegion *> +RegionStoreManager::findLazyBinding(RegionBindingsConstRef B, + const SubRegion *R, + const SubRegion *originalRegion) { if (originalRegion != R) { - if (Optional<SVal> OV = getDefaultBinding(B, R)) { - if (const nonloc::LazyCompoundVal *V = - dyn_cast<nonloc::LazyCompoundVal>(OV.getPointer())) - return std::make_pair(V->getStore(), V->getRegion()); - } + if (Optional<nonloc::LazyCompoundVal> V = + getExistingLazyBinding(svalBuilder, B, R, true)) + return std::make_pair(V->getStore(), V->getRegion()); } - + + typedef std::pair<Store, const SubRegion *> StoreRegionPair; + StoreRegionPair Result = StoreRegionPair(); + if (const ElementRegion *ER = dyn_cast<ElementRegion>(R)) { - const std::pair<Store, const MemRegion *> &X = - GetLazyBinding(B, ER->getSuperRegion(), originalRegion); - - if (X.second) - return std::make_pair(X.first, - MRMgr.getElementRegionWithSuper(ER, X.second)); - } - else if (const FieldRegion *FR = dyn_cast<FieldRegion>(R)) { - const std::pair<Store, const MemRegion *> &X = - GetLazyBinding(B, FR->getSuperRegion(), originalRegion); - - if (X.second) { - if (includeSuffix) - return std::make_pair(X.first, - MRMgr.getFieldRegionWithSuper(FR, X.second)); - return X; - } - - } - // C++ base object region is another kind of region that we should blast - // through to look for lazy compound value. It is like a field region. - else if (const CXXBaseObjectRegion *baseReg = - dyn_cast<CXXBaseObjectRegion>(R)) { - const std::pair<Store, const MemRegion *> &X = - GetLazyBinding(B, baseReg->getSuperRegion(), originalRegion); + Result = findLazyBinding(B, cast<SubRegion>(ER->getSuperRegion()), + originalRegion); + + if (Result.second) + Result.second = MRMgr.getElementRegionWithSuper(ER, Result.second); + + } else if (const FieldRegion *FR = dyn_cast<FieldRegion>(R)) { + Result = findLazyBinding(B, cast<SubRegion>(FR->getSuperRegion()), + originalRegion); + + if (Result.second) + Result.second = MRMgr.getFieldRegionWithSuper(FR, Result.second); + + } else if (const CXXBaseObjectRegion *BaseReg = + dyn_cast<CXXBaseObjectRegion>(R)) { + // C++ base object region is another kind of region that we should blast + // through to look for lazy compound value. It is like a field region. + Result = findLazyBinding(B, cast<SubRegion>(BaseReg->getSuperRegion()), + originalRegion); - if (X.second) { - if (includeSuffix) - return std::make_pair(X.first, - MRMgr.getCXXBaseObjectRegionWithSuper(baseReg, - X.second)); - return X; - } + if (Result.second) + Result.second = MRMgr.getCXXBaseObjectRegionWithSuper(BaseReg, + Result.second); } - // The NULL MemRegion indicates an non-existent lazy binding. A NULL Store is - // possible for a valid lazy binding. - return std::make_pair((Store) 0, (const MemRegion *) 0); + return Result; } -SVal RegionStoreManager::getBindingForElement(Store store, +SVal RegionStoreManager::getBindingForElement(RegionBindingsConstRef B, const ElementRegion* R) { // We do not currently model bindings of the CompoundLiteralregion. if (isa<CompoundLiteralRegion>(R->getBaseRegion())) return UnknownVal(); // Check if the region has a binding. - RegionBindings B = GetRegionBindings(store); - if (const Optional<SVal> &V = getDirectBinding(B, R)) + if (const Optional<SVal> &V = B.getDirectBinding(R)) return *V; const MemRegion* superR = R->getSuperRegion(); @@ -1163,7 +1453,7 @@ SVal RegionStoreManager::getBindingForElement(Store store, const StringLiteral *Str = StrR->getStringLiteral(); SVal Idx = R->getIndex(); - if (nonloc::ConcreteInt *CI = dyn_cast<nonloc::ConcreteInt>(&Idx)) { + if (Optional<nonloc::ConcreteInt> CI = Idx.getAs<nonloc::ConcreteInt>()) { int64_t i = CI->getValue().getSExtValue(); // Abort on string underrun. This can be possible by arbitrary // clients of getBindingForElement(). @@ -1202,7 +1492,7 @@ SVal RegionStoreManager::getBindingForElement(Store store, QualType elemT = R->getElementType(); if (elemT->isScalarType()) { if (Ctx.getTypeSizeInChars(baseT) >= Ctx.getTypeSizeInChars(elemT)) { - if (const Optional<SVal> &V = getDirectBinding(B, superR)) { + if (const Optional<SVal> &V = B.getDirectBinding(superR)) { if (SymbolRef parentSym = V->getAsSymbol()) return svalBuilder.getDerivedRegionValueSymbolVal(parentSym, R); @@ -1216,29 +1506,27 @@ SVal RegionStoreManager::getBindingForElement(Store store, } } } - return getBindingForFieldOrElementCommon(store, R, R->getElementType(), - superR); + return getBindingForFieldOrElementCommon(B, R, R->getElementType(),superR); } -SVal RegionStoreManager::getBindingForField(Store store, - const FieldRegion* R) { +SVal RegionStoreManager::getBindingForField(RegionBindingsConstRef B, + const FieldRegion* R) { // Check if the region has a binding. - RegionBindings B = GetRegionBindings(store); - if (const Optional<SVal> &V = getDirectBinding(B, R)) + if (const Optional<SVal> &V = B.getDirectBinding(R)) return *V; QualType Ty = R->getValueType(); - return getBindingForFieldOrElementCommon(store, R, Ty, R->getSuperRegion()); + return getBindingForFieldOrElementCommon(B, R, Ty, R->getSuperRegion()); } Optional<SVal> -RegionStoreManager::getBindingForDerivedDefaultValue(RegionBindings B, +RegionStoreManager::getBindingForDerivedDefaultValue(RegionBindingsConstRef B, const MemRegion *superR, const TypedValueRegion *R, QualType Ty) { - if (const Optional<SVal> &D = getDefaultBinding(B, superR)) { + if (const Optional<SVal> &D = B.getDefaultBinding(superR)) { const SVal &val = D.getValue(); if (SymbolRef parentSym = val.getAsSymbol()) return svalBuilder.getDerivedRegionValueSymbolVal(parentSym, R); @@ -1249,53 +1537,95 @@ RegionStoreManager::getBindingForDerivedDefaultValue(RegionBindings B, if (val.isUnknownOrUndef()) return val; - // Lazy bindings are handled later. - if (isa<nonloc::LazyCompoundVal>(val)) - return Optional<SVal>(); + // Lazy bindings are usually handled through getExistingLazyBinding(). + // We should unify these two code paths at some point. + if (val.getAs<nonloc::LazyCompoundVal>()) + return val; llvm_unreachable("Unknown default value"); } - return Optional<SVal>(); + return None; } -SVal RegionStoreManager::getLazyBinding(const MemRegion *lazyBindingRegion, - Store lazyBindingStore) { - if (const ElementRegion *ER = dyn_cast<ElementRegion>(lazyBindingRegion)) - return getBindingForElement(lazyBindingStore, ER); - - return getBindingForField(lazyBindingStore, - cast<FieldRegion>(lazyBindingRegion)); +SVal RegionStoreManager::getLazyBinding(const SubRegion *LazyBindingRegion, + RegionBindingsRef LazyBinding) { + SVal Result; + if (const ElementRegion *ER = dyn_cast<ElementRegion>(LazyBindingRegion)) + Result = getBindingForElement(LazyBinding, ER); + else + Result = getBindingForField(LazyBinding, + cast<FieldRegion>(LazyBindingRegion)); + + // FIXME: This is a hack to deal with RegionStore's inability to distinguish a + // default value for /part/ of an aggregate from a default value for the + // /entire/ aggregate. The most common case of this is when struct Outer + // has as its first member a struct Inner, which is copied in from a stack + // variable. In this case, even if the Outer's default value is symbolic, 0, + // or unknown, it gets overridden by the Inner's default value of undefined. + // + // This is a general problem -- if the Inner is zero-initialized, the Outer + // will now look zero-initialized. The proper way to solve this is with a + // new version of RegionStore that tracks the extent of a binding as well + // as the offset. + // + // This hack only takes care of the undefined case because that can very + // quickly result in a warning. + if (Result.isUndef()) + Result = UnknownVal(); + + return Result; } -SVal RegionStoreManager::getBindingForFieldOrElementCommon(Store store, +SVal +RegionStoreManager::getBindingForFieldOrElementCommon(RegionBindingsConstRef B, const TypedValueRegion *R, QualType Ty, const MemRegion *superR) { // At this point we have already checked in either getBindingForElement or // getBindingForField if 'R' has a direct binding. - RegionBindings B = GetRegionBindings(store); // Lazy binding? Store lazyBindingStore = NULL; - const MemRegion *lazyBindingRegion = NULL; - llvm::tie(lazyBindingStore, lazyBindingRegion) = GetLazyBinding(B, R, R, - true); - + const SubRegion *lazyBindingRegion = NULL; + llvm::tie(lazyBindingStore, lazyBindingRegion) = findLazyBinding(B, R, R); if (lazyBindingRegion) - return getLazyBinding(lazyBindingRegion, lazyBindingStore); + return getLazyBinding(lazyBindingRegion, + getRegionBindings(lazyBindingStore)); // 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 = - getBindingForDerivedDefaultValue(B, superR, R, Ty)) + // FIXME: This is a hack to deal with RegionStore's inability to distinguish a + // default value for /part/ of an aggregate from a default value for the + // /entire/ aggregate. The most common case of this is when struct Outer + // has as its first member a struct Inner, which is copied in from a stack + // variable. In this case, even if the Outer's default value is symbolic, 0, + // or unknown, it gets overridden by the Inner's default value of undefined. + // + // This is a general problem -- if the Inner is zero-initialized, the Outer + // will now look zero-initialized. The proper way to solve this is with a + // new version of RegionStore that tracks the extent of a binding as well + // as the offset. + // + // This hack only takes care of the undefined case because that can very + // quickly result in a warning. + bool hasPartialLazyBinding = false; + + const SubRegion *Base = dyn_cast<SubRegion>(superR); + while (Base) { + if (Optional<SVal> D = getBindingForDerivedDefaultValue(B, Base, R, Ty)) { + if (D->getAs<nonloc::LazyCompoundVal>()) { + hasPartialLazyBinding = true; + break; + } + return *D; + } - if (const ElementRegion *ER = dyn_cast<ElementRegion>(superR)) { + if (const ElementRegion *ER = dyn_cast<ElementRegion>(Base)) { NonLoc index = ER->getIndex(); if (!index.isConstant()) hasSymbolicIndex = true; @@ -1303,11 +1633,7 @@ SVal RegionStoreManager::getBindingForFieldOrElementCommon(Store store, // 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)) { - superR = SR->getSuperRegion(); - continue; - } - break; + Base = dyn_cast<SubRegion>(Base->getSuperRegion()); } if (R->hasStackNonParametersStorage()) { @@ -1327,27 +1653,25 @@ SVal RegionStoreManager::getBindingForFieldOrElementCommon(Store store, // a symbolic offset. if (hasSymbolicIndex) return UnknownVal(); - - return UndefinedVal(); + + if (!hasPartialLazyBinding) + return UndefinedVal(); } // All other values are symbolic. return svalBuilder.getRegionValueSymbolVal(R); } -SVal RegionStoreManager::getBindingForObjCIvar(Store store, +SVal RegionStoreManager::getBindingForObjCIvar(RegionBindingsConstRef B, const ObjCIvarRegion* R) { - - // Check if the region has a binding. - RegionBindings B = GetRegionBindings(store); - - if (const Optional<SVal> &V = getDirectBinding(B, R)) + // Check if the region has a binding. + if (const Optional<SVal> &V = B.getDirectBinding(R)) return *V; const MemRegion *superR = R->getSuperRegion(); // Check if the super region has a default binding. - if (const Optional<SVal> &V = getDefaultBinding(B, superR)) { + if (const Optional<SVal> &V = B.getDefaultBinding(superR)) { if (SymbolRef parentSym = V->getAsSymbol()) return svalBuilder.getDerivedRegionValueSymbolVal(parentSym, R); @@ -1358,51 +1682,64 @@ SVal RegionStoreManager::getBindingForObjCIvar(Store store, return getBindingForLazySymbol(R); } -SVal RegionStoreManager::getBindingForVar(Store store, const VarRegion *R) { +static Optional<SVal> getConstValue(SValBuilder &SVB, const VarDecl *VD) { + ASTContext &Ctx = SVB.getContext(); + if (!VD->getType().isConstQualified()) + return None; - // Check if the region has a binding. - RegionBindings B = GetRegionBindings(store); + const Expr *Init = VD->getInit(); + if (!Init) + return None; + + llvm::APSInt Result; + if (!Init->isGLValue() && Init->EvaluateAsInt(Result, Ctx)) + return SVB.makeIntVal(Result); + + if (Init->isNullPointerConstant(Ctx, Expr::NPC_ValueDependentIsNotNull)) + return SVB.makeNull(); - if (const Optional<SVal> &V = getDirectBinding(B, R)) + // FIXME: Handle other possible constant expressions. + return None; +} + +SVal RegionStoreManager::getBindingForVar(RegionBindingsConstRef B, + const VarRegion *R) { + + // Check if the region has a binding. + if (const Optional<SVal> &V = B.getDirectBinding(R)) return *V; // Lazily derive a value for the VarRegion. const VarDecl *VD = R->getDecl(); - QualType T = VD->getType(); const MemSpaceRegion *MS = R->getMemorySpace(); - if (isa<UnknownSpaceRegion>(MS) || - isa<StackArgumentsSpaceRegion>(MS)) + // Arguments are always symbolic. + if (isa<StackArgumentsSpaceRegion>(MS)) + return svalBuilder.getRegionValueSymbolVal(R); + + // Is 'VD' declared constant? If so, retrieve the constant value. + if (Optional<SVal> V = getConstValue(svalBuilder, VD)) + return *V; + + // This must come after the check for constants because closure-captured + // constant variables may appear in UnknownSpaceRegion. + if (isa<UnknownSpaceRegion>(MS)) return svalBuilder.getRegionValueSymbolVal(R); if (isa<GlobalsSpaceRegion>(MS)) { - if (isa<NonStaticGlobalSpaceRegion>(MS)) { - // Is 'VD' declared constant? If so, retrieve the constant value. - QualType CT = Ctx.getCanonicalType(T); - if (CT.isConstQualified()) { - const Expr *Init = VD->getInit(); - // Do the null check first, as we want to call 'IgnoreParenCasts'. - if (Init) - if (const IntegerLiteral *IL = - dyn_cast<IntegerLiteral>(Init->IgnoreParenCasts())) { - const nonloc::ConcreteInt &V = svalBuilder.makeIntVal(IL); - return svalBuilder.evalCast(V, Init->getType(), IL->getType()); - } - } + QualType T = VD->getType(); - if (const Optional<SVal> &V - = getBindingForDerivedDefaultValue(B, MS, R, CT)) - return V.getValue(); + // Function-scoped static variables are default-initialized to 0; if they + // have an initializer, it would have been processed by now. + if (isa<StaticGlobalSpaceRegion>(MS)) + return svalBuilder.makeZeroVal(T); - return svalBuilder.getRegionValueSymbolVal(R); + if (Optional<SVal> V = getBindingForDerivedDefaultValue(B, MS, R, T)) { + assert(!V->getAs<nonloc::LazyCompoundVal>()); + return V.getValue(); } - if (T->isIntegerType()) - return svalBuilder.makeIntVal(0, T); - if (T->isPointerType()) - return svalBuilder.makeNull(); - - return UnknownVal(); + return svalBuilder.getRegionValueSymbolVal(R); } return UndefinedVal(); @@ -1413,55 +1750,77 @@ SVal RegionStoreManager::getBindingForLazySymbol(const TypedValueRegion *R) { return svalBuilder.getRegionValueSymbolVal(R); } -static bool mayHaveLazyBinding(QualType Ty) { - return Ty->isArrayType() || Ty->isStructureOrClassType(); +const RegionStoreManager::SValListTy & +RegionStoreManager::getInterestingValues(nonloc::LazyCompoundVal LCV) { + // First, check the cache. + LazyBindingsMapTy::iterator I = LazyBindingsMap.find(LCV.getCVData()); + if (I != LazyBindingsMap.end()) + return I->second; + + // If we don't have a list of values cached, start constructing it. + SValListTy List; + + const SubRegion *LazyR = LCV.getRegion(); + RegionBindingsRef B = getRegionBindings(LCV.getStore()); + + // If this region had /no/ bindings at the time, there are no interesting + // values to return. + const ClusterBindings *Cluster = B.lookup(LazyR->getBaseRegion()); + if (!Cluster) + return (LazyBindingsMap[LCV.getCVData()] = llvm_move(List)); + + SmallVector<BindingPair, 32> Bindings; + collectSubRegionBindings(Bindings, svalBuilder, *Cluster, LazyR, + /*IncludeAllDefaultBindings=*/true); + for (SmallVectorImpl<BindingPair>::const_iterator I = Bindings.begin(), + E = Bindings.end(); + I != E; ++I) { + SVal V = I->second; + if (V.isUnknownOrUndef() || V.isConstant()) + continue; + + if (Optional<nonloc::LazyCompoundVal> InnerLCV = + V.getAs<nonloc::LazyCompoundVal>()) { + const SValListTy &InnerList = getInterestingValues(*InnerLCV); + List.insert(List.end(), InnerList.begin(), InnerList.end()); + continue; + } + + List.push_back(V); + } + + return (LazyBindingsMap[LCV.getCVData()] = llvm_move(List)); +} + +NonLoc RegionStoreManager::createLazyBinding(RegionBindingsConstRef B, + const TypedValueRegion *R) { + if (Optional<nonloc::LazyCompoundVal> V = + getExistingLazyBinding(svalBuilder, B, R, false)) + return *V; + + return svalBuilder.makeLazyCompoundVal(StoreRef(B.asStore(), *this), R); } -SVal RegionStoreManager::getBindingForStruct(Store store, - const TypedValueRegion* R) { +SVal RegionStoreManager::getBindingForStruct(RegionBindingsConstRef B, + const TypedValueRegion *R) { const RecordDecl *RD = R->getValueType()->castAs<RecordType>()->getDecl(); if (RD->field_empty()) return UnknownVal(); - // If we already have a lazy binding, don't create a new one, - // unless the first field might have a lazy binding of its own. - // (Right now we can't tell the difference.) - QualType FirstFieldType = RD->field_begin()->getType(); - if (!mayHaveLazyBinding(FirstFieldType)) { - RegionBindings B = GetRegionBindings(store); - BindingKey K = BindingKey::Make(R, BindingKey::Default); - if (const nonloc::LazyCompoundVal *V = - dyn_cast_or_null<nonloc::LazyCompoundVal>(lookup(B, K))) { - return *V; - } - } - - return svalBuilder.makeLazyCompoundVal(StoreRef(store, *this), R); + return createLazyBinding(B, R); } -SVal RegionStoreManager::getBindingForArray(Store store, - const TypedValueRegion * R) { - const ConstantArrayType *Ty = Ctx.getAsConstantArrayType(R->getValueType()); - assert(Ty && "Only constant array types can have compound bindings."); +SVal RegionStoreManager::getBindingForArray(RegionBindingsConstRef B, + const TypedValueRegion *R) { + assert(Ctx.getAsConstantArrayType(R->getValueType()) && + "Only constant array types can have compound bindings."); - // If we already have a lazy binding, don't create a new one, - // unless the first element might have a lazy binding of its own. - // (Right now we can't tell the difference.) - if (!mayHaveLazyBinding(Ty->getElementType())) { - RegionBindings B = GetRegionBindings(store); - BindingKey K = BindingKey::Make(R, BindingKey::Default); - if (const nonloc::LazyCompoundVal *V = - dyn_cast_or_null<nonloc::LazyCompoundVal>(lookup(B, K))) { - return *V; - } - } - - return svalBuilder.makeLazyCompoundVal(StoreRef(store, *this), R); + return createLazyBinding(B, R); } bool RegionStoreManager::includedInBindings(Store store, const MemRegion *region) const { - RegionBindings B = GetRegionBindings(store); + RegionBindingsRef B = getRegionBindings(store); region = region->getBaseRegion(); // Quick path: if the base is the head of a cluster, the region is live. @@ -1469,7 +1828,7 @@ bool RegionStoreManager::includedInBindings(Store store, return true; // Slow path: if the region is the VALUE of any binding, it is live. - for (RegionBindings::iterator RI = B.begin(), RE = B.end(); RI != RE; ++RI) { + for (RegionBindingsRef::iterator RI = B.begin(), RE = B.end(); RI != RE; ++RI) { const ClusterBindings &Cluster = RI.getData(); for (ClusterBindings::iterator CI = Cluster.begin(), CE = Cluster.end(); CI != CE; ++CI) { @@ -1488,31 +1847,33 @@ bool RegionStoreManager::includedInBindings(Store store, //===----------------------------------------------------------------------===// StoreRef RegionStoreManager::killBinding(Store ST, Loc L) { - if (isa<loc::MemRegionVal>(L)) - if (const MemRegion* R = cast<loc::MemRegionVal>(L).getRegion()) - return StoreRef(removeBinding(GetRegionBindings(ST), - R).getRootWithoutRetain(), + if (Optional<loc::MemRegionVal> LV = L.getAs<loc::MemRegionVal>()) + if (const MemRegion* R = LV->getRegion()) + return StoreRef(getRegionBindings(ST).removeBinding(R) + .asImmutableMap() + .getRootWithoutRetain(), *this); return StoreRef(ST, *this); } -StoreRef RegionStoreManager::Bind(Store store, Loc L, SVal V) { - if (isa<loc::ConcreteInt>(L)) - return StoreRef(store, *this); +RegionBindingsRef +RegionStoreManager::bind(RegionBindingsConstRef B, Loc L, SVal V) { + if (L.getAs<loc::ConcreteInt>()) + return B; // If we get here, the location should be a region. - const MemRegion *R = cast<loc::MemRegionVal>(L).getRegion(); + const MemRegion *R = L.castAs<loc::MemRegionVal>().getRegion(); // Check if the region is a struct region. if (const TypedValueRegion* TR = dyn_cast<TypedValueRegion>(R)) { QualType Ty = TR->getValueType(); if (Ty->isArrayType()) - return BindArray(store, TR, V); + return bindArray(B, TR, V); if (Ty->isStructureOrClassType()) - return BindStruct(store, TR, V); + return bindStruct(B, TR, V); if (Ty->isVectorType()) - return BindVector(store, TR, V); + return bindVector(B, TR, V); } if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(R)) { @@ -1526,12 +1887,8 @@ StoreRef RegionStoreManager::Bind(Store store, Loc L, SVal V) { } // Clear out bindings that may overlap with this binding. - - // Perform the binding. - RegionBindings B = GetRegionBindings(store); - B = removeSubRegionBindings(B, cast<SubRegion>(R)); - BindingKey Key = BindingKey::Make(R, BindingKey::Direct); - return StoreRef(addBinding(B, Key, V).getRootWithoutRetain(), *this); + RegionBindingsRef NewB = removeSubRegionBindings(B, cast<SubRegion>(R)); + return NewB.addBinding(BindingKey::Make(R, BindingKey::Direct), V); } // FIXME: this method should be merged into Bind(). @@ -1542,10 +1899,10 @@ StoreRef RegionStoreManager::bindCompoundLiteral(Store ST, return Bind(ST, loc::MemRegionVal(MRMgr.getCompoundLiteralRegion(CL, LC)), V); } -StoreRef RegionStoreManager::setImplicitDefaultValue(Store store, - const MemRegion *R, - QualType T) { - RegionBindings B = GetRegionBindings(store); +RegionBindingsRef +RegionStoreManager::setImplicitDefaultValue(RegionBindingsConstRef B, + const MemRegion *R, + QualType T) { SVal V; if (Loc::isLocType(T)) @@ -1566,12 +1923,13 @@ StoreRef RegionStoreManager::setImplicitDefaultValue(Store store, V = UnknownVal(); } - return StoreRef(addBinding(B, R, BindingKey::Default, - V).getRootWithoutRetain(), *this); + return B.addBinding(R, BindingKey::Default, V); } -StoreRef RegionStoreManager::BindArray(Store store, const TypedValueRegion* R, - SVal Init) { +RegionBindingsRef +RegionStoreManager::bindArray(RegionBindingsConstRef B, + const TypedValueRegion* R, + SVal Init) { const ArrayType *AT =cast<ArrayType>(Ctx.getCanonicalType(R->getValueType())); QualType ElementTy = AT->getElementType(); @@ -1581,30 +1939,31 @@ StoreRef RegionStoreManager::BindArray(Store store, const TypedValueRegion* R, Size = CAT->getSize().getZExtValue(); // Check if the init expr is a string literal. - if (loc::MemRegionVal *MRV = dyn_cast<loc::MemRegionVal>(&Init)) { + if (Optional<loc::MemRegionVal> MRV = Init.getAs<loc::MemRegionVal>()) { const StringRegion *S = cast<StringRegion>(MRV->getRegion()); // Treat the string as a lazy compound value. - nonloc::LazyCompoundVal LCV = - cast<nonloc::LazyCompoundVal>(svalBuilder. - makeLazyCompoundVal(StoreRef(store, *this), S)); - return BindAggregate(store, R, LCV); + StoreRef store(B.asStore(), *this); + nonloc::LazyCompoundVal LCV = svalBuilder.makeLazyCompoundVal(store, S) + .castAs<nonloc::LazyCompoundVal>(); + return bindAggregate(B, R, LCV); } // Handle lazy compound values. - if (isa<nonloc::LazyCompoundVal>(Init)) - return BindAggregate(store, R, Init); + if (Init.getAs<nonloc::LazyCompoundVal>()) + return bindAggregate(B, R, Init); // Remaining case: explicit compound values. if (Init.isUnknown()) - return setImplicitDefaultValue(store, R, ElementTy); + return setImplicitDefaultValue(B, R, ElementTy); - nonloc::CompoundVal& CV = cast<nonloc::CompoundVal>(Init); + const nonloc::CompoundVal& CV = Init.castAs<nonloc::CompoundVal>(); nonloc::CompoundVal::iterator VI = CV.begin(), VE = CV.end(); uint64_t i = 0; - StoreRef newStore(store, *this); + RegionBindingsRef NewB(B); + for (; Size.hasValue() ? i < Size.getValue() : true ; ++i, ++VI) { // The init list might be shorter than the array length. if (VI == VE) @@ -1614,44 +1973,45 @@ StoreRef RegionStoreManager::BindArray(Store store, const TypedValueRegion* R, const ElementRegion *ER = MRMgr.getElementRegion(ElementTy, Idx, R, Ctx); if (ElementTy->isStructureOrClassType()) - newStore = BindStruct(newStore.getStore(), ER, *VI); + NewB = bindStruct(NewB, ER, *VI); else if (ElementTy->isArrayType()) - newStore = BindArray(newStore.getStore(), ER, *VI); + NewB = bindArray(NewB, ER, *VI); else - newStore = Bind(newStore.getStore(), svalBuilder.makeLoc(ER), *VI); + NewB = bind(NewB, svalBuilder.makeLoc(ER), *VI); } // If the init list is shorter than the array length, set the // array default value. if (Size.hasValue() && i < Size.getValue()) - newStore = setImplicitDefaultValue(newStore.getStore(), R, ElementTy); + NewB = setImplicitDefaultValue(NewB, R, ElementTy); - return newStore; + return NewB; } -StoreRef RegionStoreManager::BindVector(Store store, const TypedValueRegion* R, - SVal V) { +RegionBindingsRef RegionStoreManager::bindVector(RegionBindingsConstRef B, + const TypedValueRegion* R, + SVal V) { QualType T = R->getValueType(); assert(T->isVectorType()); const VectorType *VT = T->getAs<VectorType>(); // Use getAs for typedefs. // Handle lazy compound values and symbolic values. - if (isa<nonloc::LazyCompoundVal>(V) || isa<nonloc::SymbolVal>(V)) - return BindAggregate(store, R, V); + if (V.getAs<nonloc::LazyCompoundVal>() || V.getAs<nonloc::SymbolVal>()) + return bindAggregate(B, R, V); // We may get non-CompoundVal accidentally due to imprecise cast logic or // that we are binding symbolic struct value. Kill the field values, and if // the value is symbolic go and bind it as a "default" binding. - if (!isa<nonloc::CompoundVal>(V)) { - return BindAggregate(store, R, UnknownVal()); + if (!V.getAs<nonloc::CompoundVal>()) { + return bindAggregate(B, R, UnknownVal()); } QualType ElemType = VT->getElementType(); - nonloc::CompoundVal& CV = cast<nonloc::CompoundVal>(V); + nonloc::CompoundVal CV = V.castAs<nonloc::CompoundVal>(); nonloc::CompoundVal::iterator VI = CV.begin(), VE = CV.end(); unsigned index = 0, numElements = VT->getNumElements(); - StoreRef newStore(store, *this); - + RegionBindingsRef NewB(B); + for ( ; index != numElements ; ++index) { if (VI == VE) break; @@ -1660,20 +2020,20 @@ StoreRef RegionStoreManager::BindVector(Store store, const TypedValueRegion* R, const ElementRegion *ER = MRMgr.getElementRegion(ElemType, Idx, R, Ctx); if (ElemType->isArrayType()) - newStore = BindArray(newStore.getStore(), ER, *VI); + NewB = bindArray(NewB, ER, *VI); else if (ElemType->isStructureOrClassType()) - newStore = BindStruct(newStore.getStore(), ER, *VI); + NewB = bindStruct(NewB, ER, *VI); else - newStore = Bind(newStore.getStore(), svalBuilder.makeLoc(ER), *VI); + NewB = bind(NewB, svalBuilder.makeLoc(ER), *VI); } - return newStore; + return NewB; } -StoreRef RegionStoreManager::BindStruct(Store store, const TypedValueRegion* R, - SVal V) { - +RegionBindingsRef RegionStoreManager::bindStruct(RegionBindingsConstRef B, + const TypedValueRegion* R, + SVal V) { if (!Features.supportsFields()) - return StoreRef(store, *this); + return B; QualType T = R->getValueType(); assert(T->isStructureOrClassType()); @@ -1682,24 +2042,24 @@ StoreRef RegionStoreManager::BindStruct(Store store, const TypedValueRegion* R, RecordDecl *RD = RT->getDecl(); if (!RD->isCompleteDefinition()) - return StoreRef(store, *this); + return B; // Handle lazy compound values and symbolic values. - if (isa<nonloc::LazyCompoundVal>(V) || isa<nonloc::SymbolVal>(V)) - return BindAggregate(store, R, V); + if (V.getAs<nonloc::LazyCompoundVal>() || V.getAs<nonloc::SymbolVal>()) + return bindAggregate(B, R, V); // We may get non-CompoundVal accidentally due to imprecise cast logic or // that we are binding symbolic struct value. Kill the field values, and if // the value is symbolic go and bind it as a "default" binding. - if (V.isUnknown() || !isa<nonloc::CompoundVal>(V)) - return BindAggregate(store, R, UnknownVal()); + if (V.isUnknown() || !V.getAs<nonloc::CompoundVal>()) + return bindAggregate(B, R, UnknownVal()); - nonloc::CompoundVal& CV = cast<nonloc::CompoundVal>(V); + const nonloc::CompoundVal& CV = V.castAs<nonloc::CompoundVal>(); nonloc::CompoundVal::iterator VI = CV.begin(), VE = CV.end(); RecordDecl::field_iterator FI, FE; - StoreRef newStore(store, *this); - + RegionBindingsRef NewB(B); + for (FI = RD->field_begin(), FE = RD->field_end(); FI != FE; ++FI) { if (VI == VE) @@ -1713,95 +2073,30 @@ StoreRef RegionStoreManager::BindStruct(Store store, const TypedValueRegion* R, const FieldRegion* FR = MRMgr.getFieldRegion(*FI, R); if (FTy->isArrayType()) - newStore = BindArray(newStore.getStore(), FR, *VI); + NewB = bindArray(NewB, FR, *VI); else if (FTy->isStructureOrClassType()) - newStore = BindStruct(newStore.getStore(), FR, *VI); + NewB = bindStruct(NewB, FR, *VI); else - newStore = Bind(newStore.getStore(), svalBuilder.makeLoc(FR), *VI); + NewB = bind(NewB, svalBuilder.makeLoc(FR), *VI); ++VI; } // There may be fewer values in the initialize list than the fields of struct. if (FI != FE) { - RegionBindings B = GetRegionBindings(newStore.getStore()); - B = addBinding(B, R, BindingKey::Default, svalBuilder.makeIntVal(0, false)); - newStore = StoreRef(B.getRootWithoutRetain(), *this); + NewB = NewB.addBinding(R, BindingKey::Default, + svalBuilder.makeIntVal(0, false)); } - return newStore; + return NewB; } -StoreRef RegionStoreManager::BindAggregate(Store store, const TypedRegion *R, - SVal Val) { +RegionBindingsRef +RegionStoreManager::bindAggregate(RegionBindingsConstRef B, + const TypedRegion *R, + SVal Val) { // Remove the old bindings, using 'R' as the root of all regions // we will invalidate. Then add the new binding. - RegionBindings B = GetRegionBindings(store); - - B = removeSubRegionBindings(B, R); - B = addBinding(B, R, BindingKey::Default, Val); - - return StoreRef(B.getRootWithoutRetain(), *this); -} - -//===----------------------------------------------------------------------===// -// "Raw" retrievals and bindings. -//===----------------------------------------------------------------------===// - - -RegionBindings RegionStoreManager::addBinding(RegionBindings B, BindingKey K, - SVal V) { - const MemRegion *Base = K.getBaseRegion(); - - const ClusterBindings *ExistingCluster = B.lookup(Base); - ClusterBindings Cluster = (ExistingCluster ? *ExistingCluster - : CBFactory.getEmptyMap()); - - ClusterBindings NewCluster = CBFactory.add(Cluster, K, V); - return RBFactory.add(B, Base, NewCluster); -} - -RegionBindings RegionStoreManager::addBinding(RegionBindings B, - const MemRegion *R, - BindingKey::Kind k, SVal V) { - return addBinding(B, BindingKey::Make(R, k), V); -} - -const SVal *RegionStoreManager::lookup(RegionBindings B, BindingKey K) { - const ClusterBindings *Cluster = B.lookup(K.getBaseRegion()); - if (!Cluster) - return 0; - - return Cluster->lookup(K); -} - -const SVal *RegionStoreManager::lookup(RegionBindings B, - const MemRegion *R, - BindingKey::Kind k) { - return lookup(B, BindingKey::Make(R, k)); -} - -RegionBindings RegionStoreManager::removeBinding(RegionBindings B, - BindingKey K) { - const MemRegion *Base = K.getBaseRegion(); - const ClusterBindings *Cluster = B.lookup(Base); - if (!Cluster) - return B; - - ClusterBindings NewCluster = CBFactory.remove(*Cluster, K); - if (NewCluster.isEmpty()) - return RBFactory.remove(B, Base); - return RBFactory.add(B, Base, NewCluster); -} - -RegionBindings RegionStoreManager::removeBinding(RegionBindings B, - const MemRegion *R, - BindingKey::Kind k){ - return removeBinding(B, BindingKey::Make(R, k)); -} - -RegionBindings RegionStoreManager::removeCluster(RegionBindings B, - const MemRegion *Base) { - return RBFactory.remove(B, Base); + return removeSubRegionBindings(B, R).addBinding(R, BindingKey::Default, Val); } //===----------------------------------------------------------------------===// @@ -1818,7 +2113,7 @@ class removeDeadBindingsWorker : public: removeDeadBindingsWorker(RegionStoreManager &rm, ProgramStateManager &stateMgr, - RegionBindings b, SymbolReaper &symReaper, + RegionBindingsRef b, SymbolReaper &symReaper, const StackFrameContext *LCtx) : ClusterAnalysis<removeDeadBindingsWorker>(rm, stateMgr, b, /* includeGlobals = */ false), @@ -1826,7 +2121,8 @@ public: // Called by ClusterAnalysis. void VisitAddedToCluster(const MemRegion *baseR, const ClusterBindings &C); - void VisitCluster(const MemRegion *baseR, const ClusterBindings &C); + void VisitCluster(const MemRegion *baseR, const ClusterBindings *C); + using ClusterAnalysis<removeDeadBindingsWorker>::VisitCluster; bool UpdatePostponed(); void VisitBinding(SVal V); @@ -1869,38 +2165,30 @@ void removeDeadBindingsWorker::VisitAddedToCluster(const MemRegion *baseR, } void removeDeadBindingsWorker::VisitCluster(const MemRegion *baseR, - const ClusterBindings &C) { + const ClusterBindings *C) { + if (!C) + return; + // Mark the symbol for any SymbolicRegion with live bindings as live itself. // This means we should continue to track that symbol. if (const SymbolicRegion *SymR = dyn_cast<SymbolicRegion>(baseR)) SymReaper.markLive(SymR->getSymbol()); - for (ClusterBindings::iterator I = C.begin(), E = C.end(); I != E; ++I) + for (ClusterBindings::iterator I = C->begin(), E = C->end(); I != E; ++I) VisitBinding(I.getData()); } void removeDeadBindingsWorker::VisitBinding(SVal V) { // Is it a LazyCompoundVal? All referenced regions are live as well. - if (const nonloc::LazyCompoundVal *LCS = - dyn_cast<nonloc::LazyCompoundVal>(&V)) { + if (Optional<nonloc::LazyCompoundVal> LCS = + V.getAs<nonloc::LazyCompoundVal>()) { - const MemRegion *LazyR = LCS->getRegion(); - RegionBindings B = RegionStoreManager::GetRegionBindings(LCS->getStore()); + const RegionStoreManager::SValListTy &Vals = RM.getInterestingValues(*LCS); - // FIXME: This should not have to walk all bindings in the old store. - for (RegionBindings::iterator RI = B.begin(), RE = B.end(); RI != RE; ++RI){ - const ClusterBindings &Cluster = RI.getData(); - for (ClusterBindings::iterator CI = Cluster.begin(), CE = Cluster.end(); - CI != CE; ++CI) { - BindingKey K = CI.getKey(); - if (const SubRegion *BaseR = dyn_cast<SubRegion>(K.getRegion())) { - if (BaseR == LazyR) - VisitBinding(CI.getData()); - else if (K.hasSymbolicOffset() && BaseR->isSubRegionOf(LazyR)) - VisitBinding(CI.getData()); - } - } - } + for (RegionStoreManager::SValListTy::const_iterator I = Vals.begin(), + E = Vals.end(); + I != E; ++I) + VisitBinding(*I); return; } @@ -1946,7 +2234,7 @@ bool removeDeadBindingsWorker::UpdatePostponed() { StoreRef RegionStoreManager::removeDeadBindings(Store store, const StackFrameContext *LCtx, SymbolReaper& SymReaper) { - RegionBindings B = GetRegionBindings(store); + RegionBindingsRef B = getRegionBindings(store); removeDeadBindingsWorker W(*this, StateMgr, B, SymReaper, LCtx); W.GenerateClusters(); @@ -1961,7 +2249,7 @@ StoreRef RegionStoreManager::removeDeadBindings(Store store, // We have now scanned the store, marking reachable regions and symbols // as live. We now remove all the regions that are dead from the store // as well as update DSymbols with the set symbols that are now dead. - for (RegionBindings::iterator I = B.begin(), E = B.end(); I != E; ++I) { + for (RegionBindingsRef::iterator I = B.begin(), E = B.end(); I != E; ++I) { const MemRegion *Base = I.getKey(); // If the cluster has been visited, we know the region has been marked. @@ -1969,7 +2257,7 @@ StoreRef RegionStoreManager::removeDeadBindings(Store store, continue; // Remove the dead entry. - B = removeCluster(B, Base); + B = B.remove(Base); if (const SymbolicRegion *SymR = dyn_cast<SymbolicRegion>(Base)) SymReaper.maybeDead(SymR->getSymbol()); @@ -1985,7 +2273,7 @@ StoreRef RegionStoreManager::removeDeadBindings(Store store, } } - return StoreRef(B.getRootWithoutRetain(), *this); + return StoreRef(B.asStore(), *this); } //===----------------------------------------------------------------------===// @@ -1994,17 +2282,9 @@ StoreRef RegionStoreManager::removeDeadBindings(Store store, void RegionStoreManager::print(Store store, raw_ostream &OS, const char* nl, const char *sep) { - RegionBindings B = GetRegionBindings(store); + RegionBindingsRef B = getRegionBindings(store); OS << "Store (direct and default bindings), " - << (void*) B.getRootWithoutRetain() + << B.asStore() << " :" << nl; - - for (RegionBindings::iterator I = B.begin(), E = B.end(); I != E; ++I) { - const ClusterBindings &Cluster = I.getData(); - for (ClusterBindings::iterator CI = Cluster.begin(), CE = Cluster.end(); - CI != CE; ++CI) { - OS << ' ' << CI.getKey() << " : " << CI.getData() << nl; - } - OS << nl; - } + B.dump(OS, nl); } diff --git a/lib/StaticAnalyzer/Core/SValBuilder.cpp b/lib/StaticAnalyzer/Core/SValBuilder.cpp index b87169a4b335..c72e7808010e 100644 --- a/lib/StaticAnalyzer/Core/SValBuilder.cpp +++ b/lib/StaticAnalyzer/Core/SValBuilder.cpp @@ -12,13 +12,13 @@ // //===----------------------------------------------------------------------===// -#include "clang/AST/ExprCXX.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h" #include "clang/AST/DeclCXX.h" +#include "clang/AST/ExprCXX.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h" #include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" using namespace clang; using namespace ento; @@ -78,13 +78,13 @@ SVal SValBuilder::convertToArrayIndex(SVal val) { return val; // Common case: we have an appropriately sized integer. - if (nonloc::ConcreteInt* CI = dyn_cast<nonloc::ConcreteInt>(&val)) { + if (Optional<nonloc::ConcreteInt> CI = val.getAs<nonloc::ConcreteInt>()) { const llvm::APSInt& I = CI->getValue(); if (I.getBitWidth() == ArrayIndexWidth && I.isSigned()) return val; } - return evalCastFromNonLoc(cast<NonLoc>(val), ArrayIndexTy); + return evalCastFromNonLoc(val.castAs<NonLoc>(), ArrayIndexTy); } nonloc::ConcreteInt SValBuilder::makeBoolVal(const CXXBoolLiteralExpr *boolean){ @@ -237,11 +237,11 @@ SVal SValBuilder::makeSymExprValNN(ProgramStateRef State, return makeNonLoc(symLHS, Op, symRHS, ResultTy); if (symLHS && symLHS->computeComplexity() < MaxComp) - if (const nonloc::ConcreteInt *rInt = dyn_cast<nonloc::ConcreteInt>(&RHS)) + if (Optional<nonloc::ConcreteInt> rInt = RHS.getAs<nonloc::ConcreteInt>()) return makeNonLoc(symLHS, Op, rInt->getValue(), ResultTy); if (symRHS && symRHS->computeComplexity() < MaxComp) - if (const nonloc::ConcreteInt *lInt = dyn_cast<nonloc::ConcreteInt>(&LHS)) + if (Optional<nonloc::ConcreteInt> lInt = LHS.getAs<nonloc::ConcreteInt>()) return makeNonLoc(lInt->getValue(), Op, symRHS, ResultTy); return UnknownVal(); @@ -257,41 +257,42 @@ SVal SValBuilder::evalBinOp(ProgramStateRef state, BinaryOperator::Opcode op, if (lhs.isUnknown() || rhs.isUnknown()) return UnknownVal(); - if (isa<Loc>(lhs)) { - if (isa<Loc>(rhs)) - return evalBinOpLL(state, op, cast<Loc>(lhs), cast<Loc>(rhs), type); + if (Optional<Loc> LV = lhs.getAs<Loc>()) { + if (Optional<Loc> RV = rhs.getAs<Loc>()) + return evalBinOpLL(state, op, *LV, *RV, type); - return evalBinOpLN(state, op, cast<Loc>(lhs), cast<NonLoc>(rhs), type); + return evalBinOpLN(state, op, *LV, rhs.castAs<NonLoc>(), type); } - if (isa<Loc>(rhs)) { + if (Optional<Loc> RV = rhs.getAs<Loc>()) { // Support pointer arithmetic where the addend is on the left // and the pointer on the right. assert(op == BO_Add); // Commute the operands. - return evalBinOpLN(state, op, cast<Loc>(rhs), cast<NonLoc>(lhs), type); + return evalBinOpLN(state, op, *RV, lhs.castAs<NonLoc>(), type); } - return evalBinOpNN(state, op, cast<NonLoc>(lhs), cast<NonLoc>(rhs), type); + return evalBinOpNN(state, op, lhs.castAs<NonLoc>(), rhs.castAs<NonLoc>(), + type); } DefinedOrUnknownSVal SValBuilder::evalEQ(ProgramStateRef state, DefinedOrUnknownSVal lhs, DefinedOrUnknownSVal rhs) { - return cast<DefinedOrUnknownSVal>(evalBinOp(state, BO_EQ, lhs, rhs, - Context.IntTy)); + return evalBinOp(state, BO_EQ, lhs, rhs, Context.IntTy) + .castAs<DefinedOrUnknownSVal>(); } /// 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)) { +/// and restrict qualifiers. Also, assume that all types are similar to 'void'. +/// Assumes the input types are canonical. +static bool shouldBeModeledWithNoOp(ASTContext &Context, QualType ToTy, + QualType FromTy) { + while (Context.UnwrapSimilarPointerTypes(ToTy, FromTy)) { Qualifiers Quals1, Quals2; - T1 = Context.getUnqualifiedArrayType(T1, Quals1); - T2 = Context.getUnqualifiedArrayType(T2, Quals2); + ToTy = Context.getUnqualifiedArrayType(ToTy, Quals1); + FromTy = Context.getUnqualifiedArrayType(FromTy, Quals2); // Make sure that non cvr-qualifiers the other qualifiers (e.g., address // spaces) are identical. @@ -301,7 +302,12 @@ static bool haveSimilarTypes(ASTContext &Context, QualType T1, return false; } - if (T1 != T2) + // If we are casting to void, the 'From' value can be used to represent the + // 'To' value. + if (ToTy->isVoidType()) + return true; + + if (ToTy != FromTy) return false; return true; @@ -314,19 +320,19 @@ SVal SValBuilder::evalCast(SVal val, QualType castTy, QualType originalTy) { if (val.isUnknownOrUndef() || castTy == originalTy) return val; - // For const casts, just propagate the value. + // For const casts, casts to void, just propagate the value. if (!castTy->isVariableArrayType() && !originalTy->isVariableArrayType()) - if (haveSimilarTypes(Context, Context.getPointerType(castTy), - Context.getPointerType(originalTy))) + if (shouldBeModeledWithNoOp(Context, Context.getPointerType(castTy), + Context.getPointerType(originalTy))) return val; // Check for casts from pointers to integers. if (castTy->isIntegerType() && Loc::isLocType(originalTy)) - return evalCastFromLoc(cast<Loc>(val), castTy); + return evalCastFromLoc(val.castAs<Loc>(), castTy); // Check for casts from integers to pointers. if (Loc::isLocType(castTy) && originalTy->isIntegerType()) { - if (nonloc::LocAsInteger *LV = dyn_cast<nonloc::LocAsInteger>(&val)) { + if (Optional<nonloc::LocAsInteger> LV = val.getAs<nonloc::LocAsInteger>()) { if (const MemRegion *R = LV->getLoc().getAsRegion()) { StoreManager &storeMgr = StateMgr.getStoreManager(); R = storeMgr.castRegion(R, castTy); @@ -346,7 +352,7 @@ SVal SValBuilder::evalCast(SVal val, QualType castTy, QualType originalTy) { // Check for casts from array type to another type. if (originalTy->isArrayType()) { // We will always decay to a pointer. - val = StateMgr.ArrayToPointer(cast<Loc>(val)); + val = StateMgr.ArrayToPointer(val.castAs<Loc>()); // Are we casting from an array to a pointer? If so just pass on // the decayed value. @@ -361,7 +367,7 @@ SVal SValBuilder::evalCast(SVal val, QualType castTy, QualType originalTy) { // need the original decayed type. // QualType elemTy = cast<ArrayType>(originalTy)->getElementType(); // QualType pointerTy = C.getPointerType(elemTy); - return evalCastFromLoc(cast<Loc>(val), castTy); + return evalCastFromLoc(val.castAs<Loc>(), castTy); } // Check for casts from a region to a specific type. diff --git a/lib/StaticAnalyzer/Core/SVals.cpp b/lib/StaticAnalyzer/Core/SVals.cpp index e34ab6a2be91..38e216f28c06 100644 --- a/lib/StaticAnalyzer/Core/SVals.cpp +++ b/lib/StaticAnalyzer/Core/SVals.cpp @@ -15,6 +15,7 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" #include "clang/AST/ExprObjC.h" #include "clang/Basic/IdentifierTable.h" +#include "llvm/Support/raw_ostream.h" using namespace clang; using namespace ento; using llvm::APSInt; @@ -29,13 +30,13 @@ using llvm::APSInt; //===----------------------------------------------------------------------===// bool SVal::hasConjuredSymbol() const { - if (const nonloc::SymbolVal* SV = dyn_cast<nonloc::SymbolVal>(this)) { + if (Optional<nonloc::SymbolVal> SV = getAs<nonloc::SymbolVal>()) { SymbolRef sym = SV->getSymbol(); if (isa<SymbolConjured>(sym)) return true; } - if (const loc::MemRegionVal *RV = dyn_cast<loc::MemRegionVal>(this)) { + if (Optional<loc::MemRegionVal> RV = getAs<loc::MemRegionVal>()) { const MemRegion *R = RV->getRegion(); if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(R)) { SymbolRef sym = SR->getSymbol(); @@ -48,7 +49,7 @@ bool SVal::hasConjuredSymbol() const { } const FunctionDecl *SVal::getAsFunctionDecl() const { - if (const loc::MemRegionVal* X = dyn_cast<loc::MemRegionVal>(this)) { + if (Optional<loc::MemRegionVal> X = getAs<loc::MemRegionVal>()) { const MemRegion* R = X->getRegion(); if (const FunctionTextRegion *CTR = R->getAs<FunctionTextRegion>()) if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(CTR->getDecl())) @@ -65,10 +66,10 @@ const FunctionDecl *SVal::getAsFunctionDecl() const { /// 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)) + if (Optional<nonloc::LocAsInteger> X = getAs<nonloc::LocAsInteger>()) return X->getLoc().getAsLocSymbol(); - if (const loc::MemRegionVal *X = dyn_cast<loc::MemRegionVal>(this)) { + if (Optional<loc::MemRegionVal> X = getAs<loc::MemRegionVal>()) { const MemRegion *R = X->stripCasts(); if (const SymbolicRegion *SymR = dyn_cast<SymbolicRegion>(R)) return SymR->getSymbol(); @@ -78,7 +79,7 @@ SymbolRef SVal::getAsLocSymbol() const { /// Get the symbol in the SVal or its base region. SymbolRef SVal::getLocSymbolInBase() const { - const loc::MemRegionVal *X = dyn_cast<loc::MemRegionVal>(this); + Optional<loc::MemRegionVal> X = getAs<loc::MemRegionVal>(); if (!X) return 0; @@ -101,7 +102,7 @@ SymbolRef SVal::getLocSymbolInBase() const { /// Otherwise return 0. SymbolRef SVal::getAsSymbol() const { // FIXME: should we consider SymbolRef wrapped in CodeTextRegion? - if (const nonloc::SymbolVal *X = dyn_cast<nonloc::SymbolVal>(this)) + if (Optional<nonloc::SymbolVal> X = getAs<nonloc::SymbolVal>()) return X->getSymbol(); return getAsLocSymbol(); @@ -110,7 +111,7 @@ SymbolRef SVal::getAsSymbol() const { /// getAsSymbolicExpression - If this Sval wraps a symbolic expression then /// return that expression. Otherwise return NULL. const SymExpr *SVal::getAsSymbolicExpression() const { - if (const nonloc::SymbolVal *X = dyn_cast<nonloc::SymbolVal>(this)) + if (Optional<nonloc::SymbolVal> X = getAs<nonloc::SymbolVal>()) return X->getSymbol(); return getAsSymbol(); @@ -124,12 +125,11 @@ const SymExpr* SVal::getAsSymExpr() const { } const MemRegion *SVal::getAsRegion() const { - if (const loc::MemRegionVal *X = dyn_cast<loc::MemRegionVal>(this)) + if (Optional<loc::MemRegionVal> X = getAs<loc::MemRegionVal>()) return X->getRegion(); - if (const nonloc::LocAsInteger *X = dyn_cast<nonloc::LocAsInteger>(this)) { + if (Optional<nonloc::LocAsInteger> X = getAs<nonloc::LocAsInteger>()) return X->getLoc().getAsRegion(); - } return 0; } @@ -143,7 +143,7 @@ const void *nonloc::LazyCompoundVal::getStore() const { return static_cast<const LazyCompoundValData*>(Data)->getStore(); } -const TypedRegion *nonloc::LazyCompoundVal::getRegion() const { +const TypedValueRegion *nonloc::LazyCompoundVal::getRegion() const { return static_cast<const LazyCompoundValData*>(Data)->getRegion(); } @@ -164,16 +164,15 @@ nonloc::CompoundVal::iterator nonloc::CompoundVal::end() const { //===----------------------------------------------------------------------===// bool SVal::isConstant() const { - return isa<nonloc::ConcreteInt>(this) || isa<loc::ConcreteInt>(this); + return getAs<nonloc::ConcreteInt>() || getAs<loc::ConcreteInt>(); } bool SVal::isConstant(int I) const { - if (isa<loc::ConcreteInt>(*this)) - return cast<loc::ConcreteInt>(*this).getValue() == I; - else if (isa<nonloc::ConcreteInt>(*this)) - return cast<nonloc::ConcreteInt>(*this).getValue() == I; - else - return false; + if (Optional<loc::ConcreteInt> LV = getAs<loc::ConcreteInt>()) + return LV->getValue() == I; + if (Optional<nonloc::ConcreteInt> NV = getAs<nonloc::ConcreteInt>()) + return NV->getValue() == I; + return false; } bool SVal::isZeroConstant() const { @@ -215,13 +214,12 @@ SVal loc::ConcreteInt::evalBinOp(BasicValueFactory& BasicVals, BinaryOperator::Opcode Op, const loc::ConcreteInt& R) const { - assert (Op == BO_Add || Op == BO_Sub || - (Op >= BO_LT && Op <= BO_NE)); + assert(BinaryOperator::isComparisonOp(Op) || Op == BO_Sub); - const llvm::APSInt* X = BasicVals.evalAPSInt(Op, getValue(), R.getValue()); + const llvm::APSInt *X = BasicVals.evalAPSInt(Op, getValue(), R.getValue()); if (X) - return loc::ConcreteInt(*X); + return nonloc::ConcreteInt(*X); else return UndefinedVal(); } @@ -238,10 +236,10 @@ void SVal::dumpToStream(raw_ostream &os) const { os << "Unknown"; break; case NonLocKind: - cast<NonLoc>(this)->dumpToStream(os); + castAs<NonLoc>().dumpToStream(os); break; case LocKind: - cast<Loc>(this)->dumpToStream(os); + castAs<Loc>().dumpToStream(os); break; case UndefinedKind: os << "Undefined"; @@ -252,7 +250,7 @@ void SVal::dumpToStream(raw_ostream &os) const { void NonLoc::dumpToStream(raw_ostream &os) const { switch (getSubKind()) { case nonloc::ConcreteIntKind: { - const nonloc::ConcreteInt& C = *cast<nonloc::ConcreteInt>(this); + const nonloc::ConcreteInt& C = castAs<nonloc::ConcreteInt>(); if (C.getValue().isUnsigned()) os << C.getValue().getZExtValue(); else @@ -262,16 +260,16 @@ void NonLoc::dumpToStream(raw_ostream &os) const { break; } case nonloc::SymbolValKind: { - os << cast<nonloc::SymbolVal>(this)->getSymbol(); + os << castAs<nonloc::SymbolVal>().getSymbol(); break; } case nonloc::LocAsIntegerKind: { - const nonloc::LocAsInteger& C = *cast<nonloc::LocAsInteger>(this); + const nonloc::LocAsInteger& C = castAs<nonloc::LocAsInteger>(); os << C.getLoc() << " [as " << C.getNumBits() << " bit integer]"; break; } case nonloc::CompoundValKind: { - const nonloc::CompoundVal& C = *cast<nonloc::CompoundVal>(this); + const nonloc::CompoundVal& C = castAs<nonloc::CompoundVal>(); os << "compoundVal{"; bool first = true; for (nonloc::CompoundVal::iterator I=C.begin(), E=C.end(); I!=E; ++I) { @@ -287,7 +285,7 @@ void NonLoc::dumpToStream(raw_ostream &os) const { break; } case nonloc::LazyCompoundValKind: { - const nonloc::LazyCompoundVal &C = *cast<nonloc::LazyCompoundVal>(this); + const nonloc::LazyCompoundVal &C = castAs<nonloc::LazyCompoundVal>(); os << "lazyCompoundVal{" << const_cast<void *>(C.getStore()) << ',' << C.getRegion() << '}'; @@ -302,13 +300,13 @@ void NonLoc::dumpToStream(raw_ostream &os) const { void Loc::dumpToStream(raw_ostream &os) const { switch (getSubKind()) { case loc::ConcreteIntKind: - os << cast<loc::ConcreteInt>(this)->getValue().getZExtValue() << " (Loc)"; + os << castAs<loc::ConcreteInt>().getValue().getZExtValue() << " (Loc)"; break; case loc::GotoLabelKind: - os << "&&" << cast<loc::GotoLabel>(this)->getLabel()->getName(); + os << "&&" << castAs<loc::GotoLabel>().getLabel()->getName(); break; case loc::MemRegionKind: - os << '&' << cast<loc::MemRegionVal>(this)->getRegion()->getString(); + os << '&' << castAs<loc::MemRegionVal>().getRegion()->getString(); break; default: llvm_unreachable("Pretty-printing not implemented for this Loc."); diff --git a/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp b/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp index 4236ee470af4..9b759df48f28 100644 --- a/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp +++ b/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp @@ -24,7 +24,7 @@ namespace ento { SimpleConstraintManager::~SimpleConstraintManager() {} bool SimpleConstraintManager::canReasonAbout(SVal X) const { - nonloc::SymbolVal *SymVal = dyn_cast<nonloc::SymbolVal>(&X); + Optional<nonloc::SymbolVal> SymVal = X.getAs<nonloc::SymbolVal>(); if (SymVal && SymVal->isExpression()) { const SymExpr *SE = SymVal->getSymbol(); @@ -49,6 +49,16 @@ bool SimpleConstraintManager::canReasonAbout(SVal X) const { } } + if (const SymSymExpr *SSE = dyn_cast<SymSymExpr>(SE)) { + if (BinaryOperator::isComparisonOp(SSE->getOpcode())) { + // We handle Loc <> Loc comparisons, but not (yet) NonLoc <> NonLoc. + if (Loc::isLocType(SSE->getLHS()->getType())) { + assert(Loc::isLocType(SSE->getRHS()->getType())); + return true; + } + } + } + return false; } @@ -58,10 +68,9 @@ bool SimpleConstraintManager::canReasonAbout(SVal X) const { ProgramStateRef SimpleConstraintManager::assume(ProgramStateRef state, DefinedSVal Cond, bool Assumption) { - if (isa<NonLoc>(Cond)) - return assume(state, cast<NonLoc>(Cond), Assumption); - else - return assume(state, cast<Loc>(Cond), Assumption); + if (Optional<NonLoc> NV = Cond.getAs<NonLoc>()) + return assume(state, *NV, Assumption); + return assume(state, Cond.castAs<Loc>(), Assumption); } ProgramStateRef SimpleConstraintManager::assume(ProgramStateRef state, Loc cond, @@ -82,7 +91,7 @@ ProgramStateRef SimpleConstraintManager::assumeAux(ProgramStateRef state, case loc::MemRegionKind: { // FIXME: Should this go into the storemanager? - const MemRegion *R = cast<loc::MemRegionVal>(Cond).getRegion(); + const MemRegion *R = Cond.castAs<loc::MemRegionVal>().getRegion(); const SubRegion *SubR = dyn_cast<SubRegion>(R); while (SubR) { @@ -104,7 +113,7 @@ ProgramStateRef SimpleConstraintManager::assumeAux(ProgramStateRef state, return Assumption ? state : NULL; case loc::ConcreteIntKind: { - bool b = cast<loc::ConcreteInt>(Cond).getValue() != 0; + bool b = Cond.castAs<loc::ConcreteInt>().getValue() != 0; bool isFeasible = b ? Assumption : !Assumption; return isFeasible ? state : NULL; } @@ -120,21 +129,6 @@ ProgramStateRef SimpleConstraintManager::assume(ProgramStateRef state, return state; } -static BinaryOperator::Opcode NegateComparison(BinaryOperator::Opcode op) { - // FIXME: This should probably be part of BinaryOperator, since this isn't - // the only place it's used. (This code was copied from SimpleSValBuilder.cpp.) - switch (op) { - default: - llvm_unreachable("Invalid opcode."); - case BO_LT: return BO_GE; - case BO_GT: return BO_LE; - case BO_LE: return BO_GT; - case BO_GE: return BO_LT; - case BO_EQ: return BO_NE; - case BO_NE: return BO_EQ; - } -} - ProgramStateRef SimpleConstraintManager::assumeAuxForSymbol(ProgramStateRef State, @@ -165,14 +159,12 @@ ProgramStateRef SimpleConstraintManager::assumeAux(ProgramStateRef state, return assumeAuxForSymbol(state, sym, Assumption); } - BasicValueFactory &BasicVals = getBasicVals(); - switch (Cond.getSubKind()) { default: llvm_unreachable("'Assume' not implemented for this NonLoc"); case nonloc::SymbolValKind: { - nonloc::SymbolVal& SV = cast<nonloc::SymbolVal>(Cond); + nonloc::SymbolVal SV = Cond.castAs<nonloc::SymbolVal>(); SymbolRef sym = SV.getSymbol(); assert(sym); @@ -181,36 +173,55 @@ ProgramStateRef SimpleConstraintManager::assumeAux(ProgramStateRef state, return assumeAuxForSymbol(state, sym, Assumption); // Handle symbolic expression. - } else { + } else if (const SymIntExpr *SE = dyn_cast<SymIntExpr>(sym)) { // 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 = SE->getType(); - const llvm::APSInt &zero = BasicVals.getValue(0, T); - op = (Assumption ? BO_NE : BO_EQ); - return assumeSymRel(state, SE, op, zero); + if (BinaryOperator::isComparisonOp(op)) { + if (!Assumption) + op = BinaryOperator::negateComparisonOp(op); + + 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()); + } else if (const SymSymExpr *SSE = dyn_cast<SymSymExpr>(sym)) { + // Translate "a != b" to "(b - a) != 0". + // We invert the order of the operands as a heuristic for how loop + // conditions are usually written ("begin != end") as compared to length + // calculations ("end - begin"). The more correct thing to do would be to + // canonicalize "a - b" and "b - a", which would allow us to treat + // "a != b" and "b != a" the same. + SymbolManager &SymMgr = getSymbolManager(); + BinaryOperator::Opcode Op = SSE->getOpcode(); + assert(BinaryOperator::isComparisonOp(Op)); + + // For now, we only support comparing pointers. + assert(Loc::isLocType(SSE->getLHS()->getType())); + assert(Loc::isLocType(SSE->getRHS()->getType())); + QualType DiffTy = SymMgr.getContext().getPointerDiffType(); + SymbolRef Subtraction = SymMgr.getSymSymExpr(SSE->getRHS(), BO_Sub, + SSE->getLHS(), DiffTy); + + const llvm::APSInt &Zero = getBasicVals().getValue(0, DiffTy); + Op = BinaryOperator::reverseComparisonOp(Op); + if (!Assumption) + Op = BinaryOperator::negateComparisonOp(Op); + return assumeSymRel(state, Subtraction, Op, Zero); } + + // If we get here, there's nothing else we can do but treat the symbol as + // opaque. + return assumeAuxForSymbol(state, sym, Assumption); } case nonloc::ConcreteIntKind: { - bool b = cast<nonloc::ConcreteInt>(Cond).getValue() != 0; + bool b = Cond.castAs<nonloc::ConcreteInt>().getValue() != 0; bool isFeasible = b ? Assumption : !Assumption; return isFeasible ? state : NULL; } case nonloc::LocAsIntegerKind: - return assumeAux(state, cast<nonloc::LocAsInteger>(Cond).getLoc(), + return assumeAux(state, Cond.castAs<nonloc::LocAsInteger>().getLoc(), Assumption); } // end switch } @@ -258,10 +269,14 @@ ProgramStateRef SimpleConstraintManager::assumeSymRel(ProgramStateRef state, APSIntType ComparisonType = std::max(WraparoundType, APSIntType(Int)); llvm::APSInt ConvertedInt = ComparisonType.convert(Int); + // Prefer unsigned comparisons. + if (ComparisonType.getBitWidth() == WraparoundType.getBitWidth() && + ComparisonType.isUnsigned() && !WraparoundType.isUnsigned()) + Adjustment.setIsSigned(false); + switch (op) { default: - // No logic yet for other operators. assume the constraint is feasible. - return state; + llvm_unreachable("invalid operation not caught by assertion above"); case BO_EQ: return assumeSymEQ(state, Sym, ConvertedInt, Adjustment); diff --git a/lib/StaticAnalyzer/Core/SimpleConstraintManager.h b/lib/StaticAnalyzer/Core/SimpleConstraintManager.h index 01f0b4e4461f..10ddef1341c5 100644 --- a/lib/StaticAnalyzer/Core/SimpleConstraintManager.h +++ b/lib/StaticAnalyzer/Core/SimpleConstraintManager.h @@ -23,10 +23,10 @@ namespace ento { class SimpleConstraintManager : public ConstraintManager { SubEngine *SU; - BasicValueFactory &BVF; + SValBuilder &SVB; public: - SimpleConstraintManager(SubEngine *subengine, BasicValueFactory &BV) - : SU(subengine), BVF(BV) {} + SimpleConstraintManager(SubEngine *subengine, SValBuilder &SB) + : SU(subengine), SVB(SB) {} virtual ~SimpleConstraintManager(); //===------------------------------------------------------------------===// @@ -81,7 +81,8 @@ protected: // Internal implementation. //===------------------------------------------------------------------===// - BasicValueFactory &getBasicVals() const { return BVF; } + BasicValueFactory &getBasicVals() const { return SVB.getBasicValueFactory(); } + SymbolManager &getSymbolManager() const { return SVB.getSymbolManager(); } bool canReasonAbout(SVal X) const; diff --git a/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp b/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp index fbc6ba055105..5cc8926a4449 100644 --- a/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp +++ b/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp @@ -11,8 +11,8 @@ // //===----------------------------------------------------------------------===// -#include "clang/StaticAnalyzer/Core/PathSensitive/APSIntType.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/APSIntType.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" using namespace clang; @@ -60,16 +60,16 @@ SValBuilder *ento::createSimpleSValBuilder(llvm::BumpPtrAllocator &alloc, //===----------------------------------------------------------------------===// 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); + assert(Val.getAs<Loc>() || Val.getAs<NonLoc>()); + return Val.getAs<Loc>() ? evalCastFromLoc(Val.castAs<Loc>(), CastTy) + : evalCastFromNonLoc(Val.castAs<NonLoc>(), CastTy); } SVal SimpleSValBuilder::evalCastFromNonLoc(NonLoc val, QualType castTy) { bool isLocType = Loc::isLocType(castTy); - if (nonloc::LocAsInteger *LI = dyn_cast<nonloc::LocAsInteger>(&val)) { + if (Optional<nonloc::LocAsInteger> LI = val.getAs<nonloc::LocAsInteger>()) { if (isLocType) return LI->getLoc(); @@ -98,15 +98,21 @@ SVal SimpleSValBuilder::evalCastFromNonLoc(NonLoc val, QualType castTy) { } // If value is a non integer constant, produce unknown. - if (!isa<nonloc::ConcreteInt>(val)) + if (!val.getAs<nonloc::ConcreteInt>()) return UnknownVal(); + // Handle casts to a boolean type. + if (castTy->isBooleanType()) { + bool b = val.castAs<nonloc::ConcreteInt>().getValue().getBoolValue(); + return makeTruthVal(b, castTy); + } + // 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(); - llvm::APSInt i = cast<nonloc::ConcreteInt>(val).getValue(); + llvm::APSInt i = val.castAs<nonloc::ConcreteInt>().getValue(); BasicVals.getAPSIntType(castTy).apply(i); if (isLocType) @@ -134,10 +140,10 @@ SVal SimpleSValBuilder::evalCastFromLoc(Loc val, QualType castTy) { if (castTy->isIntegerType()) { unsigned BitWidth = Context.getTypeSize(castTy); - if (!isa<loc::ConcreteInt>(val)) + if (!val.getAs<loc::ConcreteInt>()) return makeLocAsInteger(val, BitWidth); - llvm::APSInt i = cast<loc::ConcreteInt>(val).getValue(); + llvm::APSInt i = val.castAs<loc::ConcreteInt>().getValue(); BasicVals.getAPSIntType(castTy).apply(i); return makeIntVal(i); } @@ -155,7 +161,7 @@ SVal SimpleSValBuilder::evalCastFromLoc(Loc val, QualType castTy) { SVal SimpleSValBuilder::evalMinus(NonLoc val) { switch (val.getSubKind()) { case nonloc::ConcreteIntKind: - return cast<nonloc::ConcreteInt>(val).evalMinus(*this); + return val.castAs<nonloc::ConcreteInt>().evalMinus(*this); default: return UnknownVal(); } @@ -164,7 +170,7 @@ SVal SimpleSValBuilder::evalMinus(NonLoc val) { SVal SimpleSValBuilder::evalComplement(NonLoc X) { switch (X.getSubKind()) { case nonloc::ConcreteIntKind: - return cast<nonloc::ConcreteInt>(X).evalComplement(*this); + return X.castAs<nonloc::ConcreteInt>().evalComplement(*this); default: return UnknownVal(); } @@ -174,33 +180,6 @@ SVal SimpleSValBuilder::evalComplement(NonLoc X) { // Transfer function for binary operators. //===----------------------------------------------------------------------===// -static BinaryOperator::Opcode NegateComparison(BinaryOperator::Opcode op) { - switch (op) { - default: - llvm_unreachable("Invalid opcode."); - case BO_LT: return BO_GE; - case BO_GT: return BO_LE; - case BO_LE: return BO_GT; - case BO_GE: return BO_LT; - case BO_EQ: return BO_NE; - case BO_NE: return BO_EQ; - } -} - -static BinaryOperator::Opcode ReverseComparison(BinaryOperator::Opcode op) { - switch (op) { - default: - llvm_unreachable("Invalid opcode."); - case BO_LT: return BO_GT; - case BO_GT: return BO_LT; - case BO_LE: return BO_GE; - case BO_GE: return BO_LE; - case BO_EQ: - case BO_NE: - return op; - } -} - SVal SimpleSValBuilder::MakeSymIntVal(const SymExpr *LHS, BinaryOperator::Opcode op, const llvm::APSInt &RHS, @@ -331,15 +310,15 @@ SVal SimpleSValBuilder::evalBinOpNN(ProgramStateRef state, default: return makeSymExprValNN(state, op, lhs, rhs, resultTy); case nonloc::LocAsIntegerKind: { - Loc lhsL = cast<nonloc::LocAsInteger>(lhs).getLoc(); + Loc lhsL = lhs.castAs<nonloc::LocAsInteger>().getLoc(); switch (rhs.getSubKind()) { case nonloc::LocAsIntegerKind: return evalBinOpLL(state, op, lhsL, - cast<nonloc::LocAsInteger>(rhs).getLoc(), + rhs.castAs<nonloc::LocAsInteger>().getLoc(), resultTy); case nonloc::ConcreteIntKind: { // Transform the integer into a location and compare. - llvm::APSInt i = cast<nonloc::ConcreteInt>(rhs).getValue(); + llvm::APSInt i = rhs.castAs<nonloc::ConcreteInt>().getValue(); BasicVals.getAPSIntType(Context.VoidPtrTy).apply(i); return evalBinOpLL(state, op, lhsL, makeLoc(i), resultTy); } @@ -356,7 +335,7 @@ SVal SimpleSValBuilder::evalBinOpNN(ProgramStateRef state, } } case nonloc::ConcreteIntKind: { - llvm::APSInt LHSValue = cast<nonloc::ConcreteInt>(lhs).getValue(); + llvm::APSInt LHSValue = lhs.castAs<nonloc::ConcreteInt>().getValue(); // If we're dealing with two known constants, just perform the operation. if (const llvm::APSInt *KnownRHSValue = getKnownValue(state, rhs)) { @@ -392,7 +371,7 @@ SVal SimpleSValBuilder::evalBinOpNN(ProgramStateRef state, case BO_GT: case BO_LE: case BO_GE: - op = ReverseComparison(op); + op = BinaryOperator::reverseComparisonOp(op); // FALL-THROUGH case BO_EQ: case BO_NE: @@ -419,7 +398,7 @@ SVal SimpleSValBuilder::evalBinOpNN(ProgramStateRef state, } case nonloc::SymbolValKind: { // We only handle LHS as simple symbols or SymIntExprs. - SymbolRef Sym = cast<nonloc::SymbolVal>(lhs).getSymbol(); + SymbolRef Sym = lhs.castAs<nonloc::SymbolVal>().getSymbol(); // LHS is a symbolic expression. if (const SymIntExpr *symIntExpr = dyn_cast<SymIntExpr>(Sym)) { @@ -460,7 +439,7 @@ SVal SimpleSValBuilder::evalBinOpNN(ProgramStateRef state, case BO_EQ: case BO_NE: // Negate the comparison and make a value. - opc = NegateComparison(opc); + opc = BinaryOperator::negateComparisonOp(opc); assert(symIntExpr->getType() == resultTy); return makeNonLoc(symIntExpr->getLHS(), opc, symIntExpr->getRHS(), resultTy); @@ -502,22 +481,21 @@ SVal SimpleSValBuilder::evalBinOpNN(ProgramStateRef state, // Otherwise, make a SymIntExpr out of the expression. return MakeSymIntVal(symIntExpr, op, *RHSValue, resultTy); } + } - - } else if (isa<SymbolData>(Sym)) { - // Does the symbol simplify to a constant? If so, "fold" the constant - // by setting 'lhs' to a ConcreteInt and try again. - if (const llvm::APSInt *Constant = state->getConstraintManager() - .getSymVal(state, Sym)) { - lhs = nonloc::ConcreteInt(*Constant); - continue; - } - - // Is the RHS a constant? - if (const llvm::APSInt *RHSValue = getKnownValue(state, rhs)) - return MakeSymIntVal(Sym, op, *RHSValue, resultTy); + // Does the symbolic expression simplify to a constant? + // If so, "fold" the constant by setting 'lhs' to a ConcreteInt + // and try again. + ConstraintManager &CMgr = state->getConstraintManager(); + if (const llvm::APSInt *Constant = CMgr.getSymVal(state, Sym)) { + lhs = nonloc::ConcreteInt(*Constant); + continue; } + // Is the RHS a constant? + if (const llvm::APSInt *RHSValue = getKnownValue(state, rhs)) + return MakeSymIntVal(Sym, op, *RHSValue, resultTy); + // Give up -- this is not a symbolic expression we can handle. return makeSymExprValNN(state, op, InputLHS, InputRHS, resultTy); } @@ -595,25 +573,27 @@ SVal SimpleSValBuilder::evalBinOpLL(ProgramStateRef state, if (!BinaryOperator::isComparisonOp(op)) return UnknownVal(); - const llvm::APSInt &lVal = cast<loc::ConcreteInt>(lhs).getValue(); - return makeNonLoc(rSym, ReverseComparison(op), lVal, resultTy); + const llvm::APSInt &lVal = lhs.castAs<loc::ConcreteInt>().getValue(); + op = BinaryOperator::reverseComparisonOp(op); + return makeNonLoc(rSym, op, lVal, resultTy); } // If both operands are constants, just perform the operation. - if (loc::ConcreteInt *rInt = dyn_cast<loc::ConcreteInt>(&rhs)) { - SVal ResultVal = cast<loc::ConcreteInt>(lhs).evalBinOp(BasicVals, op, - *rInt); - if (Loc *Result = dyn_cast<Loc>(&ResultVal)) - return evalCastFromLoc(*Result, resultTy); - else - return UnknownVal(); + if (Optional<loc::ConcreteInt> rInt = rhs.getAs<loc::ConcreteInt>()) { + SVal ResultVal = + lhs.castAs<loc::ConcreteInt>().evalBinOp(BasicVals, op, *rInt); + if (Optional<NonLoc> Result = ResultVal.getAs<NonLoc>()) + return evalCastFromNonLoc(*Result, resultTy); + + assert(!ResultVal.getAs<Loc>() && "Loc-Loc ops should not produce Locs"); + return UnknownVal(); } // Special case comparisons against NULL. // This must come after the test if the RHS is a symbol, which is used to // build constraints. The address of any non-symbolic region is guaranteed // to be non-NULL, as is any label. - assert(isa<loc::MemRegionVal>(rhs) || isa<loc::GotoLabel>(rhs)); + assert(rhs.getAs<loc::MemRegionVal>() || rhs.getAs<loc::GotoLabel>()); if (lhs.isZeroConstant()) { switch (op) { default: @@ -634,7 +614,7 @@ SVal SimpleSValBuilder::evalBinOpLL(ProgramStateRef state, return UnknownVal(); } case loc::MemRegionKind: { - if (loc::ConcreteInt *rInt = dyn_cast<loc::ConcreteInt>(&rhs)) { + if (Optional<loc::ConcreteInt> rInt = rhs.getAs<loc::ConcreteInt>()) { // If one of the operands is a symbol and the other is a constant, // build an expression for use by the constraint manager. if (SymbolRef lSym = lhs.getAsLocSymbol()) @@ -676,11 +656,11 @@ SVal SimpleSValBuilder::evalBinOpLL(ProgramStateRef state, // regions, though. return UnknownVal(); - const MemSpaceRegion *LeftMS = LeftMR->getMemorySpace(); - const MemSpaceRegion *RightMS = RightMR->getMemorySpace(); - const MemSpaceRegion *UnknownMS = MemMgr.getUnknownRegion(); const MemRegion *LeftBase = LeftMR->getBaseRegion(); const MemRegion *RightBase = RightMR->getBaseRegion(); + const MemSpaceRegion *LeftMS = LeftBase->getMemorySpace(); + const MemSpaceRegion *RightMS = RightBase->getMemorySpace(); + const MemSpaceRegion *UnknownMS = MemMgr.getUnknownRegion(); // If the two regions are from different known memory spaces they cannot be // equal. Also, assume that no symbolic region (whose memory space is @@ -732,21 +712,21 @@ SVal SimpleSValBuilder::evalBinOpLL(ProgramStateRef state, // Get the left index and cast it to the correct type. // If the index is unknown or undefined, bail out here. SVal LeftIndexVal = LeftER->getIndex(); - NonLoc *LeftIndex = dyn_cast<NonLoc>(&LeftIndexVal); + Optional<NonLoc> LeftIndex = LeftIndexVal.getAs<NonLoc>(); if (!LeftIndex) return UnknownVal(); - LeftIndexVal = evalCastFromNonLoc(*LeftIndex, resultTy); - LeftIndex = dyn_cast<NonLoc>(&LeftIndexVal); + LeftIndexVal = evalCastFromNonLoc(*LeftIndex, ArrayIndexTy); + LeftIndex = LeftIndexVal.getAs<NonLoc>(); if (!LeftIndex) return UnknownVal(); // Do the same for the right index. SVal RightIndexVal = RightER->getIndex(); - NonLoc *RightIndex = dyn_cast<NonLoc>(&RightIndexVal); + Optional<NonLoc> RightIndex = RightIndexVal.getAs<NonLoc>(); if (!RightIndex) return UnknownVal(); - RightIndexVal = evalCastFromNonLoc(*RightIndex, resultTy); - RightIndex = dyn_cast<NonLoc>(&RightIndexVal); + RightIndexVal = evalCastFromNonLoc(*RightIndex, ArrayIndexTy); + RightIndex = RightIndexVal.getAs<NonLoc>(); if (!RightIndex) return UnknownVal(); @@ -783,7 +763,6 @@ SVal SimpleSValBuilder::evalBinOpLL(ProgramStateRef state, } // If we get here, we have no way of comparing the ElementRegions. - return UnknownVal(); } // See if both regions are fields of the same structure. @@ -836,6 +815,13 @@ SVal SimpleSValBuilder::evalBinOpLL(ProgramStateRef state, llvm_unreachable("Fields not found in parent record's definition"); } + // At this point we're not going to get a good answer, but we can try + // conjuring an expression instead. + SymbolRef LHSSym = lhs.getAsLocSymbol(); + SymbolRef RHSSym = rhs.getAsLocSymbol(); + if (LHSSym && RHSSym) + return makeNonLoc(LHSSym, op, RHSSym, resultTy); + // If we get here, we have no way of comparing the regions. return UnknownVal(); } @@ -852,11 +838,12 @@ SVal SimpleSValBuilder::evalBinOpLN(ProgramStateRef state, // Special case: 'rhs' is an integer that has the same width as a pointer and // we are using the integer location in a comparison. Normally this cannot be - // triggered, but transfer functions like those for OSCommpareAndSwapBarrier32 + // triggered, but transfer functions like those for OSCompareAndSwapBarrier32 // can generate comparisons that trigger this code. // FIXME: Are all locations guaranteed to have pointer width? if (BinaryOperator::isComparisonOp(op)) { - if (nonloc::ConcreteInt *rhsInt = dyn_cast<nonloc::ConcreteInt>(&rhs)) { + if (Optional<nonloc::ConcreteInt> rhsInt = + rhs.getAs<nonloc::ConcreteInt>()) { const llvm::APSInt *x = &rhsInt->getValue(); ASTContext &ctx = Context; if (ctx.getTypeSize(ctx.VoidPtrTy) == x->getBitWidth()) { @@ -873,8 +860,8 @@ SVal SimpleSValBuilder::evalBinOpLN(ProgramStateRef state, // We are dealing with pointer arithmetic. // Handle pointer arithmetic on constant values. - if (nonloc::ConcreteInt *rhsInt = dyn_cast<nonloc::ConcreteInt>(&rhs)) { - if (loc::ConcreteInt *lhsInt = dyn_cast<loc::ConcreteInt>(&lhs)) { + if (Optional<nonloc::ConcreteInt> rhsInt = rhs.getAs<nonloc::ConcreteInt>()) { + if (Optional<loc::ConcreteInt> lhsInt = lhs.getAs<loc::ConcreteInt>()) { const llvm::APSInt &leftI = lhsInt->getValue(); assert(leftI.isUnsigned()); llvm::APSInt rightI(rhsInt->getValue(), /* isUnsigned */ true); @@ -904,7 +891,7 @@ SVal SimpleSValBuilder::evalBinOpLN(ProgramStateRef state, // Handle cases where 'lhs' is a region. if (const MemRegion *region = lhs.getAsRegion()) { - rhs = cast<NonLoc>(convertToArrayIndex(rhs)); + rhs = convertToArrayIndex(rhs).castAs<NonLoc>(); SVal index = UnknownVal(); const MemRegion *superR = 0; QualType elementType; @@ -923,7 +910,7 @@ SVal SimpleSValBuilder::evalBinOpLN(ProgramStateRef state, elementType = resultTy->getPointeeType(); } - if (NonLoc *indexV = dyn_cast<NonLoc>(&index)) { + if (Optional<NonLoc> indexV = index.getAs<NonLoc>()) { return loc::MemRegionVal(MemMgr.getElementRegion(elementType, *indexV, superR, getContext())); } @@ -936,10 +923,10 @@ const llvm::APSInt *SimpleSValBuilder::getKnownValue(ProgramStateRef state, if (V.isUnknownOrUndef()) return NULL; - if (loc::ConcreteInt* X = dyn_cast<loc::ConcreteInt>(&V)) + if (Optional<loc::ConcreteInt> X = V.getAs<loc::ConcreteInt>()) return &X->getValue(); - if (nonloc::ConcreteInt* X = dyn_cast<nonloc::ConcreteInt>(&V)) + if (Optional<nonloc::ConcreteInt> X = V.getAs<nonloc::ConcreteInt>()) return &X->getValue(); if (SymbolRef Sym = V.getAsSymbol()) diff --git a/lib/StaticAnalyzer/Core/Store.cpp b/lib/StaticAnalyzer/Core/Store.cpp index 939ae54dad74..a0c24fedcfca 100644 --- a/lib/StaticAnalyzer/Core/Store.cpp +++ b/lib/StaticAnalyzer/Core/Store.cpp @@ -12,11 +12,11 @@ //===----------------------------------------------------------------------===// #include "clang/StaticAnalyzer/Core/PathSensitive/Store.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" -#include "clang/AST/CharUnits.h" #include "clang/AST/CXXInheritance.h" +#include "clang/AST/CharUnits.h" #include "clang/AST/DeclObjC.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" using namespace clang; using namespace ento; @@ -223,13 +223,38 @@ const MemRegion *StoreManager::castRegion(const MemRegion *R, QualType CastToTy) llvm_unreachable("unreachable"); } +static bool regionMatchesCXXRecordType(SVal V, QualType Ty) { + const MemRegion *MR = V.getAsRegion(); + if (!MR) + return true; + + const TypedValueRegion *TVR = dyn_cast<TypedValueRegion>(MR); + if (!TVR) + return true; + + const CXXRecordDecl *RD = TVR->getValueType()->getAsCXXRecordDecl(); + if (!RD) + return true; + + const CXXRecordDecl *Expected = Ty->getPointeeCXXRecordDecl(); + if (!Expected) + Expected = Ty->getAsCXXRecordDecl(); + + return Expected->getCanonicalDecl() == RD->getCanonicalDecl(); +} + SVal StoreManager::evalDerivedToBase(SVal Derived, const CastExpr *Cast) { + // Sanity check to avoid doing the wrong thing in the face of + // reinterpret_cast. + if (!regionMatchesCXXRecordType(Derived, Cast->getSubExpr()->getType())) + return UnknownVal(); + // Walk through the cast path to create nested CXXBaseRegions. SVal Result = Derived; for (CastExpr::path_const_iterator I = Cast->path_begin(), E = Cast->path_end(); I != E; ++I) { - Result = evalDerivedToBase(Result, (*I)->getType()); + Result = evalDerivedToBase(Result, (*I)->getType(), (*I)->isVirtual()); } return Result; } @@ -239,13 +264,16 @@ SVal StoreManager::evalDerivedToBase(SVal Derived, const CXXBasePath &Path) { SVal Result = Derived; for (CXXBasePath::const_iterator I = Path.begin(), E = Path.end(); I != E; ++I) { - Result = evalDerivedToBase(Result, I->Base->getType()); + Result = evalDerivedToBase(Result, I->Base->getType(), + I->Base->isVirtual()); } return Result; } -SVal StoreManager::evalDerivedToBase(SVal Derived, QualType BaseType) { - loc::MemRegionVal *DerivedRegVal = dyn_cast<loc::MemRegionVal>(&Derived); +SVal StoreManager::evalDerivedToBase(SVal Derived, QualType BaseType, + bool IsVirtual) { + Optional<loc::MemRegionVal> DerivedRegVal = + Derived.getAs<loc::MemRegionVal>(); if (!DerivedRegVal) return Derived; @@ -255,7 +283,8 @@ SVal StoreManager::evalDerivedToBase(SVal Derived, QualType BaseType) { assert(BaseDecl && "not a C++ object?"); const MemRegion *BaseReg = - MRMgr.getCXXBaseObjectRegion(BaseDecl, DerivedRegVal->getRegion()); + MRMgr.getCXXBaseObjectRegion(BaseDecl, DerivedRegVal->getRegion(), + IsVirtual); return loc::MemRegionVal(BaseReg); } @@ -264,7 +293,7 @@ SVal StoreManager::evalDynamicCast(SVal Base, QualType DerivedType, bool &Failed) { Failed = false; - loc::MemRegionVal *BaseRegVal = dyn_cast<loc::MemRegionVal>(&Base); + Optional<loc::MemRegionVal> BaseRegVal = Base.getAs<loc::MemRegionVal>(); if (!BaseRegVal) return UnknownVal(); const MemRegion *BaseRegion = BaseRegVal->stripCasts(/*StripBases=*/false); @@ -348,12 +377,12 @@ SVal StoreManager::getLValueFieldOrIvar(const Decl *D, SVal Base) { if (Base.isUnknownOrUndef()) return Base; - Loc BaseL = cast<Loc>(Base); + Loc BaseL = Base.castAs<Loc>(); const MemRegion* BaseR = 0; switch (BaseL.getSubKind()) { case loc::MemRegionKind: - BaseR = cast<loc::MemRegionVal>(BaseL).getRegion(); + BaseR = BaseL.castAs<loc::MemRegionVal>().getRegion(); break; case loc::GotoLabelKind: @@ -390,16 +419,16 @@ SVal StoreManager::getLValueElement(QualType elementType, NonLoc Offset, // FIXME: For absolute pointer addresses, we just return that value back as // well, although in reality we should return the offset added to that // value. - if (Base.isUnknownOrUndef() || isa<loc::ConcreteInt>(Base)) + if (Base.isUnknownOrUndef() || Base.getAs<loc::ConcreteInt>()) return Base; - const MemRegion* BaseRegion = cast<loc::MemRegionVal>(Base).getRegion(); + const MemRegion* BaseRegion = Base.castAs<loc::MemRegionVal>().getRegion(); // Pointer of any type can be cast and used as array base. const ElementRegion *ElemR = dyn_cast<ElementRegion>(BaseRegion); // Convert the offset to the appropriate size and signedness. - Offset = cast<NonLoc>(svalBuilder.convertToArrayIndex(Offset)); + Offset = svalBuilder.convertToArrayIndex(Offset).castAs<NonLoc>(); if (!ElemR) { // @@ -417,15 +446,16 @@ SVal StoreManager::getLValueElement(QualType elementType, NonLoc Offset, SVal BaseIdx = ElemR->getIndex(); - if (!isa<nonloc::ConcreteInt>(BaseIdx)) + if (!BaseIdx.getAs<nonloc::ConcreteInt>()) return UnknownVal(); - const llvm::APSInt& BaseIdxI = cast<nonloc::ConcreteInt>(BaseIdx).getValue(); + const llvm::APSInt &BaseIdxI = + BaseIdx.castAs<nonloc::ConcreteInt>().getValue(); // Only allow non-integer offsets if the base region has no offset itself. // FIXME: This is a somewhat arbitrary restriction. We should be using // SValBuilder here to add the two offsets without checking their types. - if (!isa<nonloc::ConcreteInt>(Offset)) { + if (!Offset.getAs<nonloc::ConcreteInt>()) { if (isa<ElementRegion>(BaseRegion->StripCasts())) return UnknownVal(); @@ -434,7 +464,7 @@ SVal StoreManager::getLValueElement(QualType elementType, NonLoc Offset, Ctx)); } - const llvm::APSInt& OffI = cast<nonloc::ConcreteInt>(Offset).getValue(); + const llvm::APSInt& OffI = Offset.castAs<nonloc::ConcreteInt>().getValue(); assert(BaseIdxI.isSigned()); // Compute the new index. diff --git a/lib/StaticAnalyzer/Core/SymbolManager.cpp b/lib/StaticAnalyzer/Core/SymbolManager.cpp index 0c5098b1e7d0..de2f5bc7b373 100644 --- a/lib/StaticAnalyzer/Core/SymbolManager.cpp +++ b/lib/StaticAnalyzer/Core/SymbolManager.cpp @@ -27,52 +27,33 @@ void SymExpr::dump() const { dumpToStream(llvm::errs()); } -static void print(raw_ostream &os, BinaryOperator::Opcode Op) { - switch (Op) { - default: - llvm_unreachable("operator printing not implemented"); - case BO_Mul: os << '*' ; break; - case BO_Div: os << '/' ; break; - case BO_Rem: os << '%' ; break; - case BO_Add: os << '+' ; break; - case BO_Sub: os << '-' ; break; - case BO_Shl: os << "<<" ; break; - case BO_Shr: os << ">>" ; break; - case BO_LT: os << "<" ; break; - case BO_GT: os << '>' ; break; - case BO_LE: os << "<=" ; break; - case BO_GE: os << ">=" ; break; - case BO_EQ: os << "==" ; break; - case BO_NE: os << "!=" ; break; - case BO_And: os << '&' ; break; - case BO_Xor: os << '^' ; break; - case BO_Or: os << '|' ; break; - } -} - void SymIntExpr::dumpToStream(raw_ostream &os) const { os << '('; getLHS()->dumpToStream(os); - os << ") "; - print(os, getOpcode()); - os << ' ' << getRHS().getZExtValue(); - if (getRHS().isUnsigned()) os << 'U'; + os << ") " + << BinaryOperator::getOpcodeStr(getOpcode()) << ' ' + << getRHS().getZExtValue(); + if (getRHS().isUnsigned()) + os << 'U'; } void IntSymExpr::dumpToStream(raw_ostream &os) const { - os << ' ' << getLHS().getZExtValue(); - if (getLHS().isUnsigned()) os << 'U'; - print(os, getOpcode()); - os << '('; + os << getLHS().getZExtValue(); + if (getLHS().isUnsigned()) + os << 'U'; + os << ' ' + << BinaryOperator::getOpcodeStr(getOpcode()) + << " ("; getRHS()->dumpToStream(os); - os << ") "; + os << ')'; } void SymSymExpr::dumpToStream(raw_ostream &os) const { os << '('; getLHS()->dumpToStream(os); - os << ") "; - os << '('; + os << ") " + << BinaryOperator::getOpcodeStr(getOpcode()) + << " ("; getRHS()->dumpToStream(os); os << ')'; } @@ -468,9 +449,7 @@ bool SymbolReaper::isLive(SymbolRef sym) { switch (sym->getKind()) { case SymExpr::RegionValueKind: - // FIXME: We should be able to use isLiveRegion here (this behavior - // predates isLiveRegion), but doing so causes test failures. Investigate. - KnownLive = true; + KnownLive = isLiveRegion(cast<SymbolRegionValue>(sym)->getRegion()); break; case SymExpr::ConjuredKind: KnownLive = false; diff --git a/lib/StaticAnalyzer/Core/TextPathDiagnostics.cpp b/lib/StaticAnalyzer/Core/TextPathDiagnostics.cpp index e09f4e365344..d5706d6dbbe8 100644 --- a/lib/StaticAnalyzer/Core/TextPathDiagnostics.cpp +++ b/lib/StaticAnalyzer/Core/TextPathDiagnostics.cpp @@ -12,8 +12,8 @@ //===----------------------------------------------------------------------===// #include "clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h" -#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" #include "clang/Lex/Preprocessor.h" +#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" #include "llvm/Support/raw_ostream.h" using namespace clang; using namespace ento; @@ -46,7 +46,8 @@ public: } // end anonymous namespace -void ento::createTextPathDiagnosticConsumer(PathDiagnosticConsumers &C, +void ento::createTextPathDiagnosticConsumer(AnalyzerOptions &AnalyzerOpts, + PathDiagnosticConsumers &C, const std::string& out, const Preprocessor &PP) { C.push_back(new TextPathDiagnostics(out, PP.getDiagnostics())); diff --git a/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp b/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp index 7dbac3cf93a0..d71e528848b0 100644 --- a/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp +++ b/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp @@ -20,31 +20,30 @@ #include "clang/AST/DeclObjC.h" #include "clang/AST/ParentMap.h" #include "clang/AST/RecursiveASTVisitor.h" +#include "clang/Analysis/Analyses/LiveVariables.h" #include "clang/Analysis/CFG.h" #include "clang/Analysis/CallGraph.h" -#include "clang/Analysis/Analyses/LiveVariables.h" -#include "clang/StaticAnalyzer/Frontend/CheckerRegistration.h" -#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/Basic/FileManager.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Lex/Preprocessor.h" #include "clang/StaticAnalyzer/Checkers/LocalCheckers.h" +#include "clang/StaticAnalyzer/Core/AnalyzerOptions.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" #include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h" #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" -#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" -#include "clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h" - -#include "clang/Basic/FileManager.h" -#include "clang/Basic/SourceManager.h" -#include "clang/StaticAnalyzer/Core/AnalyzerOptions.h" -#include "clang/Lex/Preprocessor.h" -#include "llvm/Support/raw_ostream.h" -#include "llvm/Support/Path.h" -#include "llvm/Support/Program.h" -#include "llvm/Support/Timer.h" +#include "clang/StaticAnalyzer/Frontend/CheckerRegistration.h" #include "llvm/ADT/DepthFirstIterator.h" #include "llvm/ADT/OwningPtr.h" +#include "llvm/ADT/PostOrderIterator.h" #include "llvm/ADT/SmallPtrSet.h" #include "llvm/ADT/Statistic.h" - +#include "llvm/Support/Path.h" +#include "llvm/Support/Program.h" +#include "llvm/Support/Timer.h" +#include "llvm/Support/raw_ostream.h" #include <queue> using namespace clang; @@ -54,9 +53,11 @@ 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(NumFunctionsAnalyzed, + "The # of functions and blocks analyzed (as top level " + "with inlining turned on)."); STATISTIC(NumBlocksInAnalyzedFunctions, - "The # of basic blocks in the analyzed functions."); + "The # of basic blocks in the analyzed functions."); STATISTIC(PercentReachableBlocks, "The % of reachable basic blocks."); STATISTIC(MaxCFGSize, "The maximum number of basic blocks in a function."); @@ -64,11 +65,13 @@ STATISTIC(MaxCFGSize, "The maximum number of basic blocks in a function."); // Special PathDiagnosticConsumers. //===----------------------------------------------------------------------===// -static void createPlistHTMLDiagnosticConsumer(PathDiagnosticConsumers &C, +static void createPlistHTMLDiagnosticConsumer(AnalyzerOptions &AnalyzerOpts, + PathDiagnosticConsumers &C, const std::string &prefix, const Preprocessor &PP) { - createHTMLDiagnosticConsumer(C, llvm::sys::path::parent_path(prefix), PP); - createPlistDiagnosticConsumer(C, prefix, PP); + createHTMLDiagnosticConsumer(AnalyzerOpts, C, + llvm::sys::path::parent_path(prefix), PP); + createPlistDiagnosticConsumer(AnalyzerOpts, C, prefix, PP); } namespace { @@ -188,13 +191,14 @@ public: switch (Opts->AnalysisDiagOpt) { default: #define ANALYSIS_DIAGNOSTICS(NAME, CMDFLAG, DESC, CREATEFN, AUTOCREATE) \ - case PD_##NAME: CREATEFN(PathConsumers, OutDir, PP); break; + case PD_##NAME: CREATEFN(*Opts.getPtr(), PathConsumers, OutDir, PP);\ + break; #include "clang/StaticAnalyzer/Core/Analyses.def" } } else if (Opts->AnalysisDiagOpt == PD_TEXT) { // Create the text client even without a specified output file since // it just uses diagnostic notes. - createTextPathDiagnosticConsumer(PathConsumers, "", PP); + createTextPathDiagnosticConsumer(*Opts.getPtr(), PathConsumers, "", PP); } // Create the analyzer component creators. @@ -208,14 +212,15 @@ public: switch (Opts->AnalysisConstraintsOpt) { default: - llvm_unreachable("Unknown store manager."); + llvm_unreachable("Unknown constraint manager."); #define ANALYSIS_CONSTRAINTS(NAME, CMDFLAG, DESC, CREATEFN) \ case NAME##Model: CreateConstraintMgr = CREATEFN; break; #include "clang/StaticAnalyzer/Core/Analyses.def" } } - void DisplayFunction(const Decl *D, AnalysisMode Mode) { + void DisplayFunction(const Decl *D, AnalysisMode Mode, + ExprEngine::InliningModes IMode) { if (!Opts->AnalyzerDisplayProgress) return; @@ -226,8 +231,18 @@ public: if (Mode == AM_Syntax) llvm::errs() << " (Syntax)"; - else if (Mode == AM_Path) - llvm::errs() << " (Path)"; + else if (Mode == AM_Path) { + llvm::errs() << " (Path, "; + switch (IMode) { + case ExprEngine::Inline_Minimal: + llvm::errs() << " Inline_Minimal"; + break; + case ExprEngine::Inline_Regular: + llvm::errs() << " Inline_Regular"; + break; + } + llvm::errs() << ")"; + } else assert(Mode == (AM_Syntax | AM_Path) && "Unexpected mode!"); @@ -268,6 +283,12 @@ public: virtual void HandleTranslationUnit(ASTContext &C); + /// \brief Determine which inlining mode should be used when this function is + /// analyzed. This allows to redefine the default inlining policies when + /// analyzing a given function. + ExprEngine::InliningModes + getInliningModeForFunction(const Decl *D, SetOfConstDecls Visited); + /// \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 HandleDeclsCallGraph(const unsigned LocalTUDeclsSize); @@ -279,10 +300,14 @@ public: /// set of functions which should be considered analyzed after analyzing the /// given root function. void HandleCode(Decl *D, AnalysisMode Mode, + ExprEngine::InliningModes IMode = ExprEngine::Inline_Minimal, SetOfConstDecls *VisitedCallees = 0); - void RunPathSensitiveChecks(Decl *D, SetOfConstDecls *VisitedCallees); + void RunPathSensitiveChecks(Decl *D, + ExprEngine::InliningModes IMode, + SetOfConstDecls *VisitedCallees); void ActionExprEngine(Decl *D, bool ObjCGCEnabled, + ExprEngine::InliningModes IMode, SetOfConstDecls *VisitedCallees); /// Visitors for the RecursiveASTVisitor. @@ -305,14 +330,25 @@ public: // only determined when they are instantiated. if (FD->isThisDeclarationADefinition() && !FD->isDependentContext()) { + assert(RecVisitorMode == AM_Syntax || Mgr->shouldInlineCall() == false); HandleCode(FD, RecVisitorMode); } return true; } bool VisitObjCMethodDecl(ObjCMethodDecl *MD) { - if (MD->isThisDeclarationADefinition()) + if (MD->isThisDeclarationADefinition()) { + assert(RecVisitorMode == AM_Syntax || Mgr->shouldInlineCall() == false); HandleCode(MD, RecVisitorMode); + } + return true; + } + + bool VisitBlockDecl(BlockDecl *BD) { + if (BD->hasBody()) { + assert(RecVisitorMode == AM_Syntax || Mgr->shouldInlineCall() == false); + HandleCode(BD, RecVisitorMode); + } return true; } @@ -352,95 +388,90 @@ void AnalysisConsumer::storeTopLevelDecls(DeclGroupRef DG) { } } -static bool shouldSkipFunction(CallGraphNode *N, - SmallPtrSet<CallGraphNode*,24> Visited) { - // We want to re-analyse the functions as top level in several cases: +static bool shouldSkipFunction(const Decl *D, + SetOfConstDecls Visited, + SetOfConstDecls VisitedAsTopLevel) { + if (VisitedAsTopLevel.count(D)) + return true; + + // We want to re-analyse the functions as top level in the following cases: // - The 'init' methods should be reanalyzed because // ObjCNonNilReturnValueChecker assumes that '[super init]' never returns - // 'nil' and unless we analyze the 'init' functions as top level, we will not - // catch errors within defensive code. + // 'nil' and unless we analyze the 'init' functions as top level, we will + // not catch errors within defensive code. // - We want to reanalyze all ObjC methods as top level to report Retain // Count naming convention errors more aggressively. - if (isa<ObjCMethodDecl>(N->getDecl())) + if (isa<ObjCMethodDecl>(D)) return false; // Otherwise, if we visited the function before, do not reanalyze it. - return Visited.count(N); + return Visited.count(D); } -void AnalysisConsumer::HandleDeclsCallGraph(const unsigned LocalTUDeclsSize) { - // Otherwise, use the Callgraph to derive the order. - // Build the Call Graph. - CallGraph CG; +ExprEngine::InliningModes +AnalysisConsumer::getInliningModeForFunction(const Decl *D, + SetOfConstDecls Visited) { + // We want to reanalyze all ObjC methods as top level to report Retain + // Count naming convention errors more aggressively. But we should tune down + // inlining when reanalyzing an already inlined function. + if (Visited.count(D)) { + assert(isa<ObjCMethodDecl>(D) && + "We are only reanalyzing ObjCMethods."); + const ObjCMethodDecl *ObjCM = cast<ObjCMethodDecl>(D); + if (ObjCM->getMethodFamily() != OMF_init) + return ExprEngine::Inline_Minimal; + } - // Add all the top level declarations to the graph. + return ExprEngine::Inline_Regular; +} + +void AnalysisConsumer::HandleDeclsCallGraph(const unsigned LocalTUDeclsSize) { + // Build the Call Graph by adding all the top level declarations to the graph. // Note: CallGraph can trigger deserialization of more items from a pch // (though HandleInterestingDecl); triggering additions to LocalTUDecls. // We rely on random access to add the initially processed Decls to CG. + CallGraph CG; for (unsigned i = 0 ; i < LocalTUDeclsSize ; ++i) { CG.addToCallGraph(LocalTUDecls[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); + // Walk over all of the call graph nodes in topological order, so that we + // analyze parents before the children. Skip the functions inlined into + // the previously processed functions. Use external Visited set to identify + // inlined functions. The topological order allows the "do not reanalyze + // previously inlined function" performance heuristic to be triggered more + // often. + SetOfConstDecls Visited; + SetOfConstDecls VisitedAsTopLevel; + llvm::ReversePostOrderTraversal<clang::CallGraph*> RPOT(&CG); + for (llvm::ReversePostOrderTraversal<clang::CallGraph*>::rpo_iterator + I = RPOT.begin(), E = RPOT.end(); I != E; ++I) { NumFunctionTopLevel++; - } - CallGraphNode *Entry = CG.getRoot(); - for (CallGraphNode::iterator I = Entry->begin(), - E = Entry->end(); I != E; ++I) { - TopLevelFunctions.push_back(*I); - NumFunctionTopLevel++; - } - // 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::deque<CallGraphNode*> BFSQueue; - for (llvm::SmallVector<CallGraphNode*, 24>::reverse_iterator - TI = TopLevelFunctions.rbegin(), TE = TopLevelFunctions.rend(); - TI != TE; ++TI) - BFSQueue.push_back(*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_front(); - - // Push the children into the queue. - for (CallGraphNode::const_iterator CI = N->begin(), - CE = N->end(); CI != CE; ++CI) { - if (!shouldSkipFunction(*CI, Visited)) - BFSQueue.push_back(*CI); - } + CallGraphNode *N = *I; + Decl *D = N->getDecl(); + + // Skip the abstract root node. + if (!D) + continue; // Skip the functions which have been processed already or previously // inlined. - if (shouldSkipFunction(N, Visited)) + if (shouldSkipFunction(D, Visited, VisitedAsTopLevel)) continue; // Analyze the function. SetOfConstDecls VisitedCallees; - Decl *D = N->getDecl(); - assert(D); - HandleCode(D, AM_Path, + + HandleCode(D, AM_Path, getInliningModeForFunction(D, Visited), (Mgr->options.InliningMode == All ? 0 : &VisitedCallees)); // Add the visited callees to the global visited set. for (SetOfConstDecls::iterator I = VisitedCallees.begin(), E = VisitedCallees.end(); I != E; ++I) { - CallGraphNode *VN = CG.getNode(*I); - if (VN) - Visited.insert(VN); + Visited.insert(*I); } - Visited.insert(N); + VisitedAsTopLevel.insert(D); } } @@ -503,16 +534,6 @@ void AnalysisConsumer::HandleTranslationUnit(ASTContext &C) { } -static void FindBlocks(DeclContext *D, SmallVectorImpl<Decl*> &WL) { - if (BlockDecl *BD = dyn_cast<BlockDecl>(D)) - WL.push_back(BD); - - for (DeclContext::decl_iterator I = D->decls_begin(), E = D->decls_end(); - I!=E; ++I) - if (DeclContext *DC = dyn_cast<DeclContext>(*I)) - FindBlocks(DC, WL); -} - static std::string getFunctionName(const Decl *D) { if (const ObjCMethodDecl *ID = dyn_cast<ObjCMethodDecl>(D)) { return ID->getSelector().getAsString(); @@ -548,40 +569,32 @@ AnalysisConsumer::getModeForDecl(Decl *D, AnalysisMode Mode) { } void AnalysisConsumer::HandleCode(Decl *D, AnalysisMode Mode, + ExprEngine::InliningModes IMode, SetOfConstDecls *VisitedCallees) { + if (!D->hasBody()) + return; Mode = getModeForDecl(D, Mode); if (Mode == AM_None) return; - DisplayFunction(D, Mode); + DisplayFunction(D, Mode, IMode); CFG *DeclCFG = Mgr->getCFG(D); if (DeclCFG) { unsigned CFGSize = DeclCFG->size(); MaxCFGSize = MaxCFGSize < CFGSize ? CFGSize : MaxCFGSize; } - // Clear the AnalysisManager of old AnalysisDeclContexts. Mgr->ClearContexts(); - - // Dispatch on the actions. - SmallVector<Decl*, 10> WL; - WL.push_back(D); - - if (D->hasBody() && Opts->AnalyzeNestedBlocks) - FindBlocks(cast<DeclContext>(D), WL); - BugReporter BR(*Mgr); - for (SmallVectorImpl<Decl*>::iterator WI=WL.begin(), WE=WL.end(); - WI != WE; ++WI) - if ((*WI)->hasBody()) { - if (Mode & AM_Syntax) - checkerMgr->runCheckersOnASTBody(*WI, *Mgr, BR); - if ((Mode & AM_Path) && checkerMgr->hasPathSensitiveCheckers()) { - RunPathSensitiveChecks(*WI, VisitedCallees); - NumFunctionsAnalyzed++; - } - } + + if (Mode & AM_Syntax) + checkerMgr->runCheckersOnASTBody(D, *Mgr, BR); + if ((Mode & AM_Path) && checkerMgr->hasPathSensitiveCheckers()) { + RunPathSensitiveChecks(D, IMode, VisitedCallees); + if (IMode != ExprEngine::Inline_Minimal) + NumFunctionsAnalyzed++; + } } //===----------------------------------------------------------------------===// @@ -589,6 +602,7 @@ void AnalysisConsumer::HandleCode(Decl *D, AnalysisMode Mode, //===----------------------------------------------------------------------===// void AnalysisConsumer::ActionExprEngine(Decl *D, bool ObjCGCEnabled, + ExprEngine::InliningModes IMode, SetOfConstDecls *VisitedCallees) { // Construct the analysis engine. First check if the CFG is valid. // FIXME: Inter-procedural analysis will need to handle invalid CFGs. @@ -599,7 +613,7 @@ void AnalysisConsumer::ActionExprEngine(Decl *D, bool ObjCGCEnabled, if (!Mgr->getAnalysisDeclContext(D)->getAnalysis<RelaxedLiveVariables>()) return; - ExprEngine Eng(*Mgr, ObjCGCEnabled, VisitedCallees, &FunctionSummaries); + ExprEngine Eng(*Mgr, ObjCGCEnabled, VisitedCallees, &FunctionSummaries,IMode); // Set the graph auditor. OwningPtr<ExplodedNode::Auditor> Auditor; @@ -610,7 +624,7 @@ void AnalysisConsumer::ActionExprEngine(Decl *D, bool ObjCGCEnabled, // Execute the worklist algorithm. Eng.ExecuteWorkList(Mgr->getAnalysisDeclContextManager().getStackFrame(D), - Mgr->options.MaxNodes); + Mgr->options.getMaxNodesPerTopLevelFunction()); // Release the auditor (if any) so that it doesn't monitor the graph // created BugReporter. @@ -625,20 +639,21 @@ void AnalysisConsumer::ActionExprEngine(Decl *D, bool ObjCGCEnabled, } void AnalysisConsumer::RunPathSensitiveChecks(Decl *D, + ExprEngine::InliningModes IMode, SetOfConstDecls *Visited) { switch (Mgr->getLangOpts().getGC()) { case LangOptions::NonGC: - ActionExprEngine(D, false, Visited); + ActionExprEngine(D, false, IMode, Visited); break; case LangOptions::GCOnly: - ActionExprEngine(D, true, Visited); + ActionExprEngine(D, true, IMode, Visited); break; case LangOptions::HybridGC: - ActionExprEngine(D, false, Visited); - ActionExprEngine(D, true, Visited); + ActionExprEngine(D, false, IMode, Visited); + ActionExprEngine(D, true, IMode, Visited); break; } } diff --git a/lib/StaticAnalyzer/Frontend/CheckerRegistration.cpp b/lib/StaticAnalyzer/Frontend/CheckerRegistration.cpp index e8daa65e410a..4fad5a8a7c59 100644 --- a/lib/StaticAnalyzer/Frontend/CheckerRegistration.cpp +++ b/lib/StaticAnalyzer/Frontend/CheckerRegistration.cpp @@ -12,19 +12,19 @@ //===----------------------------------------------------------------------===// #include "clang/StaticAnalyzer/Frontend/CheckerRegistration.h" -#include "clang/StaticAnalyzer/Frontend/FrontendActions.h" +#include "clang/Basic/Diagnostic.h" +#include "clang/Frontend/FrontendDiagnostic.h" #include "clang/StaticAnalyzer/Checkers/ClangCheckers.h" +#include "clang/StaticAnalyzer/Core/AnalyzerOptions.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/CheckerOptInfo.h" #include "clang/StaticAnalyzer/Core/CheckerRegistry.h" -#include "clang/StaticAnalyzer/Core/AnalyzerOptions.h" -#include "clang/Frontend/FrontendDiagnostic.h" -#include "clang/Basic/Diagnostic.h" +#include "clang/StaticAnalyzer/Frontend/FrontendActions.h" +#include "llvm/ADT/OwningPtr.h" +#include "llvm/ADT/SmallVector.h" #include "llvm/Support/DynamicLibrary.h" #include "llvm/Support/Path.h" #include "llvm/Support/raw_ostream.h" -#include "llvm/ADT/OwningPtr.h" -#include "llvm/ADT/SmallVector.h" using namespace clang; using namespace ento; diff --git a/lib/StaticAnalyzer/Frontend/FrontendActions.cpp b/lib/StaticAnalyzer/Frontend/FrontendActions.cpp index 85a18ec98ead..13971af9afad 100644 --- a/lib/StaticAnalyzer/Frontend/FrontendActions.cpp +++ b/lib/StaticAnalyzer/Frontend/FrontendActions.cpp @@ -8,8 +8,8 @@ //===----------------------------------------------------------------------===// #include "clang/StaticAnalyzer/Frontend/FrontendActions.h" -#include "clang/Frontend/CompilerInstance.h" #include "AnalysisConsumer.h" +#include "clang/Frontend/CompilerInstance.h" using namespace clang; using namespace ento; |