diff options
Diffstat (limited to 'lib/StaticAnalyzer')
46 files changed, 894 insertions, 453 deletions
diff --git a/lib/StaticAnalyzer/Checkers/AllocationDiagnostics.h b/lib/StaticAnalyzer/Checkers/AllocationDiagnostics.h index 2b314a3..048418e 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 20360ef..e462e2b 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 a871049..104a81e 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 0693bd6f..e91a7e1 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 d186144..339af8f 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 44eb641..ba5b4fa 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 de2ebce..05b4a61 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 d5c52b4..58d0783 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 e35557f..b7549fd 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 0f227bb..13ea4d3 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 a03fa25..aee5a43 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 4a50d93..296aec6 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 61d2b87..cb2d46b 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 eb699d6..9a460ba 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 48d96eb..41f70d7 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 3e9b57b..ccf816c 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 dad5c0d..083075d 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 93687db..8976e0a 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 4887d80..4bfed85 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 198a628..cceffef 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 f8f5cf9..7e1fc1e 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); diff --git a/lib/StaticAnalyzer/Core/AnalysisManager.cpp b/lib/StaticAnalyzer/Core/AnalysisManager.cpp index 747b73c..5798f01 100644 --- a/lib/StaticAnalyzer/Core/AnalysisManager.cpp +++ b/lib/StaticAnalyzer/Core/AnalysisManager.cpp @@ -20,13 +20,16 @@ AnalysisManager::AnalysisManager(ASTContext &ctx, DiagnosticsEngine &diags, StoreManagerCreator storemgr, ConstraintManagerCreator constraintmgr, CheckerManager *checkerMgr, - AnalyzerOptions &Options) + AnalyzerOptions &Options, + CodeInjector *injector) : AnaCtxMgr(Options.UnoptimizedCFG, /*AddImplicitDtors=*/true, /*AddInitializers=*/true, Options.includeTemporaryDtorsInCFG(), Options.shouldSynthesizeBodies(), - Options.shouldConditionalizeStaticInitializers()), + Options.shouldConditionalizeStaticInitializers(), + /*addCXXNewAllocator=*/true, + injector), Ctx(ctx), Diags(diags), LangOpts(lang), diff --git a/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp b/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp index 7944c7e..d717e3f 100644 --- a/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp +++ b/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp @@ -23,7 +23,8 @@ using namespace llvm; AnalyzerOptions::UserModeKind AnalyzerOptions::getUserMode() { if (UserMode == UMK_NotSet) { - StringRef ModeStr(Config.GetOrCreateValue("mode", "deep").getValue()); + StringRef ModeStr = + Config.insert(std::make_pair("mode", "deep")).first->second; UserMode = llvm::StringSwitch<UserModeKind>(ModeStr) .Case("shallow", UMK_Shallow) .Case("deep", UMK_Deep) @@ -48,7 +49,8 @@ IPAKind AnalyzerOptions::getIPAMode() { assert(DefaultIPA); // Lookup the ipa configuration option, use the default from User Mode. - StringRef ModeStr(Config.GetOrCreateValue("ipa", DefaultIPA).getValue()); + StringRef ModeStr = + Config.insert(std::make_pair("ipa", DefaultIPA)).first->second; IPAKind IPAConfig = llvm::StringSwitch<IPAKind>(ModeStr) .Case("none", IPAK_None) .Case("basic-inlining", IPAK_BasicInlining) @@ -72,9 +74,9 @@ AnalyzerOptions::mayInlineCXXMemberFunction(CXXInlineableMemberKind K) { if (!CXXMemberInliningMode) { static const char *ModeKey = "c++-inlining"; - - StringRef ModeStr(Config.GetOrCreateValue(ModeKey, - "destructors").getValue()); + + StringRef ModeStr = + Config.insert(std::make_pair(ModeKey, "destructors")).first->second; CXXInlineableMemberKind &MutableMode = const_cast<CXXInlineableMemberKind &>(CXXMemberInliningMode); @@ -102,7 +104,8 @@ bool AnalyzerOptions::getBooleanOption(StringRef Name, bool DefaultVal) { // FIXME: We should emit a warning here if the value is something other than // "true", "false", or the empty string (meaning the default value), // but the AnalyzerOptions doesn't have access to a diagnostic engine. - StringRef V(Config.GetOrCreateValue(Name, toString(DefaultVal)).getValue()); + StringRef V = + Config.insert(std::make_pair(Name, toString(DefaultVal))).first->second; return llvm::StringSwitch<bool>(V) .Case("true", true) .Case("false", false) @@ -200,8 +203,8 @@ int AnalyzerOptions::getOptionAsInteger(StringRef Name, int DefaultVal) { SmallString<10> StrBuf; llvm::raw_svector_ostream OS(StrBuf); OS << DefaultVal; - - StringRef V(Config.GetOrCreateValue(Name, OS.str()).getValue()); + + StringRef V = Config.insert(std::make_pair(Name, OS.str())).first->second; int Res = DefaultVal; bool b = V.getAsInteger(10, Res); assert(!b && "analyzer-config option should be numeric"); diff --git a/lib/StaticAnalyzer/Core/BugReporter.cpp b/lib/StaticAnalyzer/Core/BugReporter.cpp index 141a48b..dff81e3 100644 --- a/lib/StaticAnalyzer/Core/BugReporter.cpp +++ b/lib/StaticAnalyzer/Core/BugReporter.cpp @@ -499,10 +499,9 @@ PathDiagnosticBuilder::getEnclosingStmtLocation(const Stmt *S) { //===----------------------------------------------------------------------===// // "Visitors only" path diagnostic generation algorithm. //===----------------------------------------------------------------------===// -static bool GenerateVisitorsOnlyPathDiagnostic(PathDiagnostic &PD, - PathDiagnosticBuilder &PDB, - const ExplodedNode *N, - ArrayRef<BugReporterVisitor *> visitors) { +static bool GenerateVisitorsOnlyPathDiagnostic( + PathDiagnostic &PD, PathDiagnosticBuilder &PDB, const ExplodedNode *N, + ArrayRef<std::unique_ptr<BugReporterVisitor>> visitors) { // All path generation skips the very first node (the error node). // This is because there is special handling for the end-of-path note. N = N->getFirstPred(); @@ -511,11 +510,9 @@ static bool GenerateVisitorsOnlyPathDiagnostic(PathDiagnostic &PD, BugReport *R = PDB.getBugReport(); while (const ExplodedNode *Pred = N->getFirstPred()) { - for (ArrayRef<BugReporterVisitor *>::iterator I = visitors.begin(), - E = visitors.end(); - I != E; ++I) { + for (auto &V : visitors) { // Visit all the node pairs, but throw the path pieces away. - PathDiagnosticPiece *Piece = (*I)->VisitNode(N, Pred, PDB, *R); + PathDiagnosticPiece *Piece = V->VisitNode(N, Pred, PDB, *R); delete Piece; } @@ -556,11 +553,10 @@ static void updateStackPiecesWithMessage(PathDiagnosticPiece *P, static void CompactPathDiagnostic(PathPieces &path, const SourceManager& SM); -static bool GenerateMinimalPathDiagnostic(PathDiagnostic& PD, - PathDiagnosticBuilder &PDB, - const ExplodedNode *N, - LocationContextMap &LCM, - ArrayRef<BugReporterVisitor *> visitors) { +static bool GenerateMinimalPathDiagnostic( + PathDiagnostic &PD, PathDiagnosticBuilder &PDB, const ExplodedNode *N, + LocationContextMap &LCM, + ArrayRef<std::unique_ptr<BugReporterVisitor>> visitors) { SourceManager& SMgr = PDB.getSourceManager(); const LocationContext *LC = PDB.LC; @@ -870,10 +866,8 @@ static bool GenerateMinimalPathDiagnostic(PathDiagnostic& PD, if (NextNode) { // Add diagnostic pieces from custom visitors. BugReport *R = PDB.getBugReport(); - for (ArrayRef<BugReporterVisitor *>::iterator I = visitors.begin(), - E = visitors.end(); - I != E; ++I) { - if (PathDiagnosticPiece *p = (*I)->VisitNode(N, NextNode, PDB, *R)) { + for (auto &V : visitors) { + if (PathDiagnosticPiece *p = V->VisitNode(N, NextNode, PDB, *R)) { PD.getActivePath().push_front(p); updateStackPiecesWithMessage(p, CallStack); } @@ -1392,11 +1386,10 @@ static bool isInLoopBody(ParentMap &PM, const Stmt *S, const Stmt *Term) { // Top-level logic for generating extensive path diagnostics. //===----------------------------------------------------------------------===// -static bool GenerateExtensivePathDiagnostic(PathDiagnostic& PD, - PathDiagnosticBuilder &PDB, - const ExplodedNode *N, - LocationContextMap &LCM, - ArrayRef<BugReporterVisitor *> visitors) { +static bool GenerateExtensivePathDiagnostic( + PathDiagnostic &PD, PathDiagnosticBuilder &PDB, const ExplodedNode *N, + LocationContextMap &LCM, + ArrayRef<std::unique_ptr<BugReporterVisitor>> visitors) { EdgeBuilder EB(PD, PDB); const SourceManager& SM = PDB.getSourceManager(); StackDiagVector CallStack; @@ -1573,10 +1566,8 @@ static bool GenerateExtensivePathDiagnostic(PathDiagnostic& PD, // Add pieces from custom visitors. BugReport *R = PDB.getBugReport(); - for (ArrayRef<BugReporterVisitor *>::iterator I = visitors.begin(), - E = visitors.end(); - I != E; ++I) { - if (PathDiagnosticPiece *p = (*I)->VisitNode(N, NextNode, PDB, *R)) { + for (auto &V : visitors) { + if (PathDiagnosticPiece *p = V->VisitNode(N, NextNode, PDB, *R)) { const PathDiagnosticLocation &Loc = p->getLocation(); EB.addEdge(Loc, true); PD.getActivePath().push_front(p); @@ -1635,12 +1626,10 @@ static const char StrLoopRangeEmpty[] = static const char StrLoopCollectionEmpty[] = "Loop body skipped when collection is empty"; -static bool -GenerateAlternateExtensivePathDiagnostic(PathDiagnostic& PD, - PathDiagnosticBuilder &PDB, - const ExplodedNode *N, - LocationContextMap &LCM, - ArrayRef<BugReporterVisitor *> visitors) { +static bool GenerateAlternateExtensivePathDiagnostic( + PathDiagnostic &PD, PathDiagnosticBuilder &PDB, const ExplodedNode *N, + LocationContextMap &LCM, + ArrayRef<std::unique_ptr<BugReporterVisitor>> visitors) { BugReport *report = PDB.getBugReport(); const SourceManager& SM = PDB.getSourceManager(); @@ -1867,10 +1856,8 @@ GenerateAlternateExtensivePathDiagnostic(PathDiagnostic& PD, continue; // Add pieces from custom visitors. - for (ArrayRef<BugReporterVisitor *>::iterator I = visitors.begin(), - E = visitors.end(); - I != E; ++I) { - if (PathDiagnosticPiece *p = (*I)->VisitNode(N, NextNode, PDB, *report)) { + for (auto &V : visitors) { + if (PathDiagnosticPiece *p = V->VisitNode(N, NextNode, PDB, *report)) { addEdgeToPath(PD.getActivePath(), PrevLoc, p->getLocation(), PDB.LC); PD.getActivePath().push_front(p); updateStackPiecesWithMessage(p, CallStack); @@ -2547,7 +2534,7 @@ void BuiltinBug::anchor() {} void BugReport::NodeResolver::anchor() {} -void BugReport::addVisitor(BugReporterVisitor* visitor) { +void BugReport::addVisitor(std::unique_ptr<BugReporterVisitor> visitor) { if (!visitor) return; @@ -2555,20 +2542,15 @@ void BugReport::addVisitor(BugReporterVisitor* visitor) { visitor->Profile(ID); void *InsertPos; - if (CallbacksSet.FindNodeOrInsertPos(ID, InsertPos)) { - delete visitor; + if (CallbacksSet.FindNodeOrInsertPos(ID, InsertPos)) return; - } - CallbacksSet.InsertNode(visitor, InsertPos); - Callbacks.push_back(visitor); + CallbacksSet.InsertNode(visitor.get(), InsertPos); + Callbacks.push_back(std::move(visitor)); ++ConfigurationChangeToken; } BugReport::~BugReport() { - for (visitor_iterator I = visitor_begin(), E = visitor_end(); I != E; ++I) { - delete *I; - } while (!interestingSymbols.empty()) { popInterestingSymbolsAndRegions(); } @@ -2874,7 +2856,7 @@ TrimmedGraph::TrimmedGraph(const ExplodedGraph *OriginalGraph, // The trimmed graph is created in the body of the constructor to ensure // that the DenseMaps have been initialized already. InterExplodedGraphMap ForwardMap; - G.reset(OriginalGraph->trim(Nodes, &ForwardMap, &InverseMap)); + G = OriginalGraph->trim(Nodes, &ForwardMap, &InverseMap); // Find the (first) error node in the trimmed graph. We just need to consult // the node map which maps from nodes in the original graph to nodes @@ -2938,8 +2920,7 @@ bool TrimmedGraph::popNextReportGraph(ReportGraph &GraphWrapper) { // Create a new graph with a single path. This is the graph // that will be returned to the caller. - ExplodedGraph *GNew = new ExplodedGraph(); - GraphWrapper.Graph.reset(GNew); + auto GNew = llvm::make_unique<ExplodedGraph>(); GraphWrapper.BackMap.clear(); // Now walk from the error node up the BFS path, always taking the @@ -2976,6 +2957,8 @@ bool TrimmedGraph::popNextReportGraph(ReportGraph &GraphWrapper) { PriorityCompare<false>(PriorityMap)); } + GraphWrapper.Graph = std::move(GNew); + return true; } @@ -3126,9 +3109,9 @@ bool GRBugReporter::generatePathDiagnostic(PathDiagnostic& PD, const ExplodedNode *N = ErrorGraph.ErrorNode; // Register additional node visitors. - R->addVisitor(new NilReceiverBRVisitor()); - R->addVisitor(new ConditionBRVisitor()); - R->addVisitor(new LikelyFalsePositiveSuppressionBRVisitor()); + R->addVisitor(llvm::make_unique<NilReceiverBRVisitor>()); + R->addVisitor(llvm::make_unique<ConditionBRVisitor>()); + R->addVisitor(llvm::make_unique<LikelyFalsePositiveSuppressionBRVisitor>()); BugReport::VisitorList visitors; unsigned origReportConfigToken, finalReportConfigToken; @@ -3153,18 +3136,19 @@ bool GRBugReporter::generatePathDiagnostic(PathDiagnostic& PD, std::unique_ptr<PathDiagnosticPiece> LastPiece; for (BugReport::visitor_iterator I = visitors.begin(), E = visitors.end(); I != E; ++I) { - if (PathDiagnosticPiece *Piece = (*I)->getEndPath(PDB, N, *R)) { + if (std::unique_ptr<PathDiagnosticPiece> Piece = + (*I)->getEndPath(PDB, N, *R)) { assert (!LastPiece && "There can only be one final piece in a diagnostic."); - LastPiece.reset(Piece); + LastPiece = std::move(Piece); } } if (ActiveScheme != PathDiagnosticConsumer::None) { if (!LastPiece) - LastPiece.reset(BugReporterVisitor::getDefaultEndPath(PDB, N, *R)); + LastPiece = BugReporterVisitor::getDefaultEndPath(PDB, N, *R); assert(LastPiece); - PD.setEndOfPath(LastPiece.release()); + PD.setEndOfPath(std::move(LastPiece)); } // Make sure we get a clean location context map so we don't @@ -3187,7 +3171,7 @@ bool GRBugReporter::generatePathDiagnostic(PathDiagnostic& PD, } // Clean up the visitors we used. - llvm::DeleteContainerPointers(visitors); + visitors.clear(); // Did anything change while generating this path? finalReportConfigToken = R->getConfigurationChangeToken(); @@ -3247,15 +3231,15 @@ void BugReporter::emitReport(BugReport* R) { // To guarantee memory release. std::unique_ptr<BugReport> UniqueR(R); - // Defensive checking: throw the bug away if it comes from a BodyFarm- - // generated body. We do this very early because report processing relies - // on the report's location being valid. - // FIXME: Valid bugs can occur in BodyFarm-generated bodies, so really we - // need to just find a reasonable location like we do later on with the path - // pieces. if (const ExplodedNode *E = R->getErrorNode()) { - const LocationContext *LCtx = E->getLocationContext(); - if (LCtx->getAnalysisDeclContext()->isBodyAutosynthesized()) + const AnalysisDeclContext *DeclCtx = + E->getLocationContext()->getAnalysisDeclContext(); + // The source of autosynthesized body can be handcrafted AST or a model + // file. The locations from handcrafted ASTs have no valid source locations + // and have to be discarded. Locations from model files should be preserved + // for processing and reporting. + if (DeclCtx->isBodyAutosynthesized() && + !DeclCtx->isBodyAutosynthesizedFromModelFile()) return; } @@ -3277,12 +3261,11 @@ void BugReporter::emitReport(BugReport* R) { BugReportEquivClass* EQ = EQClasses.FindNodeOrInsertPos(ID, InsertPos); if (!EQ) { - EQ = new BugReportEquivClass(UniqueR.release()); + EQ = new BugReportEquivClass(std::move(UniqueR)); EQClasses.InsertNode(EQ, InsertPos); EQClassesVector.push_back(EQ); - } - else - EQ->AddReport(UniqueR.release()); + } else + EQ->AddReport(std::move(UniqueR)); } @@ -3449,13 +3432,13 @@ void BugReporter::FlushReport(BugReport *exampleReport, // of the issue. if (D->path.empty()) { PathDiagnosticLocation L = exampleReport->getLocation(getSourceManager()); - PathDiagnosticPiece *piece = - new PathDiagnosticEventPiece(L, exampleReport->getDescription()); + auto piece = llvm::make_unique<PathDiagnosticEventPiece>( + L, exampleReport->getDescription()); BugReport::ranges_iterator Beg, End; std::tie(Beg, End) = exampleReport->getRanges(); for ( ; Beg != End; ++Beg) piece->addRange(*Beg); - D->setEndOfPath(piece); + D->setEndOfPath(std::move(piece)); } // Get the meta data. @@ -3465,7 +3448,7 @@ void BugReporter::FlushReport(BugReport *exampleReport, D->addMeta(*i); } - PD.HandlePathDiagnostic(D.release()); + PD.HandlePathDiagnostic(std::move(D)); } void BugReporter::EmitBasicReport(const Decl *DeclWithIssue, @@ -3497,13 +3480,9 @@ BugType *BugReporter::getBugTypeForName(CheckName CheckName, StringRef name, SmallString<136> fullDesc; llvm::raw_svector_ostream(fullDesc) << CheckName.getName() << ":" << name << ":" << category; - llvm::StringMapEntry<BugType *> & - entry = StrBugTypes.GetOrCreateValue(fullDesc); - BugType *BT = entry.getValue(); - if (!BT) { + BugType *&BT = StrBugTypes[fullDesc]; + if (!BT) BT = new BugType(CheckName, name, category); - entry.setValue(BT); - } return BT; } diff --git a/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp b/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp index 0503ace..2d56bd0 100644 --- a/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp +++ b/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp @@ -100,17 +100,14 @@ const Stmt *bugreporter::GetRetValExpr(const ExplodedNode *N) { // Definitions for bug reporter visitors. //===----------------------------------------------------------------------===// -PathDiagnosticPiece* +std::unique_ptr<PathDiagnosticPiece> BugReporterVisitor::getEndPath(BugReporterContext &BRC, - const ExplodedNode *EndPathNode, - BugReport &BR) { + const ExplodedNode *EndPathNode, BugReport &BR) { return nullptr; } -PathDiagnosticPiece* -BugReporterVisitor::getDefaultEndPath(BugReporterContext &BRC, - const ExplodedNode *EndPathNode, - BugReport &BR) { +std::unique_ptr<PathDiagnosticPiece> BugReporterVisitor::getDefaultEndPath( + BugReporterContext &BRC, const ExplodedNode *EndPathNode, BugReport &BR) { PathDiagnosticLocation L = PathDiagnosticLocation::createEndOfPath(EndPathNode,BRC.getSourceManager()); @@ -119,13 +116,12 @@ BugReporterVisitor::getDefaultEndPath(BugReporterContext &BRC, // Only add the statement itself as a range if we didn't specify any // special ranges for this report. - PathDiagnosticPiece *P = new PathDiagnosticEventPiece(L, - BR.getDescription(), - Beg == End); + auto P = llvm::make_unique<PathDiagnosticEventPiece>(L, BR.getDescription(), + Beg == End); for (; Beg != End; ++Beg) P->addRange(*Beg); - return P; + return std::move(P); } @@ -222,7 +218,8 @@ public: EnableNullFPSuppression = State->isNull(*RetLoc).isConstrainedTrue(); BR.markInteresting(CalleeContext); - BR.addVisitor(new ReturnVisitor(CalleeContext, EnableNullFPSuppression)); + BR.addVisitor(llvm::make_unique<ReturnVisitor>(CalleeContext, + EnableNullFPSuppression)); } /// Returns true if any counter-suppression heuristics are enabled for @@ -399,9 +396,9 @@ public: llvm_unreachable("Invalid visit mode!"); } - PathDiagnosticPiece *getEndPath(BugReporterContext &BRC, - const ExplodedNode *N, - BugReport &BR) override { + std::unique_ptr<PathDiagnosticPiece> getEndPath(BugReporterContext &BRC, + const ExplodedNode *N, + BugReport &BR) override { if (EnableNullFPSuppression) BR.markInvalid(ReturnVisitor::getTag(), StackFrame); return nullptr; @@ -569,8 +566,8 @@ PathDiagnosticPiece *FindLastStoreBRVisitor::VisitNode(const ExplodedNode *Succ, if (const VarRegion *OriginalR = BDR->getOriginalRegion(VR)) { if (Optional<KnownSVal> KV = State->getSVal(OriginalR).getAs<KnownSVal>()) - BR.addVisitor(new FindLastStoreBRVisitor(*KV, OriginalR, - EnableNullFPSuppression)); + BR.addVisitor(llvm::make_unique<FindLastStoreBRVisitor>( + *KV, OriginalR, EnableNullFPSuppression)); } } } @@ -979,8 +976,8 @@ bool bugreporter::trackNullOrUndefValue(const ExplodedNode *N, // got initialized. if (const MemRegion *RR = getLocationRegionIfReference(Inner, N)) { if (Optional<KnownSVal> KV = LVal.getAs<KnownSVal>()) - report.addVisitor(new FindLastStoreBRVisitor(*KV, RR, - EnableNullFPSuppression)); + report.addVisitor(llvm::make_unique<FindLastStoreBRVisitor>( + *KV, RR, EnableNullFPSuppression)); } } @@ -990,30 +987,26 @@ bool bugreporter::trackNullOrUndefValue(const ExplodedNode *N, report.markInteresting(R); report.markInteresting(V); - report.addVisitor(new UndefOrNullArgVisitor(R)); + report.addVisitor(llvm::make_unique<UndefOrNullArgVisitor>(R)); // If the contents are symbolic, find out when they became null. - if (V.getAsLocSymbol(/*IncludeBaseRegions*/ true)) { - BugReporterVisitor *ConstraintTracker = - new TrackConstraintBRVisitor(V.castAs<DefinedSVal>(), false); - report.addVisitor(ConstraintTracker); - } + if (V.getAsLocSymbol(/*IncludeBaseRegions*/ true)) + report.addVisitor(llvm::make_unique<TrackConstraintBRVisitor>( + V.castAs<DefinedSVal>(), false)); // Add visitor, which will suppress inline defensive checks. if (Optional<DefinedSVal> DV = V.getAs<DefinedSVal>()) { - if (!DV->isZeroConstant() && - LVState->isNull(*DV).isConstrainedTrue() && - EnableNullFPSuppression) { - BugReporterVisitor *IDCSuppressor = - new SuppressInlineDefensiveChecksVisitor(*DV, - LVNode); - report.addVisitor(IDCSuppressor); + if (!DV->isZeroConstant() && LVState->isNull(*DV).isConstrainedTrue() && + EnableNullFPSuppression) { + report.addVisitor( + llvm::make_unique<SuppressInlineDefensiveChecksVisitor>(*DV, + LVNode)); } } if (Optional<KnownSVal> KV = V.getAs<KnownSVal>()) - report.addVisitor(new FindLastStoreBRVisitor(*KV, R, - EnableNullFPSuppression)); + report.addVisitor(llvm::make_unique<FindLastStoreBRVisitor>( + *KV, R, EnableNullFPSuppression)); return true; } } @@ -1044,12 +1037,12 @@ bool bugreporter::trackNullOrUndefValue(const ExplodedNode *N, RVal = state->getSVal(L->getRegion()); const MemRegion *RegionRVal = RVal.getAsRegion(); - report.addVisitor(new UndefOrNullArgVisitor(L->getRegion())); + report.addVisitor(llvm::make_unique<UndefOrNullArgVisitor>(L->getRegion())); if (RegionRVal && isa<SymbolicRegion>(RegionRVal)) { report.markInteresting(RegionRVal); - report.addVisitor(new TrackConstraintBRVisitor( - loc::MemRegionVal(RegionRVal), false)); + report.addVisitor(llvm::make_unique<TrackConstraintBRVisitor>( + loc::MemRegionVal(RegionRVal), false)); } } @@ -1132,8 +1125,8 @@ void FindLastStoreBRVisitor::registerStatementVarDecls(BugReport &BR, if (V.getAs<loc::ConcreteInt>() || V.getAs<nonloc::ConcreteInt>()) { // Register a new visitor with the BugReport. - BR.addVisitor(new FindLastStoreBRVisitor(V.castAs<KnownSVal>(), R, - EnableNullFPSuppression)); + BR.addVisitor(llvm::make_unique<FindLastStoreBRVisitor>( + V.castAs<KnownSVal>(), R, EnableNullFPSuppression)); } } } @@ -1517,8 +1510,7 @@ static bool isInStdNamespace(const Decl *D) { return ND->isStdNamespace(); } - -PathDiagnosticPiece * +std::unique_ptr<PathDiagnosticPiece> LikelyFalsePositiveSuppressionBRVisitor::getEndPath(BugReporterContext &BRC, const ExplodedNode *N, BugReport &BR) { diff --git a/lib/StaticAnalyzer/Core/CallEvent.cpp b/lib/StaticAnalyzer/Core/CallEvent.cpp index 7e0670a..f673544 100644 --- a/lib/StaticAnalyzer/Core/CallEvent.cpp +++ b/lib/StaticAnalyzer/Core/CallEvent.cpp @@ -607,7 +607,7 @@ RuntimeDefinition CXXDestructorCall::getRuntimeDefinition() const { ArrayRef<ParmVarDecl*> ObjCMethodCall::parameters() const { const ObjCMethodDecl *D = getDecl(); if (!D) - return ArrayRef<ParmVarDecl*>(); + return None; return D->parameters(); } diff --git a/lib/StaticAnalyzer/Core/CoreEngine.cpp b/lib/StaticAnalyzer/Core/CoreEngine.cpp index 4623c35..7844ad4 100644 --- a/lib/StaticAnalyzer/Core/CoreEngine.cpp +++ b/lib/StaticAnalyzer/Core/CoreEngine.cpp @@ -14,6 +14,7 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h" #include "clang/AST/Expr.h" +#include "clang/AST/ExprCXX.h" #include "clang/AST/StmtCXX.h" #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" @@ -164,7 +165,7 @@ WorkList* WorkList::makeBFSBlockDFSContents() { bool CoreEngine::ExecuteWorkList(const LocationContext *L, unsigned Steps, ProgramStateRef InitState) { - if (G->num_roots() == 0) { // Initialize the analysis by constructing + if (G.num_roots() == 0) { // Initialize the analysis by constructing // the root if none exists. const CFGBlock *Entry = &(L->getCFG()->getEntry()); @@ -273,8 +274,8 @@ bool CoreEngine::ExecuteWorkListWithInitialState(const LocationContext *L, ProgramStateRef InitState, ExplodedNodeSet &Dst) { bool DidNotFinish = ExecuteWorkList(L, Steps, InitState); - for (ExplodedGraph::eop_iterator I = G->eop_begin(), - E = G->eop_end(); I != E; ++I) { + for (ExplodedGraph::eop_iterator I = G.eop_begin(), E = G.eop_end(); I != E; + ++I) { Dst.Add(*I); } return DidNotFinish; @@ -346,6 +347,11 @@ void CoreEngine::HandleBlockExit(const CFGBlock * B, ExplodedNode *Pred) { default: llvm_unreachable("Analysis for this terminator not implemented."); + case Stmt::CXXBindTemporaryExprClass: + HandleCleanupTemporaryBranch( + cast<CXXBindTemporaryExpr>(B->getTerminator().getStmt()), B, Pred); + return; + // Model static initializers. case Stmt::DeclStmtClass: HandleStaticInit(cast<DeclStmt>(Term), B, Pred); @@ -461,6 +467,17 @@ void CoreEngine::HandleBranch(const Stmt *Cond, const Stmt *Term, enqueue(Dst); } +void CoreEngine::HandleCleanupTemporaryBranch(const CXXBindTemporaryExpr *BTE, + const CFGBlock *B, + ExplodedNode *Pred) { + assert(B->succ_size() == 2); + NodeBuilderContext Ctx(*this, B, Pred); + ExplodedNodeSet Dst; + SubEng.processCleanupTemporaryBranch(BTE, Ctx, Pred, Dst, *(B->succ_begin()), + *(B->succ_begin() + 1)); + // Enqueue the new frontier onto the worklist. + enqueue(Dst); +} void CoreEngine::HandleStaticInit(const DeclStmt *DS, const CFGBlock *B, ExplodedNode *Pred) { @@ -494,13 +511,13 @@ void CoreEngine::generateNode(const ProgramPoint &Loc, ExplodedNode *Pred) { bool IsNew; - ExplodedNode *Node = G->getNode(Loc, State, false, &IsNew); + ExplodedNode *Node = G.getNode(Loc, State, false, &IsNew); if (Pred) - Node->addPredecessor(Pred, *G); // Link 'Node' with its predecessor. + Node->addPredecessor(Pred, G); // Link 'Node' with its predecessor. else { assert (IsNew); - G->addRoot(Node); // 'Node' has no predecessor. Make it a root. + G.addRoot(Node); // 'Node' has no predecessor. Make it a root. } // Only add 'Node' to the worklist if it was freshly generated. @@ -549,8 +566,8 @@ void CoreEngine::enqueueStmtNode(ExplodedNode *N, } bool IsNew; - ExplodedNode *Succ = G->getNode(Loc, N->getState(), false, &IsNew); - Succ->addPredecessor(N, *G); + ExplodedNode *Succ = G.getNode(Loc, N->getState(), false, &IsNew); + Succ->addPredecessor(N, G); if (IsNew) WList->enqueue(Succ, Block, Idx+1); @@ -565,8 +582,8 @@ ExplodedNode *CoreEngine::generateCallExitBeginNode(ExplodedNode *N) { CallExitBegin Loc(LocCtx); bool isNew; - ExplodedNode *Node = G->getNode(Loc, N->getState(), false, &isNew); - Node->addPredecessor(N, *G); + ExplodedNode *Node = G.getNode(Loc, N->getState(), false, &isNew); + Node->addPredecessor(N, G); return isNew ? Node : nullptr; } @@ -596,7 +613,7 @@ void CoreEngine::enqueueEndOfFunction(ExplodedNodeSet &Set) { WList->enqueue(N); } else { // TODO: We should run remove dead bindings here. - G->addEndOfPath(N); + G.addEndOfPath(N); NumPathsExplored++; } } @@ -611,8 +628,8 @@ ExplodedNode* NodeBuilder::generateNodeImpl(const ProgramPoint &Loc, bool MarkAsSink) { HasGeneratedNodes = true; bool IsNew; - ExplodedNode *N = C.Eng.G->getNode(Loc, State, MarkAsSink, &IsNew); - N->addPredecessor(FromN, *C.Eng.G); + ExplodedNode *N = C.Eng.G.getNode(Loc, State, MarkAsSink, &IsNew); + N->addPredecessor(FromN, C.Eng.G); Frontier.erase(FromN); if (!IsNew) @@ -653,10 +670,10 @@ IndirectGotoNodeBuilder::generateNode(const iterator &I, ProgramStateRef St, bool IsSink) { bool IsNew; - ExplodedNode *Succ = Eng.G->getNode(BlockEdge(Src, I.getBlock(), - Pred->getLocationContext()), St, - IsSink, &IsNew); - Succ->addPredecessor(Pred, *Eng.G); + ExplodedNode *Succ = + Eng.G.getNode(BlockEdge(Src, I.getBlock(), Pred->getLocationContext()), + St, IsSink, &IsNew); + Succ->addPredecessor(Pred, Eng.G); if (!IsNew) return nullptr; @@ -673,10 +690,10 @@ SwitchNodeBuilder::generateCaseStmtNode(const iterator &I, ProgramStateRef St) { bool IsNew; - ExplodedNode *Succ = Eng.G->getNode(BlockEdge(Src, I.getBlock(), - Pred->getLocationContext()), St, - false, &IsNew); - Succ->addPredecessor(Pred, *Eng.G); + ExplodedNode *Succ = + Eng.G.getNode(BlockEdge(Src, I.getBlock(), Pred->getLocationContext()), + St, false, &IsNew); + Succ->addPredecessor(Pred, Eng.G); if (!IsNew) return nullptr; @@ -698,10 +715,10 @@ SwitchNodeBuilder::generateDefaultCaseNode(ProgramStateRef St, return nullptr; bool IsNew; - ExplodedNode *Succ = Eng.G->getNode(BlockEdge(Src, DefaultBlock, - Pred->getLocationContext()), St, - IsSink, &IsNew); - Succ->addPredecessor(Pred, *Eng.G); + ExplodedNode *Succ = + Eng.G.getNode(BlockEdge(Src, DefaultBlock, Pred->getLocationContext()), + St, IsSink, &IsNew); + Succ->addPredecessor(Pred, Eng.G); if (!IsNew) return nullptr; diff --git a/lib/StaticAnalyzer/Core/ExplodedGraph.cpp b/lib/StaticAnalyzer/Core/ExplodedGraph.cpp index 1c9a282..010d26e 100644 --- a/lib/StaticAnalyzer/Core/ExplodedGraph.cpp +++ b/lib/StaticAnalyzer/Core/ExplodedGraph.cpp @@ -336,10 +336,10 @@ ExplodedNode *ExplodedGraph::getNode(const ProgramPoint &L, return V; } -ExplodedGraph * +std::unique_ptr<ExplodedGraph> ExplodedGraph::trim(ArrayRef<const NodeTy *> Sinks, InterExplodedGraphMap *ForwardMap, - InterExplodedGraphMap *InverseMap) const{ + InterExplodedGraphMap *InverseMap) const { if (Nodes.empty()) return nullptr; @@ -365,12 +365,9 @@ ExplodedGraph::trim(ArrayRef<const NodeTy *> Sinks, const ExplodedNode *N = WL1.pop_back_val(); // Have we already visited this node? If so, continue to the next one. - if (Pass1.count(N)) + if (!Pass1.insert(N).second) continue; - // Otherwise, mark this node as visited. - Pass1.insert(N); - // If this is a root enqueue it to the second worklist. if (N->Preds.empty()) { WL2.push_back(N); @@ -378,9 +375,7 @@ ExplodedGraph::trim(ArrayRef<const NodeTy *> Sinks, } // Visit our predecessors and enqueue them. - for (ExplodedNode::pred_iterator I = N->Preds.begin(), E = N->Preds.end(); - I != E; ++I) - WL1.push_back(*I); + WL1.append(N->Preds.begin(), N->Preds.end()); } // We didn't hit a root? Return with a null pointer for the new graph. @@ -388,7 +383,7 @@ ExplodedGraph::trim(ArrayRef<const NodeTy *> Sinks, return nullptr; // Create an empty graph. - ExplodedGraph* G = MakeEmptyGraph(); + std::unique_ptr<ExplodedGraph> G = MakeEmptyGraph(); // ===- Pass 2 (forward DFS to construct the new graph) -=== while (!WL2.empty()) { diff --git a/lib/StaticAnalyzer/Core/ExprEngine.cpp b/lib/StaticAnalyzer/Core/ExprEngine.cpp index 4e4095c..4699df8 100644 --- a/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -51,6 +51,15 @@ STATISTIC(NumMaxBlockCountReachedInInlined, STATISTIC(NumTimesRetriedWithoutInlining, "The # of times we re-evaluated a call without inlining"); +typedef std::pair<const CXXBindTemporaryExpr *, const StackFrameContext *> + CXXBindTemporaryContext; + +// Keeps track of whether CXXBindTemporaryExpr nodes have been evaluated. +// The StackFrameContext assures that nested calls due to inlined recursive +// functions do not interfere. +REGISTER_TRAIT_WITH_PROGRAMSTATE(InitializedTemporariesSet, + llvm::ImmutableSet<CXXBindTemporaryContext>) + //===----------------------------------------------------------------------===// // Engine construction and deletion. //===----------------------------------------------------------------------===// @@ -659,13 +668,71 @@ void ExprEngine::ProcessMemberDtor(const CFGMemberDtor D, void ExprEngine::ProcessTemporaryDtor(const CFGTemporaryDtor D, ExplodedNode *Pred, ExplodedNodeSet &Dst) { + ExplodedNodeSet CleanDtorState; + StmtNodeBuilder StmtBldr(Pred, CleanDtorState, *currBldrCtx); + ProgramStateRef State = Pred->getState(); + if (State->contains<InitializedTemporariesSet>( + std::make_pair(D.getBindTemporaryExpr(), Pred->getStackFrame()))) { + // FIXME: Currently we insert temporary destructors for default parameters, + // but we don't insert the constructors. + State = State->remove<InitializedTemporariesSet>( + std::make_pair(D.getBindTemporaryExpr(), Pred->getStackFrame())); + } + StmtBldr.generateNode(D.getBindTemporaryExpr(), Pred, State); QualType varType = D.getBindTemporaryExpr()->getSubExpr()->getType(); - - // FIXME: Inlining of temporary destructors is not supported yet anyway, so we - // just put a NULL region for now. This will need to be changed later. + // FIXME: Currently CleanDtorState can be empty here due to temporaries being + // bound to default parameters. + assert(CleanDtorState.size() <= 1); + ExplodedNode *CleanPred = + CleanDtorState.empty() ? Pred : *CleanDtorState.begin(); + // FIXME: Inlining of temporary destructors is not supported yet anyway, so + // we just put a NULL region for now. This will need to be changed later. VisitCXXDestructor(varType, nullptr, D.getBindTemporaryExpr(), - /*IsBase=*/ false, Pred, Dst); + /*IsBase=*/false, CleanPred, Dst); +} + +void ExprEngine::processCleanupTemporaryBranch(const CXXBindTemporaryExpr *BTE, + NodeBuilderContext &BldCtx, + ExplodedNode *Pred, + ExplodedNodeSet &Dst, + const CFGBlock *DstT, + const CFGBlock *DstF) { + BranchNodeBuilder TempDtorBuilder(Pred, Dst, BldCtx, DstT, DstF); + if (Pred->getState()->contains<InitializedTemporariesSet>( + std::make_pair(BTE, Pred->getStackFrame()))) { + TempDtorBuilder.markInfeasible(false); + TempDtorBuilder.generateNode(Pred->getState(), true, Pred); + } else { + TempDtorBuilder.markInfeasible(true); + TempDtorBuilder.generateNode(Pred->getState(), false, Pred); + } +} + +void ExprEngine::VisitCXXBindTemporaryExpr(const CXXBindTemporaryExpr *BTE, + ExplodedNodeSet &PreVisit, + ExplodedNodeSet &Dst) { + if (!getAnalysisManager().options.includeTemporaryDtorsInCFG()) { + // In case we don't have temporary destructors in the CFG, do not mark + // the initialization - we would otherwise never clean it up. + Dst = PreVisit; + return; + } + StmtNodeBuilder StmtBldr(PreVisit, Dst, *currBldrCtx); + for (ExplodedNode *Node : PreVisit) { + ProgramStateRef State = Node->getState(); + + if (!State->contains<InitializedTemporariesSet>( + std::make_pair(BTE, Node->getStackFrame()))) { + // FIXME: Currently the state might already contain the marker due to + // incorrect handling of temporaries bound to default parameters; for + // those, we currently skip the CXXBindTemporaryExpr but rely on adding + // temporary destructor nodes. + State = State->add<InitializedTemporariesSet>( + std::make_pair(BTE, Node->getStackFrame())); + } + StmtBldr.generateNode(BTE, Node, State); + } } void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, @@ -685,6 +752,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, case Stmt::CXXTryStmtClass: case Stmt::CXXTypeidExprClass: case Stmt::CXXUuidofExprClass: + case Stmt::CXXFoldExprClass: case Stmt::MSPropertyRefExprClass: case Stmt::CXXUnresolvedConstructExprClass: case Stmt::DependentScopeDeclRefExprClass: @@ -693,6 +761,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, case Stmt::ExpressionTraitExprClass: case Stmt::UnresolvedLookupExprClass: case Stmt::UnresolvedMemberExprClass: + case Stmt::TypoExprClass: case Stmt::CXXNoexceptExprClass: case Stmt::PackExpansionExprClass: case Stmt::SubstNonTypeTemplateParmPackExprClass: @@ -734,18 +803,24 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, case Stmt::OMPParallelDirectiveClass: case Stmt::OMPSimdDirectiveClass: case Stmt::OMPForDirectiveClass: + case Stmt::OMPForSimdDirectiveClass: case Stmt::OMPSectionsDirectiveClass: case Stmt::OMPSectionDirectiveClass: case Stmt::OMPSingleDirectiveClass: case Stmt::OMPMasterDirectiveClass: case Stmt::OMPCriticalDirectiveClass: case Stmt::OMPParallelForDirectiveClass: + case Stmt::OMPParallelForSimdDirectiveClass: case Stmt::OMPParallelSectionsDirectiveClass: case Stmt::OMPTaskDirectiveClass: case Stmt::OMPTaskyieldDirectiveClass: case Stmt::OMPBarrierDirectiveClass: case Stmt::OMPTaskwaitDirectiveClass: case Stmt::OMPFlushDirectiveClass: + case Stmt::OMPOrderedDirectiveClass: + case Stmt::OMPAtomicDirectiveClass: + case Stmt::OMPTargetDirectiveClass: + case Stmt::OMPTeamsDirectiveClass: llvm_unreachable("Stmt should not be in analyzer evaluation loop"); case Stmt::ObjCSubscriptRefExprClass: @@ -771,6 +846,17 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, // Handled due to fully linearised CFG. break; + case Stmt::CXXBindTemporaryExprClass: { + Bldr.takeNodes(Pred); + ExplodedNodeSet PreVisit; + getCheckerManager().runCheckersForPreStmt(PreVisit, Pred, S, *this); + ExplodedNodeSet Next; + VisitCXXBindTemporaryExpr(cast<CXXBindTemporaryExpr>(S), PreVisit, Next); + getCheckerManager().runCheckersForPostStmt(Dst, Next, S, *this); + Bldr.addNodes(Dst); + break; + } + // Cases not handled yet; but will handle some day. case Stmt::DesignatedInitExprClass: case Stmt::ExtVectorElementExprClass: @@ -784,7 +870,6 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, case Stmt::ObjCProtocolExprClass: case Stmt::ObjCSelectorExprClass: case Stmt::ParenListExprClass: - case Stmt::PredefinedExprClass: case Stmt::ShuffleVectorExprClass: case Stmt::ConvertVectorExprClass: case Stmt::VAArgExprClass: @@ -796,6 +881,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, // Cases we intentionally don't evaluate, since they don't need // to be explicitly evaluated. + case Stmt::PredefinedExprClass: case Stmt::AddrLabelExprClass: case Stmt::AttributedStmtClass: case Stmt::IntegerLiteralClass: @@ -808,7 +894,6 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, case Stmt::SizeOfPackExprClass: case Stmt::StringLiteralClass: case Stmt::ObjCStringLiteralClass: - case Stmt::CXXBindTemporaryExprClass: case Stmt::CXXPseudoDestructorExprClass: case Stmt::SubstNonTypeTemplateParmExprClass: case Stmt::CXXNullPtrLiteralExprClass: { @@ -1403,11 +1488,8 @@ static const Stmt *ResolveCondition(const Stmt *Condition, if (!BO || !BO->isLogicalOp()) return Condition; - // FIXME: This is a workaround until we handle temporary destructor branches - // correctly; currently, temporary destructor branches lead to blocks that - // only have a terminator (and no statements). These blocks violate the - // invariant this function assumes. - if (B->getTerminator().isTemporaryDtorsBranch()) return Condition; + assert(!B->getTerminator().isTemporaryDtorsBranch() && + "Temporary destructor branches handled by processBindTemporary."); // For logical operations, we still have the case where some branches // use the traditional "merge" approach and others sink the branch @@ -1436,6 +1518,8 @@ void ExprEngine::processBranch(const Stmt *Condition, const Stmt *Term, ExplodedNodeSet &Dst, const CFGBlock *DstT, const CFGBlock *DstF) { + assert((!Condition || !isa<CXXBindTemporaryExpr>(Condition)) && + "CXXBindTemporaryExprs are handled by processBindTemporary."); const LocationContext *LCtx = Pred->getLocationContext(); PrettyStackTraceLocationContext StackCrashInfo(LCtx); currBldrCtx = &BldCtx; @@ -1599,10 +1683,29 @@ void ExprEngine::processIndirectGoto(IndirectGotoNodeBuilder &builder) { builder.generateNode(I, state); } +#if 0 +static bool stackFrameDoesNotContainInitializedTemporaries(ExplodedNode &Pred) { + const StackFrameContext* Frame = Pred.getStackFrame(); + const llvm::ImmutableSet<CXXBindTemporaryContext> &Set = + Pred.getState()->get<InitializedTemporariesSet>(); + return std::find_if(Set.begin(), Set.end(), + [&](const CXXBindTemporaryContext &Ctx) { + if (Ctx.second == Frame) { + Ctx.first->dump(); + llvm::errs() << "\n"; + } + return Ctx.second == Frame; + }) == Set.end(); +} +#endif + /// ProcessEndPath - Called by CoreEngine. Used to generate end-of-path /// nodes when the control reaches the end of a function. void ExprEngine::processEndOfFunction(NodeBuilderContext& BC, ExplodedNode *Pred) { + // FIXME: Assert that stackFrameDoesNotContainInitializedTemporaries(*Pred)). + // We currently cannot enable this assert, as lifetime extended temporaries + // are not modelled correctly. PrettyStackTraceLocationContext CrashInfo(Pred->getLocationContext()); StateMgr.EndPath(Pred->getState()); diff --git a/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp b/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp index b1e9f06..88b5464 100644 --- a/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp +++ b/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp @@ -20,8 +20,8 @@ #include "clang/Lex/Preprocessor.h" #include "clang/Rewrite/Core/HTMLRewrite.h" #include "clang/Rewrite/Core/Rewriter.h" -#include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Path.h" diff --git a/lib/StaticAnalyzer/Core/MemRegion.cpp b/lib/StaticAnalyzer/Core/MemRegion.cpp index 22711f5..76cead6 100644 --- a/lib/StaticAnalyzer/Core/MemRegion.cpp +++ b/lib/StaticAnalyzer/Core/MemRegion.cpp @@ -1116,17 +1116,6 @@ const SymbolicRegion *MemRegion::getSymbolicBase() const { return nullptr; } -// 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; -} - RegionRawOffset ElementRegion::getAsArrayOffset() const { CharUnits offset = CharUnits::Zero(); const ElementRegion *ER = this; @@ -1148,7 +1137,7 @@ RegionRawOffset ElementRegion::getAsArrayOffset() const { QualType elemType = ER->getElementType(); // If we are pointing to an incomplete type, go no further. - if (!IsCompleteType(C, elemType)) { + if (elemType->isIncompleteType()) { superR = ER; break; } @@ -1288,7 +1277,7 @@ RegionOffset MemRegion::getAsOffset() const { R = ER->getSuperRegion(); QualType EleTy = ER->getValueType(); - if (!IsCompleteType(getContext(), EleTy)) { + if (EleTy->isIncompleteType()) { // We cannot compute the offset of the base class. SymbolicOffsetBase = R; continue; diff --git a/lib/StaticAnalyzer/Core/PathDiagnostic.cpp b/lib/StaticAnalyzer/Core/PathDiagnostic.cpp index fd25bd8..b971fff 100644 --- a/lib/StaticAnalyzer/Core/PathDiagnostic.cpp +++ b/lib/StaticAnalyzer/Core/PathDiagnostic.cpp @@ -197,9 +197,8 @@ PathDiagnosticConsumer::~PathDiagnosticConsumer() { } } -void PathDiagnosticConsumer::HandlePathDiagnostic(PathDiagnostic *D) { - std::unique_ptr<PathDiagnostic> OwningD(D); - +void PathDiagnosticConsumer::HandlePathDiagnostic( + std::unique_ptr<PathDiagnostic> D) { if (!D || D->path.empty()) return; @@ -213,7 +212,7 @@ void PathDiagnosticConsumer::HandlePathDiagnostic(PathDiagnostic *D) { if (!supportsCrossFileDiagnostics()) { // Verify that the entire path is from the same FileID. FileID FID; - const SourceManager &SMgr = (*D->path.begin())->getLocation().getManager(); + const SourceManager &SMgr = D->path.front()->getLocation().getManager(); SmallVector<const PathPieces *, 5> WorkList; WorkList.push_back(&D->path); @@ -272,12 +271,12 @@ void PathDiagnosticConsumer::HandlePathDiagnostic(PathDiagnostic *D) { if (orig_size <= new_size) return; - assert(orig != D); + assert(orig != D.get()); Diags.RemoveNode(orig); delete orig; } - Diags.InsertNode(OwningD.release()); + Diags.InsertNode(D.release()); } static Optional<bool> comparePath(const PathPieces &X, const PathPieces &Y); diff --git a/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp b/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp index ba3ad2e..a2c66f8 100644 --- a/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp +++ b/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp @@ -42,7 +42,7 @@ namespace { void FlushDiagnosticsImpl(std::vector<const PathDiagnostic *> &Diags, FilesMade *filesMade) override; - virtual StringRef getName() const override { + StringRef getName() const override { return "PlistDiagnostics"; } @@ -338,10 +338,10 @@ void PlistDiagnostics::FlushDiagnosticsImpl( } // Open the file. - std::string ErrMsg; - llvm::raw_fd_ostream o(OutputFile.c_str(), ErrMsg, llvm::sys::fs::F_Text); - if (!ErrMsg.empty()) { - llvm::errs() << "warning: could not create file: " << OutputFile << '\n'; + std::error_code EC; + llvm::raw_fd_ostream o(OutputFile, EC, llvm::sys::fs::F_Text); + if (EC) { + llvm::errs() << "warning: could not create file: " << EC.message() << '\n'; return; } diff --git a/lib/StaticAnalyzer/Core/PrettyStackTraceLocationContext.h b/lib/StaticAnalyzer/Core/PrettyStackTraceLocationContext.h index c2af36f..e7cc23c 100644 --- a/lib/StaticAnalyzer/Core/PrettyStackTraceLocationContext.h +++ b/lib/StaticAnalyzer/Core/PrettyStackTraceLocationContext.h @@ -7,8 +7,8 @@ // //===----------------------------------------------------------------------===// -#ifndef LLVM_CLANG_STATICANALYZER_PRETTYSTACKTRACELOCATIONCONTEXT_H -#define LLVM_CLANG_STATICANALYZER_PRETTYSTACKTRACELOCATIONCONTEXT_H +#ifndef LLVM_CLANG_LIB_STATICANALYZER_CORE_PRETTYSTACKTRACELOCATIONCONTEXT_H +#define LLVM_CLANG_LIB_STATICANALYZER_CORE_PRETTYSTACKTRACELOCATIONCONTEXT_H #include "clang/Analysis/AnalysisContext.h" diff --git a/lib/StaticAnalyzer/Core/ProgramState.cpp b/lib/StaticAnalyzer/Core/ProgramState.cpp index 1714a27..60b32c7 100644 --- a/lib/StaticAnalyzer/Core/ProgramState.cpp +++ b/lib/StaticAnalyzer/Core/ProgramState.cpp @@ -75,8 +75,8 @@ ProgramStateManager::ProgramStateManager(ASTContext &Ctx, : Eng(SubEng), EnvMgr(alloc), GDMFactory(alloc), svalBuilder(createSimpleSValBuilder(alloc, Ctx, *this)), CallEventMgr(new CallEventManager(alloc)), Alloc(alloc) { - StoreMgr.reset((*CreateSMgr)(*this)); - ConstraintMgr.reset((*CreateCMgr)(*this, SubEng)); + StoreMgr = (*CreateSMgr)(*this); + ConstraintMgr = (*CreateCMgr)(*this, SubEng); } diff --git a/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp b/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp index 77578d3..170f7c0 100644 --- a/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp +++ b/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp @@ -328,9 +328,9 @@ private: } // end anonymous namespace -ConstraintManager * +std::unique_ptr<ConstraintManager> ento::CreateRangeConstraintManager(ProgramStateManager &StMgr, SubEngine *Eng) { - return new RangeConstraintManager(Eng, StMgr.getSValBuilder()); + return llvm::make_unique<RangeConstraintManager>(Eng, StMgr.getSValBuilder()); } const llvm::APSInt* RangeConstraintManager::getSymVal(ProgramStateRef St, diff --git a/lib/StaticAnalyzer/Core/RegionStore.cpp b/lib/StaticAnalyzer/Core/RegionStore.cpp index 3bbbb34..4505622 100644 --- a/lib/StaticAnalyzer/Core/RegionStore.cpp +++ b/lib/StaticAnalyzer/Core/RegionStore.cpp @@ -609,16 +609,17 @@ public: // Part of public interface to class. // RegionStore creation. //===----------------------------------------------------------------------===// -StoreManager *ento::CreateRegionStoreManager(ProgramStateManager& StMgr) { +std::unique_ptr<StoreManager> +ento::CreateRegionStoreManager(ProgramStateManager &StMgr) { RegionStoreFeatures F = maximal_features_tag(); - return new RegionStoreManager(StMgr, F); + return llvm::make_unique<RegionStoreManager>(StMgr, F); } -StoreManager * +std::unique_ptr<StoreManager> ento::CreateFieldsOnlyRegionStoreManager(ProgramStateManager &StMgr) { RegionStoreFeatures F = minimal_features_tag(); F.enableFields(true); - return new RegionStoreManager(StMgr, F); + return llvm::make_unique<RegionStoreManager>(StMgr, F); } @@ -708,7 +709,7 @@ public: } bool AddToWorkList(WorkListElement E, const ClusterBindings *C) { - if (C && !Visited.insert(C)) + if (C && !Visited.insert(C).second) return false; WL.push_back(E); return true; diff --git a/lib/StaticAnalyzer/Core/SimpleConstraintManager.h b/lib/StaticAnalyzer/Core/SimpleConstraintManager.h index 21e2283..a72d1d4 100644 --- a/lib/StaticAnalyzer/Core/SimpleConstraintManager.h +++ b/lib/StaticAnalyzer/Core/SimpleConstraintManager.h @@ -11,8 +11,8 @@ // //===----------------------------------------------------------------------===// -#ifndef LLVM_CLANG_GR_SIMPLE_CONSTRAINT_MANAGER_H -#define LLVM_CLANG_GR_SIMPLE_CONSTRAINT_MANAGER_H +#ifndef LLVM_CLANG_LIB_STATICANALYZER_CORE_SIMPLECONSTRAINTMANAGER_H +#define LLVM_CLANG_LIB_STATICANALYZER_CORE_SIMPLECONSTRAINTMANAGER_H #include "clang/StaticAnalyzer/Core/PathSensitive/ConstraintManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" diff --git a/lib/StaticAnalyzer/Core/Store.cpp b/lib/StaticAnalyzer/Core/Store.cpp index e38be3e..99ec1e7 100644 --- a/lib/StaticAnalyzer/Core/Store.cpp +++ b/lib/StaticAnalyzer/Core/Store.cpp @@ -48,17 +48,6 @@ const MemRegion *StoreManager::MakeElementRegion(const MemRegion *Base, return MRMgr.getElementRegion(EleTy, idx, Base, svalBuilder.getContext()); } -// FIXME: Merge with the implementation of the same method in MemRegion.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; -} - StoreRef StoreManager::BindDefault(Store store, const MemRegion *R, SVal V) { return StoreRef(store, *this); } @@ -196,7 +185,7 @@ const MemRegion *StoreManager::castRegion(const MemRegion *R, QualType CastToTy) const MemRegion *newSuperR = nullptr; // We can only compute sizeof(PointeeTy) if it is a complete type. - if (IsCompleteType(Ctx, PointeeTy)) { + if (!PointeeTy->isIncompleteType()) { // Compute the size in **bytes**. CharUnits pointeeTySize = Ctx.getTypeSizeInChars(PointeeTy); if (!pointeeTySize.isZero()) { diff --git a/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp b/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp index f0dd274..183ef35 100644 --- a/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp +++ b/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp @@ -12,6 +12,7 @@ //===----------------------------------------------------------------------===// #include "clang/StaticAnalyzer/Frontend/AnalysisConsumer.h" +#include "ModelInjector.h" #include "clang/AST/ASTConsumer.h" #include "clang/AST/DataRecursiveASTVisitor.h" #include "clang/AST/Decl.h" @@ -21,8 +22,10 @@ #include "clang/Analysis/Analyses/LiveVariables.h" #include "clang/Analysis/CFG.h" #include "clang/Analysis/CallGraph.h" +#include "clang/Analysis/CodeInjector.h" #include "clang/Basic/FileManager.h" #include "clang/Basic/SourceManager.h" +#include "clang/Frontend/CompilerInstance.h" #include "clang/Lex/Preprocessor.h" #include "clang/StaticAnalyzer/Checkers/LocalCheckers.h" #include "clang/StaticAnalyzer/Core/AnalyzerOptions.h" @@ -51,7 +54,7 @@ using llvm::SmallPtrSet; #define DEBUG_TYPE "AnalysisConsumer" -static ExplodedNode::Auditor* CreateUbiViz(); +static std::unique_ptr<ExplodedNode::Auditor> CreateUbiViz(); STATISTIC(NumFunctionTopLevel, "The # of functions at top level."); STATISTIC(NumFunctionsAnalyzed, @@ -157,6 +160,7 @@ public: const std::string OutDir; AnalyzerOptionsRef Opts; ArrayRef<std::string> Plugins; + CodeInjector *Injector; /// \brief Stores the declarations from the local translation unit. /// Note, we pre-compute the local declarations at parse time as an @@ -184,9 +188,10 @@ public: AnalysisConsumer(const Preprocessor& pp, const std::string& outdir, AnalyzerOptionsRef opts, - ArrayRef<std::string> plugins) - : RecVisitorMode(0), RecVisitorBR(nullptr), - Ctx(nullptr), PP(pp), OutDir(outdir), Opts(opts), Plugins(plugins) { + ArrayRef<std::string> plugins, + CodeInjector *injector) + : RecVisitorMode(0), RecVisitorBR(nullptr), Ctx(nullptr), PP(pp), + OutDir(outdir), Opts(opts), Plugins(plugins), Injector(injector) { DigestAnalyzerOptions(); if (Opts->PrintStats) { llvm::EnableStatistics(); @@ -284,16 +289,12 @@ public: void Initialize(ASTContext &Context) override { Ctx = &Context; - checkerMgr.reset(createCheckerManager(*Opts, PP.getLangOpts(), Plugins, - PP.getDiagnostics())); - Mgr.reset(new AnalysisManager(*Ctx, - PP.getDiagnostics(), - PP.getLangOpts(), - PathConsumers, - CreateStoreMgr, - CreateConstraintMgr, - checkerMgr.get(), - *Opts)); + checkerMgr = createCheckerManager(*Opts, PP.getLangOpts(), Plugins, + PP.getDiagnostics()); + + Mgr = llvm::make_unique<AnalysisManager>( + *Ctx, PP.getDiagnostics(), PP.getLangOpts(), PathConsumers, + CreateStoreMgr, CreateConstraintMgr, checkerMgr.get(), *Opts, Injector); } /// \brief Store the top level decls in the set to be processed later on. @@ -307,7 +308,7 @@ public: /// analyzed. This allows to redefine the default inlining policies when /// analyzing a given function. ExprEngine::InliningModes - getInliningModeForFunction(const Decl *D, const SetOfConstDecls &Visited); + getInliningModeForFunction(const Decl *D, const SetOfConstDecls &Visited); /// \brief Build the call graph for all the top level decls of this TU and /// use it to define the order in which the functions should be visited. @@ -506,6 +507,11 @@ void AnalysisConsumer::HandleTranslationUnit(ASTContext &C) { if (Diags.hasErrorOccurred() || Diags.hasFatalErrorOccurred()) return; + // Don't analyze if the user explicitly asked for no checks to be performed + // on this file. + if (Opts->DisableAllChecks) + return; + { if (TUTotalTimer) TUTotalTimer->startTimer(); @@ -643,7 +649,7 @@ void AnalysisConsumer::ActionExprEngine(Decl *D, bool ObjCGCEnabled, // Set the graph auditor. std::unique_ptr<ExplodedNode::Auditor> Auditor; if (Mgr->options.visualizeExplodedGraphWithUbiGraph) { - Auditor.reset(CreateUbiViz()); + Auditor = CreateUbiViz(); ExplodedNode::SetAuditor(Auditor.get()); } @@ -687,14 +693,18 @@ void AnalysisConsumer::RunPathSensitiveChecks(Decl *D, // AnalysisConsumer creation. //===----------------------------------------------------------------------===// -AnalysisASTConsumer * -ento::CreateAnalysisConsumer(const Preprocessor &pp, const std::string &outDir, - AnalyzerOptionsRef opts, - ArrayRef<std::string> plugins) { +std::unique_ptr<AnalysisASTConsumer> +ento::CreateAnalysisConsumer(CompilerInstance &CI) { // Disable the effects of '-Werror' when using the AnalysisConsumer. - pp.getDiagnostics().setWarningsAsErrors(false); + CI.getPreprocessor().getDiagnostics().setWarningsAsErrors(false); + + AnalyzerOptionsRef analyzerOpts = CI.getAnalyzerOpts(); + bool hasModelPath = analyzerOpts->Config.count("model-path") > 0; - return new AnalysisConsumer(pp, outDir, opts, plugins); + return llvm::make_unique<AnalysisConsumer>( + CI.getPreprocessor(), CI.getFrontendOpts().OutputFile, analyzerOpts, + CI.getFrontendOpts().Plugins, + hasModelPath ? new ModelInjector(CI) : nullptr); } //===----------------------------------------------------------------------===// @@ -721,7 +731,7 @@ public: } // end anonymous namespace -static ExplodedNode::Auditor* CreateUbiViz() { +static std::unique_ptr<ExplodedNode::Auditor> CreateUbiViz() { SmallString<128> P; int FD; llvm::sys::fs::createTemporaryFile("llvm_ubi", "", FD, P); @@ -729,7 +739,7 @@ static ExplodedNode::Auditor* CreateUbiViz() { auto Stream = llvm::make_unique<llvm::raw_fd_ostream>(FD, true); - return new UbigraphViz(std::move(Stream), P); + return llvm::make_unique<UbigraphViz>(std::move(Stream), P); } void UbigraphViz::AddEdge(ExplodedNode *Src, ExplodedNode *Dst) { @@ -778,7 +788,9 @@ UbigraphViz::~UbigraphViz() { Out.reset(); llvm::errs() << "Running 'ubiviz' program... "; std::string ErrMsg; - std::string Ubiviz = llvm::sys::FindProgramByName("ubiviz"); + std::string Ubiviz; + if (auto Path = llvm::sys::findProgramByName("ubiviz")) + Ubiviz = *Path; std::vector<const char*> args; args.push_back(Ubiviz.c_str()); args.push_back(Filename.c_str()); diff --git a/lib/StaticAnalyzer/Frontend/CMakeLists.txt b/lib/StaticAnalyzer/Frontend/CMakeLists.txt index 5349ed9..e3ca91a 100644 --- a/lib/StaticAnalyzer/Frontend/CMakeLists.txt +++ b/lib/StaticAnalyzer/Frontend/CMakeLists.txt @@ -7,13 +7,16 @@ set(LLVM_LINK_COMPONENTS add_clang_library(clangStaticAnalyzerFrontend AnalysisConsumer.cpp CheckerRegistration.cpp + ModelConsumer.cpp FrontendActions.cpp + ModelInjector.cpp LINK_LIBS clangAST clangAnalysis clangBasic clangFrontend + clangLex clangStaticAnalyzerCheckers clangStaticAnalyzerCore ) diff --git a/lib/StaticAnalyzer/Frontend/CheckerRegistration.cpp b/lib/StaticAnalyzer/Frontend/CheckerRegistration.cpp index e2577c3..36565cb 100644 --- a/lib/StaticAnalyzer/Frontend/CheckerRegistration.cpp +++ b/lib/StaticAnalyzer/Frontend/CheckerRegistration.cpp @@ -99,11 +99,10 @@ void ClangCheckerRegistry::warnIncompatible(DiagnosticsEngine *diags, << pluginAPIVersion; } - -CheckerManager *ento::createCheckerManager(AnalyzerOptions &opts, - const LangOptions &langOpts, - ArrayRef<std::string> plugins, - DiagnosticsEngine &diags) { +std::unique_ptr<CheckerManager> +ento::createCheckerManager(AnalyzerOptions &opts, const LangOptions &langOpts, + ArrayRef<std::string> plugins, + DiagnosticsEngine &diags) { std::unique_ptr<CheckerManager> checkerMgr( new CheckerManager(langOpts, &opts)); @@ -118,12 +117,15 @@ CheckerManager *ento::createCheckerManager(AnalyzerOptions &opts, checkerMgr->finishedCheckerRegistration(); for (unsigned i = 0, e = checkerOpts.size(); i != e; ++i) { - if (checkerOpts[i].isUnclaimed()) + if (checkerOpts[i].isUnclaimed()) { diags.Report(diag::err_unknown_analyzer_checker) << checkerOpts[i].getName(); + diags.Report(diag::note_suggest_disabling_all_checkers); + } + } - return checkerMgr.release(); + return std::move(checkerMgr); } void ento::printCheckerHelp(raw_ostream &out, ArrayRef<std::string> plugins) { diff --git a/lib/StaticAnalyzer/Frontend/FrontendActions.cpp b/lib/StaticAnalyzer/Frontend/FrontendActions.cpp index aa38077..b336080 100644 --- a/lib/StaticAnalyzer/Frontend/FrontendActions.cpp +++ b/lib/StaticAnalyzer/Frontend/FrontendActions.cpp @@ -8,16 +8,21 @@ //===----------------------------------------------------------------------===// #include "clang/StaticAnalyzer/Frontend/FrontendActions.h" -#include "clang/Frontend/CompilerInstance.h" #include "clang/StaticAnalyzer/Frontend/AnalysisConsumer.h" +#include "clang/StaticAnalyzer/Frontend/ModelConsumer.h" using namespace clang; using namespace ento; -ASTConsumer *AnalysisAction::CreateASTConsumer(CompilerInstance &CI, - StringRef InFile) { - return CreateAnalysisConsumer(CI.getPreprocessor(), - CI.getFrontendOpts().OutputFile, - CI.getAnalyzerOpts(), - CI.getFrontendOpts().Plugins); +std::unique_ptr<ASTConsumer> +AnalysisAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { + return CreateAnalysisConsumer(CI); } +ParseModelFileAction::ParseModelFileAction(llvm::StringMap<Stmt *> &Bodies) + : Bodies(Bodies) {} + +std::unique_ptr<ASTConsumer> +ParseModelFileAction::CreateASTConsumer(CompilerInstance &CI, + StringRef InFile) { + return llvm::make_unique<ModelConsumer>(Bodies); +} diff --git a/lib/StaticAnalyzer/Frontend/ModelConsumer.cpp b/lib/StaticAnalyzer/Frontend/ModelConsumer.cpp new file mode 100644 index 0000000..a65a5ee --- /dev/null +++ b/lib/StaticAnalyzer/Frontend/ModelConsumer.cpp @@ -0,0 +1,42 @@ +//===--- ModelConsumer.cpp - ASTConsumer for consuming model files --------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// \brief This file implements an ASTConsumer for consuming model files. +/// +/// This ASTConsumer handles the AST of a parsed model file. All top level +/// function definitions will be collected from that model file for later +/// retrieval during the static analysis. The body of these functions will not +/// be injected into the ASTUnit of the analyzed translation unit. It will be +/// available through the BodyFarm which is utilized by the AnalysisDeclContext +/// class. +/// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Frontend/ModelConsumer.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclGroup.h" + +using namespace clang; +using namespace ento; + +ModelConsumer::ModelConsumer(llvm::StringMap<Stmt *> &Bodies) + : Bodies(Bodies) {} + +bool ModelConsumer::HandleTopLevelDecl(DeclGroupRef D) { + for (DeclGroupRef::iterator I = D.begin(), E = D.end(); I != E; ++I) { + + // Only interested in definitions. + const FunctionDecl *func = llvm::dyn_cast<FunctionDecl>(*I); + if (func && func->hasBody()) { + Bodies.insert(std::make_pair(func->getName(), func->getBody())); + } + } + return true; +} diff --git a/lib/StaticAnalyzer/Frontend/ModelInjector.cpp b/lib/StaticAnalyzer/Frontend/ModelInjector.cpp new file mode 100644 index 0000000..63bb1e2 --- /dev/null +++ b/lib/StaticAnalyzer/Frontend/ModelInjector.cpp @@ -0,0 +1,117 @@ +//===-- ModelInjector.cpp ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ModelInjector.h" +#include "clang/AST/Decl.h" +#include "clang/Basic/IdentifierTable.h" +#include "clang/Frontend/ASTUnit.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/FrontendAction.h" +#include "clang/Lex/Preprocessor.h" +#include "clang/Serialization/ASTReader.h" +#include "clang/StaticAnalyzer/Frontend/FrontendActions.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/Support/CrashRecoveryContext.h" +#include "llvm/Support/FileSystem.h" +#include <string> +#include <utility> + +using namespace clang; +using namespace ento; + +ModelInjector::ModelInjector(CompilerInstance &CI) : CI(CI) {} + +Stmt *ModelInjector::getBody(const FunctionDecl *D) { + onBodySynthesis(D); + return Bodies[D->getName()]; +} + +Stmt *ModelInjector::getBody(const ObjCMethodDecl *D) { + onBodySynthesis(D); + return Bodies[D->getName()]; +} + +void ModelInjector::onBodySynthesis(const NamedDecl *D) { + + // FIXME: what about overloads? Declarations can be used as keys but what + // about file name index? Mangled names may not be suitable for that either. + if (Bodies.count(D->getName()) != 0) + return; + + SourceManager &SM = CI.getSourceManager(); + FileID mainFileID = SM.getMainFileID(); + + AnalyzerOptionsRef analyzerOpts = CI.getAnalyzerOpts(); + llvm::StringRef modelPath = analyzerOpts->Config["model-path"]; + + llvm::SmallString<128> fileName; + + if (!modelPath.empty()) + fileName = + llvm::StringRef(modelPath.str() + "/" + D->getName().str() + ".model"); + else + fileName = llvm::StringRef(D->getName().str() + ".model"); + + if (!llvm::sys::fs::exists(fileName.str())) { + Bodies[D->getName()] = nullptr; + return; + } + + IntrusiveRefCntPtr<CompilerInvocation> Invocation( + new CompilerInvocation(CI.getInvocation())); + + FrontendOptions &FrontendOpts = Invocation->getFrontendOpts(); + InputKind IK = IK_CXX; // FIXME + FrontendOpts.Inputs.clear(); + FrontendOpts.Inputs.push_back(FrontendInputFile(fileName, IK)); + FrontendOpts.DisableFree = true; + + Invocation->getDiagnosticOpts().VerifyDiagnostics = 0; + + // Modules are parsed by a separate CompilerInstance, so this code mimics that + // behavior for models + CompilerInstance Instance; + Instance.setInvocation(&*Invocation); + Instance.createDiagnostics( + new ForwardingDiagnosticConsumer(CI.getDiagnosticClient()), + /*ShouldOwnClient=*/true); + + Instance.getDiagnostics().setSourceManager(&SM); + + Instance.setVirtualFileSystem(&CI.getVirtualFileSystem()); + + // The instance wants to take ownership, however DisableFree frontend option + // is set to true to avoid double free issues + Instance.setFileManager(&CI.getFileManager()); + Instance.setSourceManager(&SM); + Instance.setPreprocessor(&CI.getPreprocessor()); + Instance.setASTContext(&CI.getASTContext()); + + Instance.getPreprocessor().InitializeForModelFile(); + + ParseModelFileAction parseModelFile(Bodies); + + const unsigned ThreadStackSize = 8 << 20; + llvm::CrashRecoveryContext CRC; + + CRC.RunSafelyOnThread([&]() { Instance.ExecuteAction(parseModelFile); }, + ThreadStackSize); + + Instance.getPreprocessor().FinalizeForModelFile(); + + Instance.resetAndLeakSourceManager(); + Instance.resetAndLeakFileManager(); + Instance.resetAndLeakPreprocessor(); + + // The preprocessor enters to the main file id when parsing is started, so + // the main file id is changed to the model file during parsing and it needs + // to be reseted to the former main file id after parsing of the model file + // is done. + SM.setMainFileID(mainFileID); +} diff --git a/lib/StaticAnalyzer/Frontend/ModelInjector.h b/lib/StaticAnalyzer/Frontend/ModelInjector.h new file mode 100644 index 0000000..fd24e32 --- /dev/null +++ b/lib/StaticAnalyzer/Frontend/ModelInjector.h @@ -0,0 +1,74 @@ +//===-- ModelInjector.h -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// \brief This file defines the clang::ento::ModelInjector class which implements the +/// clang::CodeInjector interface. This class is responsible for injecting +/// function definitions that were synthesized from model files. +/// +/// Model files allow definitions of functions to be lazily constituted for functions +/// which lack bodies in the original source code. This allows the analyzer +/// to more precisely analyze code that calls such functions, analyzing the +/// artificial definitions (which typically approximate the semantics of the +/// called function) when called by client code. These definitions are +/// reconstituted lazily, on-demand, by the static analyzer engine. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_SA_FRONTEND_MODELINJECTOR_H +#define LLVM_CLANG_SA_FRONTEND_MODELINJECTOR_H + +#include "clang/Analysis/CodeInjector.h" +#include "llvm/ADT/IntrusiveRefCntPtr.h" +#include "llvm/ADT/StringMap.h" +#include <map> +#include <memory> +#include <vector> + +namespace clang { + +class CompilerInstance; +class ASTUnit; +class ASTReader; +class NamedDecl; +class Module; + +namespace ento { +class ModelInjector : public CodeInjector { +public: + ModelInjector(CompilerInstance &CI); + Stmt *getBody(const FunctionDecl *D); + Stmt *getBody(const ObjCMethodDecl *D); + +private: + /// \brief Synthesize a body for a declaration + /// + /// This method first looks up the appropriate model file based on the + /// model-path configuration option and the name of the declaration that is + /// looked up. If no model were synthesized yet for a function with that name + /// it will create a new compiler instance to parse the model file using the + /// ASTContext, Preprocessor, SourceManager of the original compiler instance. + /// The former resources are shared between the two compiler instance, so the + /// newly created instance have to "leak" these objects, since they are owned + /// by the original instance. + /// + /// The model-path should be either an absolute path or relative to the + /// working directory of the compiler. + void onBodySynthesis(const NamedDecl *D); + + CompilerInstance &CI; + + // FIXME: double memoization is redundant, with memoization both here and in + // BodyFarm. + llvm::StringMap<Stmt *> Bodies; +}; +} +} + +#endif |