diff options
Diffstat (limited to 'lib/StaticAnalyzer/Checkers')
21 files changed, 304 insertions, 188 deletions
diff --git a/lib/StaticAnalyzer/Checkers/AllocationDiagnostics.h b/lib/StaticAnalyzer/Checkers/AllocationDiagnostics.h index 2b314a3b749a1..048418ef62db7 100644 --- a/lib/StaticAnalyzer/Checkers/AllocationDiagnostics.h +++ b/lib/StaticAnalyzer/Checkers/AllocationDiagnostics.h @@ -11,8 +11,8 @@ // //===----------------------------------------------------------------------===// -#ifndef LLVM_CLANG_SA_LIB_CHECKERS_ALLOC_DIAGS_H -#define LLVM_CLANG_SA_LIB_CHECKERS_ALLOC_DIAGS_H +#ifndef LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_ALLOCATIONDIAGNOSTICS_H +#define LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_ALLOCATIONDIAGNOSTICS_H #include "clang/StaticAnalyzer/Core/AnalyzerOptions.h" diff --git a/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp b/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp index 20360efccfe47..e462e2b2f15e7 100644 --- a/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp +++ b/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp @@ -218,17 +218,6 @@ void RegionRawOffsetV2::dumpToStream(raw_ostream &os) const { os << "raw_offset_v2{" << getRegion() << ',' << getByteOffset() << '}'; } -// FIXME: Merge with the implementation of the same method in Store.cpp -static bool IsCompleteType(ASTContext &Ctx, QualType Ty) { - if (const RecordType *RT = Ty->getAs<RecordType>()) { - const RecordDecl *D = RT->getDecl(); - if (!D->getDefinition()) - return false; - } - - return true; -} - // Lazily computes a value to be used by 'computeOffset'. If 'val' // is unknown or undefined, we lazily substitute '0'. Otherwise, @@ -288,7 +277,7 @@ RegionRawOffsetV2 RegionRawOffsetV2::computeOffset(ProgramStateRef state, QualType elemType = elemReg->getElementType(); // If the element is an incomplete type, go no further. ASTContext &astContext = svalBuilder.getContext(); - if (!IsCompleteType(astContext, elemType)) + if (elemType->isIncompleteType()) return RegionRawOffsetV2(); // Update the offset. diff --git a/lib/StaticAnalyzer/Checkers/BuiltinFunctionChecker.cpp b/lib/StaticAnalyzer/Checkers/BuiltinFunctionChecker.cpp index a871049848835..104a81eac4c74 100644 --- a/lib/StaticAnalyzer/Checkers/BuiltinFunctionChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/BuiltinFunctionChecker.cpp @@ -42,8 +42,10 @@ bool BuiltinFunctionChecker::evalCall(const CallExpr *CE, return false; case Builtin::BI__builtin_expect: + case Builtin::BI__builtin_assume_aligned: case Builtin::BI__builtin_addressof: { - // For __builtin_expect, just return the value of the subexpression. + // For __builtin_expect and __builtin_assume_aligned, just return the value + // of the subexpression. // __builtin_addressof is going from a reference to a pointer, but those // are represented the same way in the analyzer. assert (CE->arg_begin() != CE->arg_end()); diff --git a/lib/StaticAnalyzer/Checkers/CStringChecker.cpp b/lib/StaticAnalyzer/Checkers/CStringChecker.cpp index 0693bd6fd94df..e91a7e16802e7 100644 --- a/lib/StaticAnalyzer/Checkers/CStringChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/CStringChecker.cpp @@ -969,8 +969,13 @@ void CStringChecker::evalCopyCommon(CheckerContext &C, // Get the length to copy. if (Optional<NonLoc> lenValNonLoc = sizeVal.getAs<NonLoc>()) { // Get the byte after the last byte copied. + SValBuilder &SvalBuilder = C.getSValBuilder(); + ASTContext &Ctx = SvalBuilder.getContext(); + QualType CharPtrTy = Ctx.getPointerType(Ctx.CharTy); + loc::MemRegionVal DestRegCharVal = SvalBuilder.evalCast(destRegVal, + CharPtrTy, Dest->getType()).castAs<loc::MemRegionVal>(); SVal lastElement = C.getSValBuilder().evalBinOpLN(state, BO_Add, - destRegVal, + DestRegCharVal, *lenValNonLoc, Dest->getType()); diff --git a/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp b/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp index d186144b9ddd1..339af8f033245 100644 --- a/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp +++ b/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp @@ -28,27 +28,6 @@ using namespace clang; using namespace ento; -static bool scan_dealloc(Stmt *S, Selector Dealloc) { - - if (ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(S)) - if (ME->getSelector() == Dealloc) { - switch (ME->getReceiverKind()) { - case ObjCMessageExpr::Instance: return false; - case ObjCMessageExpr::SuperInstance: return true; - case ObjCMessageExpr::Class: break; - case ObjCMessageExpr::SuperClass: break; - } - } - - // Recurse to children. - - for (Stmt::child_iterator I = S->child_begin(), E= S->child_end(); I!=E; ++I) - if (*I && scan_dealloc(*I, Dealloc)) - return true; - - return false; -} - static bool scan_ivar_release(Stmt *S, ObjCIvarDecl *ID, const ObjCPropertyDecl *PD, Selector Release, @@ -181,24 +160,6 @@ static void checkObjCDealloc(const CheckerBase *Checker, return; } - // dealloc found. Scan for missing [super dealloc]. - if (MD->getBody() && !scan_dealloc(MD->getBody(), S)) { - - const char* name = LOpts.getGC() == LangOptions::NonGC - ? "missing [super dealloc]" - : "missing [super dealloc] (Hybrid MM, non-GC)"; - - std::string buf; - llvm::raw_string_ostream os(buf); - os << "The 'dealloc' instance method in Objective-C class '" << *D - << "' does not send a 'dealloc' message to its super class" - " (missing [super dealloc])"; - - BR.EmitBasicReport(MD, Checker, name, categories::CoreFoundationObjectiveC, - os.str(), DLoc); - return; - } - // Get the "release" selector. IdentifierInfo* RII = &Ctx.Idents.get("release"); Selector RS = Ctx.Selectors.getSelector(0, &RII); diff --git a/lib/StaticAnalyzer/Checkers/Checkers.td b/lib/StaticAnalyzer/Checkers/Checkers.td index 44eb64187bfbf..ba5b4fa3c66c8 100644 --- a/lib/StaticAnalyzer/Checkers/Checkers.td +++ b/lib/StaticAnalyzer/Checkers/Checkers.td @@ -184,6 +184,10 @@ def NewDeleteChecker : Checker<"NewDelete">, HelpText<"Check for double-free and use-after-free problems. Traces memory managed by new/delete.">, DescFile<"MallocChecker.cpp">; +def NewDeleteLeaksChecker : Checker<"NewDeleteLeaks">, + HelpText<"Check for memory leaks. Traces memory managed by new/delete.">, + DescFile<"MallocChecker.cpp">; + } // end: "cplusplus" let ParentPackage = CplusplusAlpha in { @@ -192,10 +196,6 @@ def VirtualCallChecker : Checker<"VirtualCall">, HelpText<"Check virtual function calls during construction or destruction">, DescFile<"VirtualCallChecker.cpp">; -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/ClangSACheckers.h b/lib/StaticAnalyzer/Checkers/ClangSACheckers.h index de2ebce52c026..05b4a61c5af12 100644 --- a/lib/StaticAnalyzer/Checkers/ClangSACheckers.h +++ b/lib/StaticAnalyzer/Checkers/ClangSACheckers.h @@ -12,8 +12,8 @@ // //===----------------------------------------------------------------------===// -#ifndef LLVM_CLANG_SA_LIB_CHECKERS_CLANGSACHECKERS_H -#define LLVM_CLANG_SA_LIB_CHECKERS_CLANGSACHECKERS_H +#ifndef LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_CLANGSACHECKERS_H +#define LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_CLANGSACHECKERS_H #include "clang/StaticAnalyzer/Core/BugReporter/CommonBugCategories.h" diff --git a/lib/StaticAnalyzer/Checkers/IdenticalExprChecker.cpp b/lib/StaticAnalyzer/Checkers/IdenticalExprChecker.cpp index d5c52b4c6a310..58d0783f3974d 100644 --- a/lib/StaticAnalyzer/Checkers/IdenticalExprChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/IdenticalExprChecker.cpp @@ -445,7 +445,12 @@ static bool isIdenticalStmt(const ASTContext &Ctx, const Stmt *Stmt1, case Stmt::IntegerLiteralClass: { const IntegerLiteral *IntLit1 = cast<IntegerLiteral>(Stmt1); const IntegerLiteral *IntLit2 = cast<IntegerLiteral>(Stmt2); - return IntLit1->getValue() == IntLit2->getValue(); + + llvm::APInt I1 = IntLit1->getValue(); + llvm::APInt I2 = IntLit2->getValue(); + if (I1.getBitWidth() != I2.getBitWidth()) + return false; + return I1 == I2; } case Stmt::FloatingLiteralClass: { const FloatingLiteral *FloatLit1 = cast<FloatingLiteral>(Stmt1); @@ -455,7 +460,7 @@ static bool isIdenticalStmt(const ASTContext &Ctx, const Stmt *Stmt1, case Stmt::StringLiteralClass: { const StringLiteral *StringLit1 = cast<StringLiteral>(Stmt1); const StringLiteral *StringLit2 = cast<StringLiteral>(Stmt2); - return StringLit1->getString() == StringLit2->getString(); + return StringLit1->getBytes() == StringLit2->getBytes(); } case Stmt::MemberExprClass: { const MemberExpr *MemberStmt1 = cast<MemberExpr>(Stmt1); diff --git a/lib/StaticAnalyzer/Checkers/InterCheckerAPI.h b/lib/StaticAnalyzer/Checkers/InterCheckerAPI.h index e35557f24bbc0..b7549fd17da95 100644 --- a/lib/StaticAnalyzer/Checkers/InterCheckerAPI.h +++ b/lib/StaticAnalyzer/Checkers/InterCheckerAPI.h @@ -10,8 +10,8 @@ // inter-checker communications. //===----------------------------------------------------------------------===// -#ifndef INTERCHECKERAPI_H_ -#define INTERCHECKERAPI_H_ +#ifndef LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_INTERCHECKERAPI_H +#define LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_INTERCHECKERAPI_H namespace clang { namespace ento { diff --git a/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp b/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp index 0f227bba0000e..13ea4d3ed090a 100644 --- a/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp @@ -270,7 +270,7 @@ void MacOSKeychainAPIChecker:: os << "Deallocator doesn't match the allocator: '" << FunctionsToTrack[PDeallocIdx].Name << "' should be used."; BugReport *Report = new BugReport(*BT, os.str(), N); - Report->addVisitor(new SecKeychainBugVisitor(AP.first)); + Report->addVisitor(llvm::make_unique<SecKeychainBugVisitor>(AP.first)); Report->addRange(ArgExpr->getSourceRange()); markInteresting(Report, AP); C.emitReport(Report); @@ -311,7 +311,7 @@ void MacOSKeychainAPIChecker::checkPreStmt(const CallExpr *CE, << FunctionsToTrack[DIdx].Name << "'."; BugReport *Report = new BugReport(*BT, os.str(), N); - Report->addVisitor(new SecKeychainBugVisitor(V)); + Report->addVisitor(llvm::make_unique<SecKeychainBugVisitor>(V)); Report->addRange(ArgExpr->getSourceRange()); Report->markInteresting(AS->Region); C.emitReport(Report); @@ -430,7 +430,7 @@ void MacOSKeychainAPIChecker::checkPreStmt(const CallExpr *CE, initBugType(); BugReport *Report = new BugReport(*BT, "Only call free if a valid (non-NULL) buffer was returned.", N); - Report->addVisitor(new SecKeychainBugVisitor(ArgSM)); + Report->addVisitor(llvm::make_unique<SecKeychainBugVisitor>(ArgSM)); Report->addRange(ArgExpr->getSourceRange()); Report->markInteresting(AS->Region); C.emitReport(Report); @@ -540,7 +540,7 @@ BugReport *MacOSKeychainAPIChecker:: BugReport *Report = new BugReport(*BT, os.str(), N, LocUsedForUniqueing, AllocNode->getLocationContext()->getDecl()); - Report->addVisitor(new SecKeychainBugVisitor(AP.first)); + Report->addVisitor(llvm::make_unique<SecKeychainBugVisitor>(AP.first)); markInteresting(Report, AP); return Report; } diff --git a/lib/StaticAnalyzer/Checkers/MallocChecker.cpp b/lib/StaticAnalyzer/Checkers/MallocChecker.cpp index a03fa259005af..aee5a43048b95 100644 --- a/lib/StaticAnalyzer/Checkers/MallocChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/MallocChecker.cpp @@ -15,6 +15,7 @@ #include "ClangSACheckers.h" #include "InterCheckerAPI.h" #include "clang/AST/Attr.h" +#include "clang/AST/ParentMap.h" #include "clang/Basic/SourceManager.h" #include "clang/Basic/TargetInfo.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" @@ -41,7 +42,8 @@ enum AllocationFamily { AF_None, AF_Malloc, AF_CXXNew, - AF_CXXNewArray + AF_CXXNewArray, + AF_IfNameIndex }; class RefState { @@ -160,7 +162,8 @@ public: MallocChecker() : II_malloc(nullptr), II_free(nullptr), II_realloc(nullptr), II_calloc(nullptr), II_valloc(nullptr), II_reallocf(nullptr), - II_strndup(nullptr), II_strdup(nullptr), II_kmalloc(nullptr) {} + II_strndup(nullptr), II_strdup(nullptr), II_kmalloc(nullptr), + II_if_nameindex(nullptr), II_if_freenameindex(nullptr) {} /// In pessimistic mode, the checker assumes that it does not know which /// functions might free the memory. @@ -173,6 +176,12 @@ public: CK_NumCheckKinds }; + enum class MemoryOperationKind { + MOK_Allocate, + MOK_Free, + MOK_Any + }; + DefaultBool ChecksEnabled[CK_NumCheckKinds]; CheckName CheckNames[CK_NumCheckKinds]; @@ -211,7 +220,7 @@ private: mutable std::unique_ptr<BugType> BT_OffsetFree[CK_NumCheckKinds]; mutable IdentifierInfo *II_malloc, *II_free, *II_realloc, *II_calloc, *II_valloc, *II_reallocf, *II_strndup, *II_strdup, - *II_kmalloc; + *II_kmalloc, *II_if_nameindex, *II_if_freenameindex; mutable Optional<uint64_t> KernelZeroFlagVal; void initIdentifierInfo(ASTContext &C) const; @@ -237,8 +246,10 @@ private: /// 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 isCMemFunction(const FunctionDecl *FD, + ASTContext &C, + AllocationFamily Family, + MemoryOperationKind MemKind) const; bool isStandardNewDelete(const FunctionDecl *FD, ASTContext &C) const; ///@} ProgramStateRef MallocMemReturnsAttr(CheckerContext &C, @@ -419,9 +430,9 @@ private: BugReporterContext &BRC, BugReport &BR) override; - PathDiagnosticPiece* getEndPath(BugReporterContext &BRC, - const ExplodedNode *EndPathNode, - BugReport &BR) override { + std::unique_ptr<PathDiagnosticPiece> + getEndPath(BugReporterContext &BRC, const ExplodedNode *EndPathNode, + BugReport &BR) override { if (!IsLeak) return nullptr; @@ -429,7 +440,8 @@ private: PathDiagnosticLocation::createEndOfPath(EndPathNode, BRC.getSourceManager()); // Do not add the statement itself as a range in case of leak. - return new PathDiagnosticEventPiece(L, BR.getDescription(), false); + return llvm::make_unique<PathDiagnosticEventPiece>(L, BR.getDescription(), + false); } private: @@ -494,13 +506,15 @@ void MallocChecker::initIdentifierInfo(ASTContext &Ctx) const { II_strdup = &Ctx.Idents.get("strdup"); II_strndup = &Ctx.Idents.get("strndup"); II_kmalloc = &Ctx.Idents.get("kmalloc"); + II_if_nameindex = &Ctx.Idents.get("if_nameindex"); + II_if_freenameindex = &Ctx.Idents.get("if_freenameindex"); } bool MallocChecker::isMemFunction(const FunctionDecl *FD, ASTContext &C) const { - if (isFreeFunction(FD, C)) + if (isCMemFunction(FD, C, AF_Malloc, MemoryOperationKind::MOK_Any)) return true; - if (isAllocationFunction(FD, C)) + if (isCMemFunction(FD, C, AF_IfNameIndex, MemoryOperationKind::MOK_Any)) return true; if (isStandardNewDelete(FD, C)) @@ -509,45 +523,61 @@ bool MallocChecker::isMemFunction(const FunctionDecl *FD, ASTContext &C) const { return false; } -bool MallocChecker::isAllocationFunction(const FunctionDecl *FD, - ASTContext &C) const { +bool MallocChecker::isCMemFunction(const FunctionDecl *FD, + ASTContext &C, + AllocationFamily Family, + MemoryOperationKind MemKind) const { if (!FD) return false; + bool CheckFree = (MemKind == MemoryOperationKind::MOK_Any || + MemKind == MemoryOperationKind::MOK_Free); + bool CheckAlloc = (MemKind == MemoryOperationKind::MOK_Any || + MemKind == MemoryOperationKind::MOK_Allocate); + if (FD->getKind() == Decl::Function) { - IdentifierInfo *FunI = FD->getIdentifier(); + const IdentifierInfo *FunI = FD->getIdentifier(); initIdentifierInfo(C); - if (FunI == II_malloc || FunI == II_realloc || - FunI == II_reallocf || FunI == II_calloc || FunI == II_valloc || - FunI == II_strdup || FunI == II_strndup || FunI == II_kmalloc) - return true; - } + if (Family == AF_Malloc && CheckFree) { + if (FunI == II_free || FunI == II_realloc || FunI == II_reallocf) + return true; + } - if (ChecksEnabled[CK_MallocOptimistic] && FD->hasAttrs()) - for (const auto *I : FD->specific_attrs<OwnershipAttr>()) - if (I->getOwnKind() == OwnershipAttr::Returns) + if (Family == AF_Malloc && CheckAlloc) { + if (FunI == II_malloc || FunI == II_realloc || FunI == II_reallocf || + FunI == II_calloc || FunI == II_valloc || FunI == II_strdup || + FunI == II_strndup || FunI == II_kmalloc) return true; - return false; -} + } -bool MallocChecker::isFreeFunction(const FunctionDecl *FD, ASTContext &C) const { - if (!FD) - return false; + if (Family == AF_IfNameIndex && CheckFree) { + if (FunI == II_if_freenameindex) + return true; + } - if (FD->getKind() == Decl::Function) { - IdentifierInfo *FunI = FD->getIdentifier(); - initIdentifierInfo(C); + if (Family == AF_IfNameIndex && CheckAlloc) { + if (FunI == II_if_nameindex) + return true; + } + } - if (FunI == II_free || FunI == II_realloc || FunI == II_reallocf) - return true; + if (Family != AF_Malloc) + return false; + + if (ChecksEnabled[CK_MallocOptimistic] && FD->hasAttrs()) { + for (const auto *I : FD->specific_attrs<OwnershipAttr>()) { + OwnershipAttr::OwnershipKind OwnKind = I->getOwnKind(); + if(OwnKind == OwnershipAttr::Takes || OwnKind == OwnershipAttr::Holds) { + if (CheckFree) + return true; + } else if (OwnKind == OwnershipAttr::Returns) { + if (CheckAlloc) + return true; + } + } } - if (ChecksEnabled[CK_MallocOptimistic] && FD->hasAttrs()) - for (const auto *I : FD->specific_attrs<OwnershipAttr>()) - if (I->getOwnKind() == OwnershipAttr::Takes || - I->getOwnKind() == OwnershipAttr::Holds) - return true; return false; } @@ -730,6 +760,13 @@ void MallocChecker::checkPostStmt(const CallExpr *CE, CheckerContext &C) const { State = FreeMemAux(C, CE, State, 0, false, ReleasedAllocatedMemory); else llvm_unreachable("not a new/delete operator"); + } else if (FunI == II_if_nameindex) { + // Should we model this differently? We can allocate a fixed number of + // elements with zeros in the last one. + State = MallocMemAux(C, CE, UnknownVal(), UnknownVal(), State, + AF_IfNameIndex); + } else if (FunI == II_if_freenameindex) { + State = FreeMemAux(C, CE, State, 0, false, ReleasedAllocatedMemory); } } @@ -753,6 +790,42 @@ void MallocChecker::checkPostStmt(const CallExpr *CE, CheckerContext &C) const { C.addTransition(State); } +static QualType getDeepPointeeType(QualType T) { + QualType Result = T, PointeeType = T->getPointeeType(); + while (!PointeeType.isNull()) { + Result = PointeeType; + PointeeType = PointeeType->getPointeeType(); + } + return Result; +} + +static bool treatUnusedNewEscaped(const CXXNewExpr *NE) { + + const CXXConstructExpr *ConstructE = NE->getConstructExpr(); + if (!ConstructE) + return false; + + if (!NE->getAllocatedType()->getAsCXXRecordDecl()) + return false; + + const CXXConstructorDecl *CtorD = ConstructE->getConstructor(); + + // Iterate over the constructor parameters. + for (const auto *CtorParam : CtorD->params()) { + + QualType CtorParamPointeeT = CtorParam->getType()->getPointeeType(); + if (CtorParamPointeeT.isNull()) + continue; + + CtorParamPointeeT = getDeepPointeeType(CtorParamPointeeT); + + if (CtorParamPointeeT->getAsCXXRecordDecl()) + return true; + } + + return false; +} + void MallocChecker::checkPostStmt(const CXXNewExpr *NE, CheckerContext &C) const { @@ -765,6 +838,10 @@ void MallocChecker::checkPostStmt(const CXXNewExpr *NE, if (!isStandardNewDelete(NE->getOperatorNew(), C.getASTContext())) return; + ParentMap &PM = C.getLocationContext()->getParentMap(); + if (!PM.isConsumedExpr(NE) && treatUnusedNewEscaped(NE)) + 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 @@ -859,6 +936,10 @@ ProgramStateRef MallocChecker::MallocMemAux(CheckerContext &C, ProgramStateRef State, AllocationFamily Family) { + // We expect the malloc functions to return a pointer. + if (!Loc::isLocType(CE->getType())) + return nullptr; + // 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 // side effects other than what we model here. @@ -869,10 +950,6 @@ ProgramStateRef MallocChecker::MallocMemAux(CheckerContext &C, .castAs<DefinedSVal>(); State = State->BindExpr(CE, C.getLocationContext(), RetVal); - // We expect the malloc functions to return a pointer. - if (!RetVal.getAs<Loc>()) - return nullptr; - // Fill the region with the initialization value. State = State->bindDefault(RetVal, Init); @@ -974,7 +1051,7 @@ AllocationFamily MallocChecker::getAllocationFamily(CheckerContext &C, ASTContext &Ctx = C.getASTContext(); - if (isAllocationFunction(FD, Ctx) || isFreeFunction(FD, Ctx)) + if (isCMemFunction(FD, Ctx, AF_Malloc, MemoryOperationKind::MOK_Any)) return AF_Malloc; if (isStandardNewDelete(FD, Ctx)) { @@ -985,6 +1062,9 @@ AllocationFamily MallocChecker::getAllocationFamily(CheckerContext &C, return AF_CXXNewArray; } + if (isCMemFunction(FD, Ctx, AF_IfNameIndex, MemoryOperationKind::MOK_Any)) + return AF_IfNameIndex; + return AF_None; } @@ -1048,6 +1128,7 @@ void MallocChecker::printExpectedAllocName(raw_ostream &os, CheckerContext &C, case AF_Malloc: os << "malloc()"; return; case AF_CXXNew: os << "'new'"; return; case AF_CXXNewArray: os << "'new[]'"; return; + case AF_IfNameIndex: os << "'if_nameindex()'"; return; case AF_None: llvm_unreachable("not a deallocation expression"); } } @@ -1058,6 +1139,7 @@ void MallocChecker::printExpectedDeallocName(raw_ostream &os, case AF_Malloc: os << "free()"; return; case AF_CXXNew: os << "'delete'"; return; case AF_CXXNewArray: os << "'delete[]'"; return; + case AF_IfNameIndex: os << "'if_freenameindex()'"; return; case AF_None: llvm_unreachable("suspicious AF_None argument"); } } @@ -1201,7 +1283,8 @@ ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C, Optional<MallocChecker::CheckKind> MallocChecker::getCheckIfTracked(AllocationFamily Family) const { switch (Family) { - case AF_Malloc: { + case AF_Malloc: + case AF_IfNameIndex: { if (ChecksEnabled[CK_MallocOptimistic]) { return CK_MallocOptimistic; } else if (ChecksEnabled[CK_MallocPessimistic]) { @@ -1425,7 +1508,7 @@ void MallocChecker::ReportMismatchedDealloc(CheckerContext &C, BugReport *R = new BugReport(*BT_MismatchedDealloc, os.str(), N); R->markInteresting(Sym); R->addRange(Range); - R->addVisitor(new MallocBugVisitor(Sym)); + R->addVisitor(llvm::make_unique<MallocBugVisitor>(Sym)); C.emitReport(R); } } @@ -1509,7 +1592,7 @@ void MallocChecker::ReportUseAfterFree(CheckerContext &C, SourceRange Range, R->markInteresting(Sym); R->addRange(Range); - R->addVisitor(new MallocBugVisitor(Sym)); + R->addVisitor(llvm::make_unique<MallocBugVisitor>(Sym)); C.emitReport(R); } } @@ -1541,7 +1624,7 @@ void MallocChecker::ReportDoubleFree(CheckerContext &C, SourceRange Range, R->markInteresting(Sym); if (PrevSym) R->markInteresting(PrevSym); - R->addVisitor(new MallocBugVisitor(Sym)); + R->addVisitor(llvm::make_unique<MallocBugVisitor>(Sym)); C.emitReport(R); } } @@ -1565,7 +1648,7 @@ void MallocChecker::ReportDoubleDelete(CheckerContext &C, SymbolRef Sym) const { "Attempt to delete released memory", N); R->markInteresting(Sym); - R->addVisitor(new MallocBugVisitor(Sym)); + R->addVisitor(llvm::make_unique<MallocBugVisitor>(Sym)); C.emitReport(R); } } @@ -1793,7 +1876,7 @@ void MallocChecker::reportLeak(SymbolRef Sym, ExplodedNode *N, new BugReport(*BT_Leak[*CheckKind], os.str(), N, LocUsedForUniqueing, AllocNode->getLocationContext()->getDecl()); R->markInteresting(Sym); - R->addVisitor(new MallocBugVisitor(Sym, true)); + R->addVisitor(llvm::make_unique<MallocBugVisitor>(Sym, true)); C.emitReport(R); } @@ -1865,13 +1948,16 @@ void MallocChecker::checkPreCall(const CallEvent &Call, if (!FD) return; + ASTContext &Ctx = C.getASTContext(); if ((ChecksEnabled[CK_MallocOptimistic] || ChecksEnabled[CK_MallocPessimistic]) && - isFreeFunction(FD, C.getASTContext())) + (isCMemFunction(FD, Ctx, AF_Malloc, MemoryOperationKind::MOK_Free) || + isCMemFunction(FD, Ctx, AF_IfNameIndex, + MemoryOperationKind::MOK_Free))) return; if (ChecksEnabled[CK_NewDeleteChecker] && - isStandardNewDelete(FD, C.getASTContext())) + isStandardNewDelete(FD, Ctx)) return; } diff --git a/lib/StaticAnalyzer/Checkers/MallocSizeofChecker.cpp b/lib/StaticAnalyzer/Checkers/MallocSizeofChecker.cpp index 4a50d9362874c..296aec66805a4 100644 --- a/lib/StaticAnalyzer/Checkers/MallocSizeofChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/MallocSizeofChecker.cpp @@ -137,6 +137,10 @@ public: // Determine if the pointee and sizeof types are compatible. Here // we ignore constness of pointer types. static bool typesCompatible(ASTContext &C, QualType A, QualType B) { + // sizeof(void*) is compatible with any other pointer. + if (B->isVoidPointerType() && A->getAs<PointerType>()) + return true; + while (true) { A = A.getCanonicalType(); B = B.getCanonicalType(); diff --git a/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp b/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp index 61d2b87cb0c0a..cb2d46b58310a 100644 --- a/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp @@ -49,14 +49,27 @@ void NonNullParamChecker::checkPreCall(const CallEvent &Call, if (!FD) return; - const NonNullAttr *Att = FD->getAttr<NonNullAttr>(); + // Merge all non-null attributes + unsigned NumArgs = Call.getNumArgs(); + llvm::SmallBitVector AttrNonNull(NumArgs); + for (const auto *NonNull : FD->specific_attrs<NonNullAttr>()) { + if (!NonNull->args_size()) { + AttrNonNull.set(0, NumArgs); + break; + } + for (unsigned Val : NonNull->args()) { + if (Val >= NumArgs) + continue; + AttrNonNull.set(Val); + } + } 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){ + for (unsigned idx = 0; idx < NumArgs; ++idx) { // Check if the parameter is a reference. We want to report when reference // to a null pointer is passed as a paramter. @@ -66,7 +79,7 @@ void NonNullParamChecker::checkPreCall(const CallEvent &Call, TyI++; } - bool haveAttrNonNull = Att && Att->isNonNull(idx); + bool haveAttrNonNull = AttrNonNull[idx]; if (!haveAttrNonNull) { // Check if the parameter is also marked 'nonnull'. ArrayRef<ParmVarDecl*> parms = Call.parameters(); diff --git a/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp b/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp index eb699d6940872..9a460ba50dddd 100644 --- a/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp @@ -1359,6 +1359,7 @@ RetainSummaryManager::getStandardMethodSummary(const ObjCMethodDecl *MD, // Check the method family, and apply any default annotations. switch (MD ? MD->getMethodFamily() : S.getMethodFamily()) { case OMF_None: + case OMF_initialize: case OMF_performSelector: // Assume all Objective-C methods follow Cocoa Memory Management rules. // FIXME: Does the non-threaded performSelector family really belong here? @@ -1579,19 +1580,19 @@ void RetainSummaryManager::InitializeMethodSummaries() { // Create summaries QCRenderer/QCView -createSnapShotImageOfType: addInstMethSummary("QCRenderer", AllocSumm, - "createSnapshotImageOfType", NULL); + "createSnapshotImageOfType", nullptr); addInstMethSummary("QCView", AllocSumm, - "createSnapshotImageOfType", NULL); + "createSnapshotImageOfType", nullptr); // Create summaries for CIContext, 'createCGImage' and // 'createCGLayerWithSize'. These objects are CF objects, and are not // automatically garbage collected. addInstMethSummary("CIContext", CFAllocSumm, - "createCGImage", "fromRect", NULL); - addInstMethSummary("CIContext", CFAllocSumm, - "createCGImage", "fromRect", "format", "colorSpace", NULL); - addInstMethSummary("CIContext", CFAllocSumm, "createCGLayerWithSize", - "info", NULL); + "createCGImage", "fromRect", nullptr); + addInstMethSummary("CIContext", CFAllocSumm, "createCGImage", "fromRect", + "format", "colorSpace", nullptr); + addInstMethSummary("CIContext", CFAllocSumm, "createCGLayerWithSize", "info", + nullptr); } //===----------------------------------------------------------------------===// @@ -1716,9 +1717,9 @@ namespace { BugReporterContext &BRC, BugReport &BR) override; - PathDiagnosticPiece *getEndPath(BugReporterContext &BRC, - const ExplodedNode *N, - BugReport &BR) override; + std::unique_ptr<PathDiagnosticPiece> getEndPath(BugReporterContext &BRC, + const ExplodedNode *N, + BugReport &BR) override; }; class CFRefLeakReportVisitor : public CFRefReportVisitor { @@ -1727,17 +1728,17 @@ namespace { const SummaryLogTy &log) : CFRefReportVisitor(sym, GCEnabled, log) {} - PathDiagnosticPiece *getEndPath(BugReporterContext &BRC, - const ExplodedNode *N, - BugReport &BR) override; + std::unique_ptr<PathDiagnosticPiece> getEndPath(BugReporterContext &BRC, + const ExplodedNode *N, + BugReport &BR) override; - BugReporterVisitor *clone() const override { + std::unique_ptr<BugReporterVisitor> clone() const override { // The curiously-recurring template pattern only works for one level of // subclassing. Rather than make a new template base for // CFRefReportVisitor, we simply override clone() to do the right thing. // This could be trouble someday if BugReporterVisitorImpl is ever // used for something else besides a convenient implementation of clone(). - return new CFRefLeakReportVisitor(*this); + return llvm::make_unique<CFRefLeakReportVisitor>(*this); } }; @@ -1750,7 +1751,7 @@ namespace { bool registerVisitor = true) : BugReport(D, D.getDescription(), n) { if (registerVisitor) - addVisitor(new CFRefReportVisitor(sym, GCEnabled, Log)); + addVisitor(llvm::make_unique<CFRefReportVisitor>(sym, GCEnabled, Log)); addGCModeDescription(LOpts, GCEnabled); } @@ -1758,7 +1759,7 @@ namespace { const SummaryLogTy &Log, ExplodedNode *n, SymbolRef sym, StringRef endText) : BugReport(D, D.getDescription(), endText, n) { - addVisitor(new CFRefReportVisitor(sym, GCEnabled, Log)); + addVisitor(llvm::make_unique<CFRefReportVisitor>(sym, GCEnabled, Log)); addGCModeDescription(LOpts, GCEnabled); } @@ -2218,18 +2219,16 @@ GetAllocationSite(ProgramStateManager& StateMgr, const ExplodedNode *N, InterestingMethodContext); } -PathDiagnosticPiece* +std::unique_ptr<PathDiagnosticPiece> CFRefReportVisitor::getEndPath(BugReporterContext &BRC, - const ExplodedNode *EndN, - BugReport &BR) { + const ExplodedNode *EndN, BugReport &BR) { BR.markInteresting(Sym); return BugReporterVisitor::getDefaultEndPath(BRC, EndN, BR); } -PathDiagnosticPiece* +std::unique_ptr<PathDiagnosticPiece> CFRefLeakReportVisitor::getEndPath(BugReporterContext &BRC, - const ExplodedNode *EndN, - BugReport &BR) { + const ExplodedNode *EndN, BugReport &BR) { // Tell the BugReporterContext to report cases when the tracked symbol is // assigned to different variables, etc. @@ -2309,7 +2308,7 @@ CFRefLeakReportVisitor::getEndPath(BugReporterContext &BRC, os << " is not referenced later in this execution path and has a retain " "count of +" << RV->getCount(); - return new PathDiagnosticEventPiece(L, os.str()); + return llvm::make_unique<PathDiagnosticEventPiece>(L, os.str()); } CFRefLeakReport::CFRefLeakReport(CFRefBug &D, const LangOptions &LOpts, @@ -2388,7 +2387,7 @@ CFRefLeakReport::CFRefLeakReport(CFRefBug &D, const LangOptions &LOpts, } } - addVisitor(new CFRefLeakReportVisitor(sym, GCEnabled, Log)); + addVisitor(llvm::make_unique<CFRefLeakReportVisitor>(sym, GCEnabled, Log)); } //===----------------------------------------------------------------------===// diff --git a/lib/StaticAnalyzer/Checkers/SelectorExtras.h b/lib/StaticAnalyzer/Checkers/SelectorExtras.h index 48d96eb422346..41f70d7d5b692 100644 --- a/lib/StaticAnalyzer/Checkers/SelectorExtras.h +++ b/lib/StaticAnalyzer/Checkers/SelectorExtras.h @@ -7,8 +7,8 @@ // //===----------------------------------------------------------------------===// -#ifndef LLVM_CLANG_SA_CHECKERS_SELECTOREXTRAS -#define LLVM_CLANG_SA_CHECKERS_SELECTOREXTRAS +#ifndef LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_SELECTOREXTRAS_H +#define LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_SELECTOREXTRAS_H #include "clang/AST/ASTContext.h" #include <cstdarg> @@ -34,7 +34,7 @@ static inline Selector getKeywordSelector(ASTContext &Ctx, va_list argp) { return getKeywordSelectorImpl(Ctx, First, argp); } -END_WITH_NULL +LLVM_END_WITH_NULL static inline Selector getKeywordSelector(ASTContext &Ctx, const char *First, ...) { va_list argp; @@ -44,7 +44,7 @@ static inline Selector getKeywordSelector(ASTContext &Ctx, return result; } -END_WITH_NULL +LLVM_END_WITH_NULL static inline void lazyInitKeywordSelector(Selector &Sel, ASTContext &Ctx, const char *First, ...) { if (!Sel.isNull()) diff --git a/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp b/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp index 3e9b57bdc5074..ccf816c80c1e4 100644 --- a/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp @@ -63,8 +63,7 @@ class SimpleStreamChecker : public Checker<check::PostCall, const CallEvent &Call, CheckerContext &C) const; - void reportLeaks(SymbolVector LeakedStreams, - CheckerContext &C, + void reportLeaks(ArrayRef<SymbolRef> LeakedStreams, CheckerContext &C, ExplodedNode *ErrNode) const; bool guaranteedNotToCloseFile(const CallEvent &Call) const; @@ -222,16 +221,15 @@ void SimpleStreamChecker::reportDoubleClose(SymbolRef FileDescSym, C.emitReport(R); } -void SimpleStreamChecker::reportLeaks(SymbolVector LeakedStreams, - CheckerContext &C, - ExplodedNode *ErrNode) const { +void SimpleStreamChecker::reportLeaks(ArrayRef<SymbolRef> LeakedStreams, + CheckerContext &C, + ExplodedNode *ErrNode) const { // Attach bug reports to the leak node. // TODO: Identify the leaked file descriptor. - for (SmallVectorImpl<SymbolRef>::iterator - I = LeakedStreams.begin(), E = LeakedStreams.end(); I != E; ++I) { + for (SymbolRef LeakedStream : LeakedStreams) { BugReport *R = new BugReport(*LeakBugType, "Opened file is never closed; potential resource leak", ErrNode); - R->markInteresting(*I); + R->markInteresting(LeakedStream); C.emitReport(R); } } diff --git a/lib/StaticAnalyzer/Checkers/TestAfterDivZeroChecker.cpp b/lib/StaticAnalyzer/Checkers/TestAfterDivZeroChecker.cpp index dad5c0d3bab89..083075db8fb9e 100644 --- a/lib/StaticAnalyzer/Checkers/TestAfterDivZeroChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/TestAfterDivZeroChecker.cpp @@ -176,7 +176,8 @@ void TestAfterDivZeroChecker::reportBug(SVal Val, CheckerContext &C) const { "already been used for division", N); - R->addVisitor(new DivisionBRVisitor(Val.getAsSymbol(), C.getStackFrame())); + R->addVisitor(llvm::make_unique<DivisionBRVisitor>(Val.getAsSymbol(), + C.getStackFrame())); C.emitReport(R); } } diff --git a/lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp b/lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp index 93687db073f95..8976e0a699ec9 100644 --- a/lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp @@ -92,8 +92,8 @@ 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(*V, VR, - /*EnableNullFPSuppression*/false)); + R->addVisitor(llvm::make_unique<FindLastStoreBRVisitor>( + *V, VR, /*EnableNullFPSuppression*/ false)); R->disablePathPruning(); // need location of block C.emitReport(R); diff --git a/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp b/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp index 4887d804c6072..4bfed8504f985 100644 --- a/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp @@ -62,6 +62,10 @@ private: return; BT.reset(new BugType(this, name, categories::UnixAPI)); } + void ReportOpenBug(CheckerContext &C, + ProgramStateRef State, + const char *Msg, + SourceRange SR) const; }; } //end anonymous namespace @@ -69,7 +73,44 @@ private: // "open" (man 2 open) //===----------------------------------------------------------------------===// +void UnixAPIChecker::ReportOpenBug(CheckerContext &C, + ProgramStateRef State, + const char *Msg, + SourceRange SR) const { + ExplodedNode *N = C.generateSink(State); + if (!N) + return; + + LazyInitialize(BT_open, "Improper use of 'open'"); + + BugReport *Report = new BugReport(*BT_open, Msg, N); + Report->addRange(SR); + C.emitReport(Report); +} + void UnixAPIChecker::CheckOpen(CheckerContext &C, const CallExpr *CE) const { + ProgramStateRef state = C.getState(); + + if (CE->getNumArgs() < 2) { + // The frontend should issue a warning for this case, so this is a sanity + // check. + return; + } else if (CE->getNumArgs() == 3) { + const Expr *Arg = CE->getArg(2); + QualType QT = Arg->getType(); + if (!QT->isIntegerType()) { + ReportOpenBug(C, state, + "Third argument to 'open' is not an integer", + Arg->getSourceRange()); + return; + } + } else if (CE->getNumArgs() > 3) { + ReportOpenBug(C, state, + "Call to 'open' with more than three arguments", + CE->getArg(3)->getSourceRange()); + return; + } + // The definition of O_CREAT is platform specific. We need a better way // of querying this information from the checking environment. if (!Val_O_CREAT.hasValue()) { @@ -85,15 +126,6 @@ void UnixAPIChecker::CheckOpen(CheckerContext &C, const CallExpr *CE) const { } } - // Look at the 'oflags' argument for the O_CREAT flag. - ProgramStateRef state = C.getState(); - - if (CE->getNumArgs() < 2) { - // The frontend should issue a warning for this case, so this is a sanity - // check. - return; - } - // Now check if oflags has O_CREAT set. const Expr *oflagsEx = CE->getArg(1); const SVal V = state->getSVal(oflagsEx, C.getLocationContext()); @@ -122,18 +154,10 @@ void UnixAPIChecker::CheckOpen(CheckerContext &C, const CallExpr *CE) const { return; if (CE->getNumArgs() < 3) { - ExplodedNode *N = C.generateSink(trueState); - if (!N) - return; - - LazyInitialize(BT_open, "Improper use of 'open'"); - - BugReport *report = - new BugReport(*BT_open, - "Call to 'open' requires a third argument when " - "the 'O_CREAT' flag is set", N); - report->addRange(oflagsEx->getSourceRange()); - C.emitReport(report); + ReportOpenBug(C, trueState, + "Call to 'open' requires a third argument when " + "the 'O_CREAT' flag is set", + oflagsEx->getSourceRange()); } } diff --git a/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp b/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp index 198a6285c908c..cceffef82b36e 100644 --- a/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp @@ -30,7 +30,7 @@ using namespace ento; namespace { class VLASizeChecker : public Checker< check::PreStmt<DeclStmt> > { mutable std::unique_ptr<BugType> BT; - enum VLASize_Kind { VLA_Garbage, VLA_Zero, VLA_Tainted }; + enum VLASize_Kind { VLA_Garbage, VLA_Zero, VLA_Tainted, VLA_Negative }; void reportBug(VLASize_Kind Kind, const Expr *SizeE, @@ -67,6 +67,9 @@ void VLASizeChecker::reportBug(VLASize_Kind Kind, case VLA_Tainted: os << "has tainted size"; break; + case VLA_Negative: + os << "has negative size"; + break; } BugReport *report = new BugReport(*BT, os.str(), N); @@ -128,8 +131,27 @@ void VLASizeChecker::checkPreStmt(const DeclStmt *DS, CheckerContext &C) const { // declared. We do this by multiplying the array length by the element size, // then matching that with the array region's extent symbol. - // Convert the array length to size_t. + // Check if the size is negative. SValBuilder &svalBuilder = C.getSValBuilder(); + + QualType Ty = SE->getType(); + DefinedOrUnknownSVal Zero = svalBuilder.makeZeroVal(Ty); + + SVal LessThanZeroVal = svalBuilder.evalBinOp(state, BO_LT, sizeD, Zero, Ty); + if (Optional<DefinedSVal> LessThanZeroDVal = + LessThanZeroVal.getAs<DefinedSVal>()) { + ConstraintManager &CM = C.getConstraintManager(); + ProgramStateRef StatePos, StateNeg; + + std::tie(StateNeg, StatePos) = CM.assumeDual(state, *LessThanZeroDVal); + if (StateNeg && !StatePos) { + reportBug(VLA_Negative, SE, state, C); + return; + } + state = StatePos; + } + + // Convert the array length to size_t. QualType SizeTy = Ctx.getSizeType(); NonLoc ArrayLength = svalBuilder.evalCast(sizeD, SizeTy, SE->getType()).castAs<NonLoc>(); diff --git a/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp b/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp index f8f5cf93ca894..7e1fc1eb54adf 100644 --- a/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp @@ -146,15 +146,22 @@ void WalkAST::VisitCXXMemberCallExpr(CallExpr *CE) { if (CME->getQualifier()) callIsNonVirtual = true; - // Elide analyzing the call entirely if the base pointer is not 'this'. - if (Expr *base = CME->getBase()->IgnoreImpCasts()) + if (Expr *base = CME->getBase()->IgnoreImpCasts()) { + // Elide analyzing the call entirely if the base pointer is not 'this'. if (!isa<CXXThisExpr>(base)) return; + + // If the most derived class is marked final, we know that now subclass + // can override this member. + if (base->getBestDynamicClassType()->hasAttr<FinalAttr>()) + callIsNonVirtual = true; + } } // Get the callee. const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(CE->getDirectCallee()); - if (MD && MD->isVirtual() && !callIsNonVirtual) + if (MD && MD->isVirtual() && !callIsNonVirtual && !MD->hasAttr<FinalAttr>() && + !MD->getParent()->hasAttr<FinalAttr>()) ReportVirtualCall(CE, MD->isPure()); Enqueue(CE); |