diff options
Diffstat (limited to 'lib/StaticAnalyzer/Checkers')
68 files changed, 2382 insertions, 1533 deletions
diff --git a/lib/StaticAnalyzer/Checkers/AnalyzerStatsChecker.cpp b/lib/StaticAnalyzer/Checkers/AnalyzerStatsChecker.cpp index aa6f97b..9af0a5a 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 535d8ee..312bc74 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 457c870..5e4b824 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 81e8dd8..0000000 --- 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 eba534e..533a324 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 92edefe..5169244 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 6ef022b..a3327d8 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 8e455de..b7df10e 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 eae9ddf..cc55e9f 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 f1a3aac..3a57a56 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 82bc136..4965d22 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 1cb8a8d..5e6e1054 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 d6d0e3c..60348c7 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 9087205..3f9b3cc 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 6df47b1..9cb1d2d 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 5cd6194..7ef13ab 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 cc7fd37..f2c5050 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 efaec2b..a9dd19a 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 235e633..3db3fb9 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 c885616..9912965 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 230baa7..bea908d 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 59e03ec..f2e3e6d 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 7ad9c59..29b4a63 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 3ace4be..72d46c5 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 dc90b67..6d3dd1e 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 76fb3f2..93daf94 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 b0a4bc6..9f176a4 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 e7e3162..810473f 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 7fde689..085a991 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 a9e0217..c67c597 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 ffbbb8b..271ba47 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 bf256cd..5ed28e9 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 757a4ce..02a7cc3 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 76f20b6..f1f06c7 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 467b8b1..32ebb51 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 caf70ca..4b0e766 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 daec418..34425e3 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 fb40f22..ce7d4cc 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 3331bc8..fc28e1f 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 7a66ec3..9f01522 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 efb7072..0009e1b 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 0000000..273a7a3 --- /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 9d84f52..4018a66 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 63a8480..4a0309d 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 999c994..b9e96ee 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 e906e8a..789b9f4 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 98d2a85a..8506e08 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 582269c..c66c7d0 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 b5d9959..bcbfacd 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 47da87f..07c82d4 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 d9b6384..ffb8cf2 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 304051c..79409e8 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 f3560aa..fe253b7 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 37ec1aa..7a5d993 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 ee055ad..1ccf339 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 0c2f266..4fd778e 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 c06ba7c..ffdf2d5 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 382be84..264f7f9 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 b97cd6c..57c9ed4 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 70e141e..8235e68 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 30ccffa..93812f7 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 415bab5..6733563 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 b3a83e8..176ee48 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 410010a..e04f49c 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 171e15b..4ea07e2 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 5a13ed0..91c2ffb 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 58f9ec0..30aef06 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 bdc9627..06f01ad 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; |