diff options
author | Dimitry Andric <dim@FreeBSD.org> | 2013-06-10 20:45:12 +0000 |
---|---|---|
committer | Dimitry Andric <dim@FreeBSD.org> | 2013-06-10 20:45:12 +0000 |
commit | 6a0372513edbc473b538d2f724efac50405d6fef (patch) | |
tree | 8f7776b7310bebaf415ac5b69e46e9f928c37144 /lib/StaticAnalyzer/Checkers | |
parent | 809500fc2c13c8173a16b052304d983864e4a1e1 (diff) | |
download | src-6a0372513edbc473b538d2f724efac50405d6fef.tar.gz src-6a0372513edbc473b538d2f724efac50405d6fef.zip |
Vendor import of clang tags/RELEASE_33/final r183502 (effectively, 3.3vendor/clang/clang-release_33-r183502
Notes
Notes:
svn path=/vendor/clang/dist/; revision=251609
svn path=/vendor/clang/clang-release_33-r183502/; revision=251610; tag=vendor/clang/clang-release_33-r183502
Diffstat (limited to 'lib/StaticAnalyzer/Checkers')
-rw-r--r-- | lib/StaticAnalyzer/Checkers/AllocationDiagnostics.cpp | 24 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Checkers/AllocationDiagnostics.h | 31 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp | 112 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Checkers/CMakeLists.txt | 3 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Checkers/CStringChecker.cpp | 62 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Checkers/CStringSyntaxChecker.cpp | 2 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp | 6 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Checkers/Checkers.td | 9 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp | 14 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Checkers/DebugCheckers.cpp | 2 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp | 18 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp | 47 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Checkers/MallocChecker.cpp | 220 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Checkers/MallocSizeofChecker.cpp | 2 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp | 158 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Checkers/ReturnUndefChecker.cpp | 13 |
16 files changed, 550 insertions, 173 deletions
diff --git a/lib/StaticAnalyzer/Checkers/AllocationDiagnostics.cpp b/lib/StaticAnalyzer/Checkers/AllocationDiagnostics.cpp new file mode 100644 index 000000000000..3dec8a58c929 --- /dev/null +++ b/lib/StaticAnalyzer/Checkers/AllocationDiagnostics.cpp @@ -0,0 +1,24 @@ +//=- AllocationDiagnostics.cpp - Config options for allocation diags *- C++ -*-// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Declares the configuration functions for leaks/allocation diagnostics. +// +//===-------------------------- + +#include "AllocationDiagnostics.h" + +namespace clang { +namespace ento { + +bool shouldIncludeAllocationSiteInLeakDiagnostics(AnalyzerOptions &AOpts) { + return AOpts.getBooleanOption("leak-diagnostics-reference-allocation", + false); +} + +}} diff --git a/lib/StaticAnalyzer/Checkers/AllocationDiagnostics.h b/lib/StaticAnalyzer/Checkers/AllocationDiagnostics.h new file mode 100644 index 000000000000..2b314a3b749a --- /dev/null +++ b/lib/StaticAnalyzer/Checkers/AllocationDiagnostics.h @@ -0,0 +1,31 @@ +//=--- AllocationDiagnostics.h - Config options for allocation diags *- C++ -*-// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Declares the configuration functions for leaks/allocation diagnostics. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_SA_LIB_CHECKERS_ALLOC_DIAGS_H +#define LLVM_CLANG_SA_LIB_CHECKERS_ALLOC_DIAGS_H + +#include "clang/StaticAnalyzer/Core/AnalyzerOptions.h" + +namespace clang { namespace ento { + +/// \brief Returns true if leak diagnostics should directly reference +/// the allocatin site (where possible). +/// +/// The default is false. +/// +bool shouldIncludeAllocationSiteInLeakDiagnostics(AnalyzerOptions &AOpts); + +}} + +#endif + diff --git a/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp b/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp index 533a324e7507..fba14a0fc498 100644 --- a/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp +++ b/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp @@ -123,18 +123,29 @@ void NilArgChecker::WarnIfNilArg(CheckerContext &C, 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 { + if (Arg == 0) { + os << "Value stored into '"; + os << GetReceiverInterfaceName(msg) << "' cannot be nil"; + } else { assert(Arg == 1); - os << "Dictionary key cannot be nil"; + os << "'"<< GetReceiverInterfaceName(msg) << "' 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"; + if (Class == FC_NSDictionary) { + if (Arg == 0) + os << "Value argument "; + else { + assert(Arg == 1); + os << "Key argument "; + } + os << "to '" << msg.getSelector().getAsString() << "' cannot be nil"; + } else { + os << "Argument to '" << GetReceiverInterfaceName(msg) << "' method '" + << msg.getSelector().getAsString() << "' cannot be nil"; + } } BugReport *R = new BugReport(*BT, os.str(), N); @@ -377,7 +388,7 @@ void CFNumberCreateChecker::checkPreStmt(const CallExpr *CE, // FIXME: If the pointee isn't an integer type, should we flag a warning? // People can do weird stuff with pointers. - if (!T->isIntegerType()) + if (!T->isIntegralOrEnumerationType()) return; uint64_t SourceSize = Ctx.getTypeSize(T); @@ -748,38 +759,81 @@ static bool isKnownNonNilCollectionType(QualType T) { } } -void ObjCLoopChecker::checkPostStmt(const ObjCForCollectionStmt *FCS, - CheckerContext &C) const { - ProgramStateRef State = C.getState(); - - // Check if this is the branch for the end of the loop. - SVal CollectionSentinel = State->getSVal(FCS, C.getLocationContext()); - if (CollectionSentinel.isZeroConstant()) - return; - +/// Assumes that the collection is non-nil. +/// +/// If the collection is known to be nil, returns NULL to indicate an infeasible +/// path. +static ProgramStateRef checkCollectionNonNil(CheckerContext &C, + ProgramStateRef State, + const ObjCForCollectionStmt *FCS) { + if (!State) + return NULL; + + SVal CollectionVal = C.getSVal(FCS->getCollection()); + Optional<DefinedSVal> KnownCollection = CollectionVal.getAs<DefinedSVal>(); + if (!KnownCollection) + return State; + + ProgramStateRef StNonNil, StNil; + llvm::tie(StNonNil, StNil) = State->assume(*KnownCollection); + if (StNil && !StNonNil) { + // The collection is nil. This path is infeasible. + return NULL; + } + + return StNonNil; +} + +/// Assumes that the collection elements are non-nil. +/// +/// This only applies if the collection is one of those known not to contain +/// nil values. +static ProgramStateRef checkElementNonNil(CheckerContext &C, + ProgramStateRef State, + const ObjCForCollectionStmt *FCS) { + if (!State) + return NULL; + // See if the collection is one where we /know/ the elements are non-nil. - const Expr *Collection = FCS->getCollection(); - if (!isKnownNonNilCollectionType(Collection->getType())) - return; - - // FIXME: Copied from ExprEngineObjC. + if (!isKnownNonNilCollectionType(FCS->getCollection()->getType())) + return State; + + const LocationContext *LCtx = C.getLocationContext(); const Stmt *Element = FCS->getElement(); - SVal ElementVar; + + // FIXME: Copied from ExprEngineObjC. + Optional<Loc> ElementLoc; if (const DeclStmt *DS = dyn_cast<DeclStmt>(Element)) { const VarDecl *ElemDecl = cast<VarDecl>(DS->getSingleDecl()); assert(ElemDecl->getInit() == 0); - ElementVar = State->getLValue(ElemDecl, C.getLocationContext()); + ElementLoc = State->getLValue(ElemDecl, LCtx); } else { - ElementVar = State->getSVal(Element, C.getLocationContext()); + ElementLoc = State->getSVal(Element, LCtx).getAs<Loc>(); } - if (!ElementVar.getAs<Loc>()) - return; + if (!ElementLoc) + return State; // Go ahead and assume the value is non-nil. - SVal Val = State->getSVal(ElementVar.castAs<Loc>()); - State = State->assume(Val.castAs<DefinedOrUnknownSVal>(), true); - C.addTransition(State); + SVal Val = State->getSVal(*ElementLoc); + return State->assume(Val.castAs<DefinedOrUnknownSVal>(), true); +} + +void ObjCLoopChecker::checkPostStmt(const ObjCForCollectionStmt *FCS, + CheckerContext &C) const { + // Check if this is the branch for the end of the loop. + SVal CollectionSentinel = C.getSVal(FCS); + if (CollectionSentinel.isZeroConstant()) + return; + + ProgramStateRef State = C.getState(); + State = checkCollectionNonNil(C, State, FCS); + State = checkElementNonNil(C, State, FCS); + + if (!State) + C.generateSink(); + else if (State != C.getState()) + C.addTransition(State); } namespace { diff --git a/lib/StaticAnalyzer/Checkers/CMakeLists.txt b/lib/StaticAnalyzer/Checkers/CMakeLists.txt index b7df10e7ffbe..7da68251063b 100644 --- a/lib/StaticAnalyzer/Checkers/CMakeLists.txt +++ b/lib/StaticAnalyzer/Checkers/CMakeLists.txt @@ -4,6 +4,7 @@ clang_tablegen(Checkers.inc -gen-clang-sa-checkers TARGET ClangSACheckers) add_clang_library(clangStaticAnalyzerCheckers + AllocationDiagnostics.cpp AnalyzerStatsChecker.cpp ArrayBoundChecker.cpp ArrayBoundCheckerV2.cpp @@ -42,8 +43,8 @@ add_clang_library(clangStaticAnalyzerCheckers MallocSizeofChecker.cpp NSAutoreleasePoolChecker.cpp NSErrorChecker.cpp - NonNullParamChecker.cpp NoReturnFunctionChecker.cpp + NonNullParamChecker.cpp ObjCAtSyncChecker.cpp ObjCContainersASTChecker.cpp ObjCContainersChecker.cpp diff --git a/lib/StaticAnalyzer/Checkers/CStringChecker.cpp b/lib/StaticAnalyzer/Checkers/CStringChecker.cpp index cc55e9f6ecf0..aa1ca6f2f809 100644 --- a/lib/StaticAnalyzer/Checkers/CStringChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/CStringChecker.cpp @@ -114,6 +114,8 @@ public: bool isBounded = false, bool ignoreCase = false) const; + void evalStrsep(CheckerContext &C, const CallExpr *CE) const; + // Utility methods std::pair<ProgramStateRef , ProgramStateRef > static assumeZero(CheckerContext &C, @@ -1752,6 +1754,63 @@ void CStringChecker::evalStrcmpCommon(CheckerContext &C, const CallExpr *CE, C.addTransition(state); } +void CStringChecker::evalStrsep(CheckerContext &C, const CallExpr *CE) const { + //char *strsep(char **stringp, const char *delim); + if (CE->getNumArgs() < 2) + return; + + // Sanity: does the search string parameter match the return type? + const Expr *SearchStrPtr = CE->getArg(0); + QualType CharPtrTy = SearchStrPtr->getType()->getPointeeType(); + if (CharPtrTy.isNull() || + CE->getType().getUnqualifiedType() != CharPtrTy.getUnqualifiedType()) + return; + + CurrentFunctionDescription = "strsep()"; + ProgramStateRef State = C.getState(); + const LocationContext *LCtx = C.getLocationContext(); + + // Check that the search string pointer is non-null (though it may point to + // a null string). + SVal SearchStrVal = State->getSVal(SearchStrPtr, LCtx); + State = checkNonNull(C, State, SearchStrPtr, SearchStrVal); + if (!State) + return; + + // Check that the delimiter string is non-null. + const Expr *DelimStr = CE->getArg(1); + SVal DelimStrVal = State->getSVal(DelimStr, LCtx); + State = checkNonNull(C, State, DelimStr, DelimStrVal); + if (!State) + return; + + SValBuilder &SVB = C.getSValBuilder(); + SVal Result; + if (Optional<Loc> SearchStrLoc = SearchStrVal.getAs<Loc>()) { + // Get the current value of the search string pointer, as a char*. + Result = State->getSVal(*SearchStrLoc, CharPtrTy); + + // Invalidate the search string, representing the change of one delimiter + // character to NUL. + State = InvalidateBuffer(C, State, SearchStrPtr, Result); + + // Overwrite the search string pointer. The new value is either an address + // further along in the same string, or NULL if there are no more tokens. + State = State->bindLoc(*SearchStrLoc, + SVB.conjureSymbolVal(getTag(), CE, LCtx, CharPtrTy, + C.blockCount())); + } else { + assert(SearchStrVal.isUnknown()); + // Conjure a symbolic value. It's the best we can do. + Result = SVB.conjureSymbolVal(0, CE, LCtx, C.blockCount()); + } + + // Set the return value, and finish. + State = State->BindExpr(CE, LCtx, Result); + C.addTransition(State); +} + + //===----------------------------------------------------------------------===// // The driver method, and other Checker callbacks. //===----------------------------------------------------------------------===// @@ -1762,6 +1821,7 @@ bool CStringChecker::evalCall(const CallExpr *CE, CheckerContext &C) const { if (!FDecl) return false; + // FIXME: Poorly-factored string switches are slow. FnCheck evalFunction = 0; if (C.isCLibraryFunction(FDecl, "memcpy")) evalFunction = &CStringChecker::evalMemcpy; @@ -1793,6 +1853,8 @@ bool CStringChecker::evalCall(const CallExpr *CE, CheckerContext &C) const { evalFunction = &CStringChecker::evalStrcasecmp; else if (C.isCLibraryFunction(FDecl, "strncasecmp")) evalFunction = &CStringChecker::evalStrncasecmp; + else if (C.isCLibraryFunction(FDecl, "strsep")) + evalFunction = &CStringChecker::evalStrsep; else if (C.isCLibraryFunction(FDecl, "bcopy")) evalFunction = &CStringChecker::evalBcopy; else if (C.isCLibraryFunction(FDecl, "bcmp")) diff --git a/lib/StaticAnalyzer/Checkers/CStringSyntaxChecker.cpp b/lib/StaticAnalyzer/Checkers/CStringSyntaxChecker.cpp index 3a57a56aea6e..92c0eef3e880 100644 --- a/lib/StaticAnalyzer/Checkers/CStringSyntaxChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/CStringSyntaxChecker.cpp @@ -101,6 +101,8 @@ public: // - strncat(dst, src, sizeof(dst) - 1); // - strncat(dst, src, sizeof(dst)); bool WalkAST::containsBadStrncatPattern(const CallExpr *CE) { + if (CE->getNumArgs() != 3) + return false; const Expr *DstArg = CE->getArg(0); const Expr *SrcArg = CE->getArg(1); const Expr *LenArg = CE->getArg(2); diff --git a/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp b/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp index 7ef13ab53865..63080ea230f8 100644 --- a/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp +++ b/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp @@ -345,7 +345,7 @@ void WalkAST::checkCall_getpw(const CallExpr *CE, const FunctionDecl *FD) { return; // Verify the first argument type is integer. - if (!FPT->getArgType(0)->isIntegerType()) + if (!FPT->getArgType(0)->isIntegralOrUnscopedEnumerationType()) return; // Verify the second argument type is char*. @@ -602,7 +602,7 @@ void WalkAST::checkCall_rand(const CallExpr *CE, const FunctionDecl *FD) { if (!PT) return; - if (! PT->getPointeeType()->isIntegerType()) + if (! PT->getPointeeType()->isIntegralOrUnscopedEnumerationType()) return; } else if (FTP->getNumArgs() != 0) @@ -725,7 +725,7 @@ void WalkAST::checkUncheckedReturnValue(CallExpr *CE) { // The arguments must be integers. for (unsigned i = 0; i < FTP->getNumArgs(); i++) - if (! FTP->getArgType(i)->isIntegerType()) + if (! FTP->getArgType(i)->isIntegralOrUnscopedEnumerationType()) return; // Issue a warning. diff --git a/lib/StaticAnalyzer/Checkers/Checkers.td b/lib/StaticAnalyzer/Checkers/Checkers.td index 3db3fb9962a5..fc35b223ee7f 100644 --- a/lib/StaticAnalyzer/Checkers/Checkers.td +++ b/lib/StaticAnalyzer/Checkers/Checkers.td @@ -167,6 +167,11 @@ def ReturnUndefChecker : Checker<"UndefReturn">, //===----------------------------------------------------------------------===// let ParentPackage = Cplusplus in { + +def NewDeleteChecker : Checker<"NewDelete">, + HelpText<"Check for double-free and use-after-free problems. Traces memory managed by new/delete.">, + DescFile<"MallocChecker.cpp">; + } // end: "cplusplus" let ParentPackage = CplusplusAlpha in { @@ -175,8 +180,8 @@ 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.">, +def NewDeleteLeaksChecker : Checker<"NewDeleteLeaks">, + HelpText<"Check for memory leaks. Traces memory managed by new/delete.">, DescFile<"MallocChecker.cpp">; } // end: "alpha.cplusplus" diff --git a/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp b/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp index f2e3e6d7815e..f336a6e68051 100644 --- a/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp @@ -101,7 +101,8 @@ void ReachableCode::computeReachableBlocks() { } } -static const Expr *LookThroughTransitiveAssignments(const Expr *Ex) { +static const Expr * +LookThroughTransitiveAssignmentsAndCommaOperators(const Expr *Ex) { while (Ex) { const BinaryOperator *BO = dyn_cast<BinaryOperator>(Ex->IgnoreParenCasts()); @@ -111,6 +112,10 @@ static const Expr *LookThroughTransitiveAssignments(const Expr *Ex) { Ex = BO->getRHS(); continue; } + if (BO->getOpcode() == BO_Comma) { + Ex = BO->getRHS(); + continue; + } break; } return Ex; @@ -266,7 +271,9 @@ public: if (VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) { // Special case: check for assigning null to a pointer. // This is a common form of defensive programming. - const Expr *RHS = LookThroughTransitiveAssignments(B->getRHS()); + const Expr *RHS = + LookThroughTransitiveAssignmentsAndCommaOperators(B->getRHS()); + RHS = RHS->IgnoreParenCasts(); QualType T = VD->getType(); if (T->isPointerType() || T->isObjCObjectPointerType()) { @@ -274,7 +281,6 @@ public: return; } - RHS = RHS->IgnoreParenCasts(); // Special case: self-assignments. These are often used to shut up // "unused variable" compiler warnings. if (const DeclRefExpr *RhsDR = dyn_cast<DeclRefExpr>(RHS)) @@ -326,7 +332,7 @@ public: // Look through transitive assignments, e.g.: // int x = y = 0; - E = LookThroughTransitiveAssignments(E); + E = LookThroughTransitiveAssignmentsAndCommaOperators(E); // Don't warn on C++ objects (yet) until we can show that their // constructors/destructors don't have side effects. diff --git a/lib/StaticAnalyzer/Checkers/DebugCheckers.cpp b/lib/StaticAnalyzer/Checkers/DebugCheckers.cpp index 29b4a637cda4..fe12866e51c3 100644 --- a/lib/StaticAnalyzer/Checkers/DebugCheckers.cpp +++ b/lib/StaticAnalyzer/Checkers/DebugCheckers.cpp @@ -7,7 +7,7 @@ // //===----------------------------------------------------------------------===// // -// This file defines a checkers that display debugging information. +// This file defines checkers that display debugging information. // //===----------------------------------------------------------------------===// diff --git a/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp b/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp index 9f176a4b5bf7..759aa6605ee7 100644 --- a/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp +++ b/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp @@ -27,7 +27,8 @@ namespace { class DynamicTypePropagation: public Checker< check::PreCall, check::PostCall, - check::PostStmt<ImplicitCastExpr> > { + check::PostStmt<ImplicitCastExpr>, + check::PostStmt<CXXNewExpr> > { const ObjCObjectType *getObjectTypeForAllocAndNew(const ObjCMessageExpr *MsgE, CheckerContext &C) const; @@ -38,6 +39,7 @@ public: void checkPreCall(const CallEvent &Call, CheckerContext &C) const; void checkPostCall(const CallEvent &Call, CheckerContext &C) const; void checkPostStmt(const ImplicitCastExpr *CastE, CheckerContext &C) const; + void checkPostStmt(const CXXNewExpr *NewE, CheckerContext &C) const; }; } @@ -190,6 +192,20 @@ void DynamicTypePropagation::checkPostStmt(const ImplicitCastExpr *CastE, return; } +void DynamicTypePropagation::checkPostStmt(const CXXNewExpr *NewE, + CheckerContext &C) const { + if (NewE->isArray()) + return; + + // We only track dynamic type info for regions. + const MemRegion *MR = C.getSVal(NewE).getAsRegion(); + if (!MR) + return; + + C.addTransition(C.getState()->setDynamicTypeInfo(MR, NewE->getType(), + /*CanBeSubclass=*/false)); +} + const ObjCObjectType * DynamicTypePropagation::getObjectTypeForAllocAndNew(const ObjCMessageExpr *MsgE, CheckerContext &C) const { diff --git a/lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp b/lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp index 5ed28e955d4e..cc940be7b187 100644 --- a/lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp @@ -437,6 +437,7 @@ visit(const ObjCImplementationDecl *ImplD) const { // Remove ivars invalidated by the partial invalidation methods. They do not // need to be invalidated in the regular invalidation methods. + bool AtImplementationContainsAtLeastOnePartialInvalidationMethod = false; for (MethodSet::iterator I = PartialInfo.InvalidationMethods.begin(), E = PartialInfo.InvalidationMethods.end(); I != E; ++I) { @@ -446,6 +447,8 @@ visit(const ObjCImplementationDecl *ImplD) const { const ObjCMethodDecl *D = ImplD->getMethod(InterfD->getSelector(), InterfD->isInstanceMethod()); if (D && D->hasBody()) { + AtImplementationContainsAtLeastOnePartialInvalidationMethod = true; + bool CalledAnotherInvalidationMethod = false; // The MethodCrowler is going to remove the invalidated ivars. MethodCrawler(Ivars, @@ -471,7 +474,7 @@ visit(const ObjCImplementationDecl *ImplD) const { containsInvalidationMethod(InterfaceD, Info, /*LookForPartial*/ false); // Report an error in case none of the invalidation methods are declared. - if (!Info.needsInvalidation()) { + if (!Info.needsInvalidation() && !PartialInfo.needsInvalidation()) { if (Filter.check_MissingInvalidationMethod) reportNoInvalidationMethod(FirstIvarDecl, IvarToPopertyMap, InterfaceD, /*MissingDeclaration*/ true); @@ -520,9 +523,19 @@ visit(const ObjCImplementationDecl *ImplD) const { } // Report an error in case none of the invalidation methods are implemented. - if (!AtImplementationContainsAtLeastOneInvalidationMethod) - reportNoInvalidationMethod(FirstIvarDecl, IvarToPopertyMap, InterfaceD, - /*MissingDeclaration*/ false); + if (!AtImplementationContainsAtLeastOneInvalidationMethod) { + if (AtImplementationContainsAtLeastOnePartialInvalidationMethod) { + // Warn on the ivars that were not invalidated by the prrtial + // invalidation methods. + for (IvarSet::const_iterator + I = Ivars.begin(), E = Ivars.end(); I != E; ++I) + reportIvarNeedsInvalidation(I->first, IvarToPopertyMap, 0); + } else { + // Otherwise, no invalidation methods were implemented. + reportNoInvalidationMethod(FirstIvarDecl, IvarToPopertyMap, InterfaceD, + /*MissingDeclaration*/ false); + } + } } void IvarInvalidationCheckerImpl:: @@ -551,19 +564,27 @@ reportNoInvalidationMethod(const ObjCIvarDecl *FirstIvarDecl, void IvarInvalidationCheckerImpl:: reportIvarNeedsInvalidation(const ObjCIvarDecl *IvarD, - const IvarToPropMapTy &IvarToPopertyMap, - const ObjCMethodDecl *MethodD) const { + 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); + if (MethodD) { + PathDiagnosticLocation MethodDecLocation = + PathDiagnosticLocation::createEnd(MethodD->getBody(), + BR.getSourceManager(), + Mgr.getAnalysisDeclContext(MethodD)); + BR.EmitBasicReport(MethodD, "Incomplete invalidation", + categories::CoreFoundationObjectiveC, os.str(), + MethodDecLocation); + } else { + BR.EmitBasicReport(IvarD, "Incomplete invalidation", + categories::CoreFoundationObjectiveC, os.str(), + PathDiagnosticLocation::createBegin(IvarD, + BR.getSourceManager())); + + } } void IvarInvalidationCheckerImpl::MethodCrawler::markInvalidated( diff --git a/lib/StaticAnalyzer/Checkers/MallocChecker.cpp b/lib/StaticAnalyzer/Checkers/MallocChecker.cpp index 4b0e7661d8da..5d3eb65148dc 100644 --- a/lib/StaticAnalyzer/Checkers/MallocChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/MallocChecker.cpp @@ -50,7 +50,12 @@ class RefState { Released, // The responsibility for freeing resources has transfered from // this reference. A relinquished symbol should not be freed. - Relinquished }; + Relinquished, + // We are no longer guaranteed to have observed all manipulations + // of this pointer/memory. For example, it could have been + // passed as a parameter to an opaque function. + Escaped + }; const Stmt *S; unsigned K : 2; // Kind enum, but stored as a bitfield. @@ -58,12 +63,15 @@ class RefState { // family. RefState(Kind k, const Stmt *s, unsigned family) - : S(s), K(k), Family(family) {} + : S(s), K(k), Family(family) { + assert(family != AF_None); + } public: bool isAllocated() const { return K == Allocated; } bool isReleased() const { return K == Released; } bool isRelinquished() const { return K == Relinquished; } - AllocationFamily getAllocationFamily() const { + bool isEscaped() const { return K == Escaped; } + AllocationFamily getAllocationFamily() const { return (AllocationFamily)Family; } const Stmt *getStmt() const { return S; } @@ -81,6 +89,9 @@ public: static RefState getRelinquished(unsigned family, const Stmt *s) { return RefState(Relinquished, s, family); } + static RefState getEscaped(const RefState *RS) { + return RefState(Escaped, RS->getStmt(), RS->getAllocationFamily()); + } void Profile(llvm::FoldingSetNodeID &ID) const { ID.AddInteger(K); @@ -136,7 +147,7 @@ class MallocChecker : public Checker<check::DeadSymbols, check::PointerEscape, check::ConstPointerEscape, check::PreStmt<ReturnStmt>, - check::PreStmt<CallExpr>, + check::PreCall, check::PostStmt<CallExpr>, check::PostStmt<CXXNewExpr>, check::PreStmt<CXXDeleteExpr>, @@ -164,12 +175,13 @@ public: DefaultBool CMallocPessimistic; DefaultBool CMallocOptimistic; DefaultBool CNewDeleteChecker; + DefaultBool CNewDeleteLeaksChecker; DefaultBool CMismatchedDeallocatorChecker; }; ChecksFilter Filter; - void checkPreStmt(const CallExpr *S, CheckerContext &C) const; + void checkPreCall(const CallEvent &Call, 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; @@ -282,19 +294,20 @@ private: 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; - + ///@{ + /// Tells if a given family/call/symbol is tracked by the current checker. + bool isTrackedByCurrentChecker(AllocationFamily Family) const; + bool isTrackedByCurrentChecker(CheckerContext &C, + const Stmt *AllocDeallocStmt) const; + bool isTrackedByCurrentChecker(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 Expr *DeallocExpr) const; void ReportMismatchedDealloc(CheckerContext &C, SourceRange Range, - const Expr *DeallocExpr, - const RefState *RS) const; + const Expr *DeallocExpr, const RefState *RS, + SymbolRef Sym) const; void ReportOffsetFree(CheckerContext &C, SVal ArgVal, SourceRange Range, const Expr *DeallocExpr, const Expr *AllocExpr = 0) const; @@ -1007,36 +1020,37 @@ ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C, 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()) && + // Check for double free first. + if ((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; + // If the pointer is allocated or escaped, but we are now trying to free it, + // check that the call to free is proper. + } else if (RsBase->isAllocated() || RsBase->isEscaped()) { + + // Check if an expected deallocation function matches the real one. + bool DeallocMatchesAlloc = + RsBase->getAllocationFamily() == getAllocationFamily(C, ParentExpr); + if (!DeallocMatchesAlloc) { + ReportMismatchedDealloc(C, ArgExpr->getSourceRange(), + ParentExpr, RsBase, SymBase); + return 0; + } + + // Check if the memory location being freed is the actual location + // allocated, or an offset. + RegionOffset Offset = R->getAsOffset(); + if (Offset.isValid() && + !Offset.hasSymbolicOffset() && + Offset.getOffset() != 0) { + const Expr *AllocExpr = cast<Expr>(RsBase->getStmt()); + ReportOffsetFree(C, ArgVal, ArgExpr->getSourceRange(), ParentExpr, + AllocExpr); + return 0; + } } } @@ -1056,7 +1070,8 @@ ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C, } } - AllocationFamily Family = RsBase ? RsBase->getAllocationFamily() : AF_None; + AllocationFamily Family = RsBase ? RsBase->getAllocationFamily() + : getAllocationFamily(C, ParentExpr); // Normal free. if (Hold) return State->set<RegionState>(SymBase, @@ -1067,7 +1082,7 @@ ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C, RefState::getReleased(Family, ParentExpr)); } -bool MallocChecker::isTrackedFamily(AllocationFamily Family) const { +bool MallocChecker::isTrackedByCurrentChecker(AllocationFamily Family) const { switch (Family) { case AF_Malloc: { if (!Filter.CMallocOptimistic && !Filter.CMallocPessimistic) @@ -1081,22 +1096,24 @@ bool MallocChecker::isTrackedFamily(AllocationFamily Family) const { return true; } case AF_None: { - return true; + llvm_unreachable("no family"); } } llvm_unreachable("unhandled family"); } -bool MallocChecker::isTrackedFamily(CheckerContext &C, - const Stmt *AllocDeallocStmt) const { - return isTrackedFamily(getAllocationFamily(C, AllocDeallocStmt)); +bool +MallocChecker::isTrackedByCurrentChecker(CheckerContext &C, + const Stmt *AllocDeallocStmt) const { + return isTrackedByCurrentChecker(getAllocationFamily(C, AllocDeallocStmt)); } -bool MallocChecker::isTrackedFamily(CheckerContext &C, SymbolRef Sym) const { - const RefState *RS = C.getState()->get<RegionState>(Sym); +bool MallocChecker::isTrackedByCurrentChecker(CheckerContext &C, + SymbolRef Sym) const { - return RS ? isTrackedFamily(RS->getAllocationFamily()) - : isTrackedFamily(AF_None); + const RefState *RS = C.getState()->get<RegionState>(Sym); + assert(RS); + return isTrackedByCurrentChecker(RS->getAllocationFamily()); } bool MallocChecker::SummarizeValue(raw_ostream &os, SVal V) { @@ -1194,7 +1211,7 @@ void MallocChecker::ReportBadFree(CheckerContext &C, SVal ArgVal, !Filter.CNewDeleteChecker) return; - if (!isTrackedFamily(C, DeallocExpr)) + if (!isTrackedByCurrentChecker(C, DeallocExpr)) return; if (ExplodedNode *N = C.generateSink()) { @@ -1236,7 +1253,8 @@ void MallocChecker::ReportBadFree(CheckerContext &C, SVal ArgVal, void MallocChecker::ReportMismatchedDealloc(CheckerContext &C, SourceRange Range, const Expr *DeallocExpr, - const RefState *RS) const { + const RefState *RS, + SymbolRef Sym) const { if (!Filter.CMismatchedDeallocatorChecker) return; @@ -1266,7 +1284,9 @@ void MallocChecker::ReportMismatchedDealloc(CheckerContext &C, os << ", not " << DeallocOs.str(); BugReport *R = new BugReport(*BT_MismatchedDealloc, os.str(), N); + R->markInteresting(Sym); R->addRange(Range); + R->addVisitor(new MallocBugVisitor(Sym)); C.emitReport(R); } } @@ -1279,7 +1299,7 @@ void MallocChecker::ReportOffsetFree(CheckerContext &C, SVal ArgVal, !Filter.CNewDeleteChecker) return; - if (!isTrackedFamily(C, AllocExpr)) + if (!isTrackedByCurrentChecker(C, AllocExpr)) return; ExplodedNode *N = C.generateSink(); @@ -1331,7 +1351,7 @@ void MallocChecker::ReportUseAfterFree(CheckerContext &C, SourceRange Range, !Filter.CNewDeleteChecker) return; - if (!isTrackedFamily(C, Sym)) + if (!isTrackedByCurrentChecker(C, Sym)) return; if (ExplodedNode *N = C.generateSink()) { @@ -1356,7 +1376,7 @@ void MallocChecker::ReportDoubleFree(CheckerContext &C, SourceRange Range, !Filter.CNewDeleteChecker) return; - if (!isTrackedFamily(C, Sym)) + if (!isTrackedByCurrentChecker(C, Sym)) return; if (ExplodedNode *N = C.generateSink()) { @@ -1510,13 +1530,19 @@ MallocChecker::getAllocationSite(const ExplodedNode *N, SymbolRef Sym, // Find the most recent expression bound to the symbol in the current // context. - if (!ReferenceRegion) { - if (const MemRegion *MR = C.getLocationRegionIfPostStore(N)) { - SVal Val = State->getSVal(MR); - if (Val.getAsLocSymbol() == Sym) - ReferenceRegion = MR; + if (!ReferenceRegion) { + if (const MemRegion *MR = C.getLocationRegionIfPostStore(N)) { + SVal Val = State->getSVal(MR); + if (Val.getAsLocSymbol() == Sym) { + const VarRegion* VR = MR->getBaseRegion()->getAs<VarRegion>(); + // Do not show local variables belonging to a function other than + // where the error is reported. + if (!VR || + (VR->getStackFrame() == LeakContext->getCurrentStackFrame())) + ReferenceRegion = MR; + } + } } - } // Allocation node, is the last node in the current context in which the // symbol was tracked. @@ -1532,12 +1558,21 @@ void MallocChecker::reportLeak(SymbolRef Sym, ExplodedNode *N, CheckerContext &C) const { if (!Filter.CMallocOptimistic && !Filter.CMallocPessimistic && - !Filter.CNewDeleteChecker) + !Filter.CNewDeleteLeaksChecker) return; - if (!isTrackedFamily(C, Sym)) + const RefState *RS = C.getState()->get<RegionState>(Sym); + assert(RS && "cannot leak an untracked symbol"); + AllocationFamily Family = RS->getAllocationFamily(); + if (!isTrackedByCurrentChecker(Family)) return; + // Special case for new and new[]; these are controlled by a separate checker + // flag so that they can be selectively disabled. + if (Family == AF_CXXNew || Family == AF_CXXNewArray) + if (!Filter.CNewDeleteLeaksChecker) + return; + assert(N); if (!BT_Leak) { BT_Leak.reset(new BugType("Memory leak", "Memory Error")); @@ -1570,11 +1605,11 @@ void MallocChecker::reportLeak(SymbolRef Sym, ExplodedNode *N, SmallString<200> buf; llvm::raw_svector_ostream os(buf); - os << "Memory is never released; potential leak"; if (Region && Region->canPrintPretty()) { - os << " of memory pointed to by '"; + os << "Potential leak of memory pointed to by "; Region->printPretty(os); - os << '\''; + } else { + os << "Potential memory leak"; } BugReport *R = new BugReport(*BT_Leak, os.str(), N, @@ -1638,26 +1673,39 @@ void MallocChecker::checkDeadSymbols(SymbolReaper &SymReaper, C.addTransition(state->set<RegionState>(RS), N); } -void MallocChecker::checkPreStmt(const CallExpr *CE, CheckerContext &C) const { +void MallocChecker::checkPreCall(const CallEvent &Call, + CheckerContext &C) const { + // We will check for double free in the post visit. - if ((Filter.CMallocOptimistic || Filter.CMallocPessimistic) && - isFreeFunction(C.getCalleeDecl(CE), C.getASTContext())) - return; + if (const AnyFunctionCall *FC = dyn_cast<AnyFunctionCall>(&Call)) { + const FunctionDecl *FD = FC->getDecl(); + if (!FD) + return; - if (Filter.CNewDeleteChecker && - isStandardNewDelete(C.getCalleeDecl(CE), C.getASTContext())) - return; + if ((Filter.CMallocOptimistic || Filter.CMallocPessimistic) && + isFreeFunction(FD, C.getASTContext())) + return; - // Check use after free, when a freed pointer is passed to a call. - ProgramStateRef State = C.getState(); - for (CallExpr::const_arg_iterator I = CE->arg_begin(), - E = CE->arg_end(); I != E; ++I) { - const Expr *A = *I; - if (A->getType().getTypePtr()->isAnyPointerType()) { - SymbolRef Sym = C.getSVal(A).getAsSymbol(); + if (Filter.CNewDeleteChecker && + isStandardNewDelete(FD, C.getASTContext())) + return; + } + + // Check if the callee of a method is deleted. + if (const CXXInstanceCall *CC = dyn_cast<CXXInstanceCall>(&Call)) { + SymbolRef Sym = CC->getCXXThisVal().getAsSymbol(); + if (!Sym || checkUseAfterFree(Sym, C, CC->getCXXThisExpr())) + return; + } + + // Check arguments for being used after free. + for (unsigned I = 0, E = Call.getNumArgs(); I != E; ++I) { + SVal ArgSVal = Call.getArgSVal(I); + if (ArgSVal.getAs<Loc>()) { + SymbolRef Sym = ArgSVal.getAsSymbol(); if (!Sym) continue; - if (checkUseAfterFree(Sym, C, A)) + if (checkUseAfterFree(Sym, C, Call.getArgExpr(I))) return; } } @@ -1976,8 +2024,10 @@ ProgramStateRef MallocChecker::checkPointerEscapeAux(ProgramStateRef State, SymbolRef sym = *I; if (const RefState *RS = State->get<RegionState>(sym)) { - if (RS->isAllocated() && CheckRefState(RS)) + if (RS->isAllocated() && CheckRefState(RS)) { State = State->remove<RegionState>(sym); + State = State->set<RegionState>(sym, RefState::getEscaped(RS)); + } } } return State; @@ -2042,7 +2092,7 @@ MallocChecker::MallocBugVisitor::VisitNode(const ExplodedNode *N, } else if (isReleased(RS, RSPrev, S)) { Msg = "Memory is released"; StackHint = new StackHintGeneratorForSymbol(Sym, - "Returned released memory"); + "Returning; memory was released"); } else if (isRelinquished(RS, RSPrev, S)) { Msg = "Memory ownership is transfered"; StackHint = new StackHintGeneratorForSymbol(Sym, ""); @@ -2102,6 +2152,14 @@ void MallocChecker::printState(raw_ostream &Out, ProgramStateRef State, } } +void ento::registerNewDeleteLeaksChecker(CheckerManager &mgr) { + registerCStringCheckerBasic(mgr); + mgr.registerChecker<MallocChecker>()->Filter.CNewDeleteLeaksChecker = true; + // We currently treat NewDeleteLeaks checker as a subchecker of NewDelete + // checker. + mgr.registerChecker<MallocChecker>()->Filter.CNewDeleteChecker = true; +} + #define REGISTER_CHECKER(name) \ void ento::register##name(CheckerManager &mgr) {\ registerCStringCheckerBasic(mgr); \ diff --git a/lib/StaticAnalyzer/Checkers/MallocSizeofChecker.cpp b/lib/StaticAnalyzer/Checkers/MallocSizeofChecker.cpp index ce7d4ccf7a0a..d29f34fb03e2 100644 --- a/lib/StaticAnalyzer/Checkers/MallocSizeofChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/MallocSizeofChecker.cpp @@ -188,7 +188,7 @@ public: for (CallExpr::const_arg_iterator ai = i->AllocCall->arg_begin(), ae = i->AllocCall->arg_end(); ai != ae; ++ai) { - if (!(*ai)->getType()->isIntegerType()) + if (!(*ai)->getType()->isIntegralOrUnscopedEnumerationType()) continue; SizeofFinder SFinder; diff --git a/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp b/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp index 79409e85bda4..0f456ea8d785 100644 --- a/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp @@ -37,6 +37,8 @@ #include "llvm/ADT/StringExtras.h" #include <cstdarg> +#include "AllocationDiagnostics.h" + using namespace clang; using namespace ento; using llvm::StrInStrNoCase; @@ -324,7 +326,7 @@ void RefVal::print(raw_ostream &Out) const { break; case RefVal::ErrorOverAutorelease: - Out << "Over autoreleased"; + Out << "Over-autoreleased"; break; case RefVal::ErrorReturnedNotOwned: @@ -1114,12 +1116,14 @@ RetainSummaryManager::getFunctionSummary(const FunctionDecl *FD) { // correctly. ScratchArgs = AF.add(ScratchArgs, 12, StopTracking); S = getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, DoNothing); - } else if (FName == "dispatch_set_context") { + } else if (FName == "dispatch_set_context" || + FName == "xpc_connection_set_context") { // <rdar://problem/11059275> - The analyzer currently doesn't have // a good way to reason about the finalizer function for libdispatch. // If we pass a context object that is memory managed, stop tracking it. + // <rdar://problem/13783514> - Same problem, but for XPC. // FIXME: this hack should possibly go away once we can handle - // libdispatch finalizers. + // libdispatch and XPC finalizers. ScratchArgs = AF.add(ScratchArgs, 1, StopTracking); S = getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, DoNothing); } else if (FName.startswith("NSLog")) { @@ -1660,10 +1664,10 @@ namespace { class OverAutorelease : public CFRefBug { public: OverAutorelease() - : CFRefBug("Object sent -autorelease too many times") {} + : CFRefBug("Object autoreleased too many times") {} const char *getDescription() const { - return "Object sent -autorelease too many times"; + return "Object autoreleased too many times"; } }; @@ -1773,11 +1777,11 @@ namespace { class CFRefLeakReport : public CFRefReport { const MemRegion* AllocBinding; - public: CFRefLeakReport(CFRefBug &D, const LangOptions &LOpts, bool GCEnabled, const SummaryLogTy &Log, ExplodedNode *n, SymbolRef sym, - CheckerContext &Ctx); + CheckerContext &Ctx, + bool IncludeAllocationLine); PathDiagnosticLocation getLocation(const SourceManager &SM) const { assert(Location.isValid()); @@ -2048,7 +2052,7 @@ PathDiagnosticPiece *CFRefReportVisitor::VisitNode(const ExplodedNode *N, return 0; assert(PrevV.getAutoreleaseCount() < CurrV.getAutoreleaseCount()); - os << "Object sent -autorelease message"; + os << "Object autoreleased"; break; } @@ -2134,32 +2138,86 @@ PathDiagnosticPiece *CFRefReportVisitor::VisitNode(const ExplodedNode *N, // Find the first node in the current function context that referred to the // tracked symbol and the memory location that value was stored to. Note, the // value is only reported if the allocation occurred in the same function as -// the leak. -static std::pair<const ExplodedNode*,const MemRegion*> +// the leak. The function can also return a location context, which should be +// treated as interesting. +struct AllocationInfo { + const ExplodedNode* N; + const MemRegion *R; + const LocationContext *InterestingMethodContext; + AllocationInfo(const ExplodedNode *InN, + const MemRegion *InR, + const LocationContext *InInterestingMethodContext) : + N(InN), R(InR), InterestingMethodContext(InInterestingMethodContext) {} +}; + +static AllocationInfo GetAllocationSite(ProgramStateManager& StateMgr, const ExplodedNode *N, SymbolRef Sym) { - const ExplodedNode *Last = N; + const ExplodedNode *AllocationNode = N; + const ExplodedNode *AllocationNodeInCurrentContext = N; const MemRegion* FirstBinding = 0; const LocationContext *LeakContext = N->getLocationContext(); + // The location context of the init method called on the leaked object, if + // available. + const LocationContext *InitMethodContext = 0; + while (N) { ProgramStateRef St = N->getState(); + const LocationContext *NContext = N->getLocationContext(); if (!getRefBinding(St, Sym)) break; StoreManager::FindUniqueBinding FB(Sym); StateMgr.iterBindings(St, FB); - if (FB) FirstBinding = FB.getRegion(); + + if (FB) { + const MemRegion *R = FB.getRegion(); + const VarRegion *VR = R->getBaseRegion()->getAs<VarRegion>(); + // Do not show local variables belonging to a function other than + // where the error is reported. + if (!VR || VR->getStackFrame() == LeakContext->getCurrentStackFrame()) + FirstBinding = R; + } - // Allocation node, is the last node in the current context in which the - // symbol was tracked. - if (N->getLocationContext() == LeakContext) - Last = N; + // AllocationNode is the last node in which the symbol was tracked. + AllocationNode = N; + + // AllocationNodeInCurrentContext, is the last node in the current context + // in which the symbol was tracked. + if (NContext == LeakContext) + AllocationNodeInCurrentContext = N; + + // Find the last init that was called on the given symbol and store the + // init method's location context. + if (!InitMethodContext) + if (Optional<CallEnter> CEP = N->getLocation().getAs<CallEnter>()) { + const Stmt *CE = CEP->getCallExpr(); + if (const ObjCMessageExpr *ME = dyn_cast_or_null<ObjCMessageExpr>(CE)) { + const Stmt *RecExpr = ME->getInstanceReceiver(); + if (RecExpr) { + SVal RecV = St->getSVal(RecExpr, NContext); + if (ME->getMethodFamily() == OMF_init && RecV.getAsSymbol() == Sym) + InitMethodContext = CEP->getCalleeContext(); + } + } + } N = N->pred_empty() ? NULL : *(N->pred_begin()); } + // If we are reporting a leak of the object that was allocated with alloc, + // mark its init method as interesting. + const LocationContext *InterestingMethodContext = 0; + if (InitMethodContext) { + const ProgramPoint AllocPP = AllocationNode->getLocation(); + if (Optional<StmtPoint> SP = AllocPP.getAs<StmtPoint>()) + if (const ObjCMessageExpr *ME = SP->getStmtAs<ObjCMessageExpr>()) + if (ME->getMethodFamily() == OMF_alloc) + InterestingMethodContext = InitMethodContext; + } + // If allocation happened in a function different from the leak node context, // do not report the binding. assert(N && "Could not find allocation node"); @@ -2167,7 +2225,9 @@ GetAllocationSite(ProgramStateManager& StateMgr, const ExplodedNode *N, FirstBinding = 0; } - return std::make_pair(Last, FirstBinding); + return AllocationInfo(AllocationNodeInCurrentContext, + FirstBinding, + InterestingMethodContext); } PathDiagnosticPiece* @@ -2190,12 +2250,12 @@ CFRefLeakReportVisitor::getEndPath(BugReporterContext &BRC, // We are reporting a leak. Walk up the graph to get to the first node where // the symbol appeared, and also get the first VarDecl that tracked object // is stored to. - const ExplodedNode *AllocNode = 0; - const MemRegion* FirstBinding = 0; - - llvm::tie(AllocNode, FirstBinding) = + AllocationInfo AllocI = GetAllocationSite(BRC.getStateManager(), EndN, Sym); + const MemRegion* FirstBinding = AllocI.R; + BR.markInteresting(AllocI.InterestingMethodContext); + SourceManager& SM = BRC.getSourceManager(); // Compute an actual location for the leak. Sometimes a leak doesn't @@ -2267,8 +2327,9 @@ CFRefLeakReportVisitor::getEndPath(BugReporterContext &BRC, CFRefLeakReport::CFRefLeakReport(CFRefBug &D, const LangOptions &LOpts, bool GCEnabled, const SummaryLogTy &Log, ExplodedNode *n, SymbolRef sym, - CheckerContext &Ctx) -: CFRefReport(D, LOpts, GCEnabled, Log, n, sym, false) { + CheckerContext &Ctx, + bool IncludeAllocationLine) + : CFRefReport(D, LOpts, GCEnabled, Log, n, sym, false) { // Most bug reports are cached at the location where they occurred. // With leaks, we want to unique them by the location where they were @@ -2282,9 +2343,13 @@ CFRefLeakReport::CFRefLeakReport(CFRefBug &D, const LangOptions &LOpts, const SourceManager& SMgr = Ctx.getSourceManager(); - llvm::tie(AllocNode, AllocBinding) = // Set AllocBinding. + AllocationInfo AllocI = GetAllocationSite(Ctx.getStateManager(), getErrorNode(), sym); + AllocNode = AllocI.N; + AllocBinding = AllocI.R; + markInteresting(AllocI.InterestingMethodContext); + // Get the SourceLocation for the allocation site. // FIXME: This will crash the analyzer if an allocation comes from an // implicit call. (Currently there are no such allocations in Cocoa, though.) @@ -2295,8 +2360,17 @@ CFRefLeakReport::CFRefLeakReport(CFRefBug &D, const LangOptions &LOpts, else AllocStmt = P.castAs<PostStmt>().getStmt(); assert(AllocStmt && "All allocations must come from explicit calls"); - Location = PathDiagnosticLocation::createBegin(AllocStmt, SMgr, - n->getLocationContext()); + + PathDiagnosticLocation AllocLocation = + PathDiagnosticLocation::createBegin(AllocStmt, SMgr, + AllocNode->getLocationContext()); + Location = AllocLocation; + + // Set uniqieing info, which will be used for unique the bug reports. The + // leaks should be uniqued on the allocation site. + UniqueingLocation = AllocLocation; + UniqueingDecl = AllocNode->getLocationContext()->getDecl(); + // Fill in the description of the bug. Description.clear(); llvm::raw_string_ostream os(Description); @@ -2305,9 +2379,13 @@ CFRefLeakReport::CFRefLeakReport(CFRefBug &D, const LangOptions &LOpts, os << "(when using garbage collection) "; os << "of an object"; - // FIXME: AllocBinding doesn't get populated for RegionStore yet. - if (AllocBinding) + if (AllocBinding) { os << " stored into '" << AllocBinding->getString() << '\''; + if (IncludeAllocationLine) { + FullSourceLoc SL(AllocStmt->getLocStart(), Ctx.getSourceManager()); + os << " (allocated on line " << SL.getSpellingLineNumber() << ")"; + } + } addVisitor(new CFRefLeakReportVisitor(sym, GCEnabled, Log)); } @@ -2348,8 +2426,14 @@ class RetainCountChecker mutable SummaryLogTy SummaryLog; mutable bool ShouldResetSummaryLog; + /// Optional setting to indicate if leak reports should include + /// the allocation line. + mutable bool IncludeAllocationLine; + public: - RetainCountChecker() : ShouldResetSummaryLog(false) {} + RetainCountChecker(AnalyzerOptions &AO) + : ShouldResetSummaryLog(false), + IncludeAllocationLine(shouldIncludeAllocationSiteInLeakDiagnostics(AO)) {} virtual ~RetainCountChecker() { DeleteContainerSeconds(DeadSymbolTags); @@ -3294,7 +3378,8 @@ void RetainCountChecker::checkReturnWithRetEffect(const ReturnStmt *S, CFRefReport *report = new CFRefLeakReport(*getLeakAtReturnBug(LOpts, GCEnabled), LOpts, GCEnabled, SummaryLog, - N, Sym, C); + N, Sym, C, IncludeAllocationLine); + C.emitReport(report); } } @@ -3480,10 +3565,12 @@ RetainCountChecker::handleAutoreleaseCounts(ProgramStateRef state, if (N) { SmallString<128> sbuf; llvm::raw_svector_ostream os(sbuf); - os << "Object over-autoreleased: object was sent -autorelease "; + os << "Object was autoreleased "; if (V.getAutoreleaseCount() > 1) - os << V.getAutoreleaseCount() << " times "; - os << "but the object has a +" << V.getCount() << " retain count"; + os << V.getAutoreleaseCount() << " times but the object "; + else + os << "but "; + os << "has a +" << V.getCount() << " retain count"; if (!overAutorelease) overAutorelease.reset(new OverAutorelease()); @@ -3534,7 +3621,8 @@ RetainCountChecker::processLeaks(ProgramStateRef state, assert(BT && "BugType not initialized."); CFRefLeakReport *report = new CFRefLeakReport(*BT, LOpts, GCEnabled, - SummaryLog, N, *I, Ctx); + SummaryLog, N, *I, Ctx, + IncludeAllocationLine); Ctx.emitReport(report); } } @@ -3656,6 +3744,6 @@ void RetainCountChecker::printState(raw_ostream &Out, ProgramStateRef State, //===----------------------------------------------------------------------===// void ento::registerRetainCountChecker(CheckerManager &Mgr) { - Mgr.registerChecker<RetainCountChecker>(); + Mgr.registerChecker<RetainCountChecker>(Mgr.getAnalyzerOptions()); } diff --git a/lib/StaticAnalyzer/Checkers/ReturnUndefChecker.cpp b/lib/StaticAnalyzer/Checkers/ReturnUndefChecker.cpp index 7a5d99360108..ed96c401a7aa 100644 --- a/lib/StaticAnalyzer/Checkers/ReturnUndefChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/ReturnUndefChecker.cpp @@ -55,8 +55,17 @@ void ReturnUndefChecker::checkPreStmt(const ReturnStmt *RS, // void test() { // return foo(); // } - if (RT.isNull() || !RT->isVoidType()) - emitUndef(C, RetE); + if (!RT.isNull() && RT->isVoidType()) + return; + + // Not all blocks have explicitly-specified return types; if the return type + // is not available, but the return value expression has 'void' type, assume + // Sema already checked it. + if (RT.isNull() && isa<BlockDecl>(SFC->getDecl()) && + RetE->getType()->isVoidType()) + return; + + emitUndef(C, RetE); return; } |