diff options
Diffstat (limited to 'lib/StaticAnalyzer/Core')
50 files changed, 688 insertions, 2739 deletions
diff --git a/lib/StaticAnalyzer/Core/APSIntType.cpp b/lib/StaticAnalyzer/Core/APSIntType.cpp index c7e9526821..a1de10c89e 100644 --- a/lib/StaticAnalyzer/Core/APSIntType.cpp +++ b/lib/StaticAnalyzer/Core/APSIntType.cpp @@ -1,9 +1,8 @@ //===--- APSIntType.cpp - Simple record of the type of APSInts ------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// diff --git a/lib/StaticAnalyzer/Core/AnalysisManager.cpp b/lib/StaticAnalyzer/Core/AnalysisManager.cpp index 7fb1c09ca0..2e69c2c43b 100644 --- a/lib/StaticAnalyzer/Core/AnalysisManager.cpp +++ b/lib/StaticAnalyzer/Core/AnalysisManager.cpp @@ -1,9 +1,8 @@ //===-- AnalysisManager.cpp -------------------------------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// diff --git a/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp b/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp index 0588c2bd3d..893c72190e 100644 --- a/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp +++ b/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp @@ -1,9 +1,8 @@ //===- AnalyzerOptions.cpp - Analysis Engine Options ----------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -34,7 +33,7 @@ std::vector<StringRef> AnalyzerOptions::getRegisteredCheckers(bool IncludeExperimental /* = false */) { static const StringRef StaticAnalyzerChecks[] = { #define GET_CHECKERS -#define CHECKER(FULLNAME, CLASS, HELPTEXT, DOC_URI) \ +#define CHECKER(FULLNAME, CLASS, HELPTEXT, DOC_URI, IS_HIDDEN) \ FULLNAME, #include "clang/StaticAnalyzer/Checkers/Checkers.inc" #undef CHECKER @@ -102,18 +101,14 @@ AnalyzerOptions::mayInlineCXXMemberFunction( return *K >= Param; } -StringRef AnalyzerOptions::getCheckerStringOption(StringRef OptionName, +StringRef AnalyzerOptions::getCheckerStringOption(StringRef CheckerName, + StringRef OptionName, StringRef DefaultVal, - const CheckerBase *C, - bool SearchInParents) const { - assert(C); - // Search for a package option if the option for the checker is not specified - // and search in parents is enabled. - StringRef CheckerName = C->getTagDescription(); - + bool SearchInParents ) const { assert(!CheckerName.empty() && "Empty checker name! Make sure the checker object (including it's " "bases!) if fully initialized before calling this function!"); + ConfigTable::const_iterator E = Config.end(); do { ConfigTable::const_iterator I = @@ -128,29 +123,56 @@ StringRef AnalyzerOptions::getCheckerStringOption(StringRef OptionName, return DefaultVal; } -bool AnalyzerOptions::getCheckerBooleanOption(StringRef Name, bool DefaultVal, - const CheckerBase *C, - bool SearchInParents) const { +StringRef AnalyzerOptions::getCheckerStringOption(const ento::CheckerBase *C, + StringRef OptionName, + StringRef DefaultVal, + bool SearchInParents ) const { + return getCheckerStringOption( + C->getTagDescription(), OptionName, DefaultVal, SearchInParents); +} + +bool AnalyzerOptions::getCheckerBooleanOption(StringRef CheckerName, + StringRef OptionName, + bool DefaultVal, + bool SearchInParents ) const { // 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. - assert(C); return llvm::StringSwitch<bool>( - getCheckerStringOption(Name, DefaultVal ? "true" : "false", C, + getCheckerStringOption(CheckerName, OptionName, + DefaultVal ? "true" : "false", SearchInParents)) .Case("true", true) .Case("false", false) .Default(DefaultVal); } -int AnalyzerOptions::getCheckerIntegerOption(StringRef Name, int DefaultVal, - const CheckerBase *C, - bool SearchInParents) const { +bool AnalyzerOptions::getCheckerBooleanOption(const ento::CheckerBase *C, + StringRef OptionName, + bool DefaultVal, + bool SearchInParents ) const { + return getCheckerBooleanOption( + C->getTagDescription(), OptionName, DefaultVal, SearchInParents); +} + +int AnalyzerOptions::getCheckerIntegerOption(StringRef CheckerName, + StringRef OptionName, + int DefaultVal, + bool SearchInParents ) const { int Ret = DefaultVal; - bool HasFailed = getCheckerStringOption(Name, std::to_string(DefaultVal), C, + bool HasFailed = getCheckerStringOption(CheckerName, OptionName, + std::to_string(DefaultVal), SearchInParents) .getAsInteger(10, Ret); assert(!HasFailed && "analyzer-config option should be numeric"); (void)HasFailed; return Ret; } + +int AnalyzerOptions::getCheckerIntegerOption(const ento::CheckerBase *C, + StringRef OptionName, + int DefaultVal, + bool SearchInParents ) const { + return getCheckerIntegerOption( + C->getTagDescription(), OptionName, DefaultVal, SearchInParents); +} diff --git a/lib/StaticAnalyzer/Core/BasicValueFactory.cpp b/lib/StaticAnalyzer/Core/BasicValueFactory.cpp index d8ed6942de..9ae30b605a 100644 --- a/lib/StaticAnalyzer/Core/BasicValueFactory.cpp +++ b/lib/StaticAnalyzer/Core/BasicValueFactory.cpp @@ -1,9 +1,8 @@ //===- BasicValueFactory.cpp - Basic values for Path Sens analysis --------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // diff --git a/lib/StaticAnalyzer/Core/BlockCounter.cpp b/lib/StaticAnalyzer/Core/BlockCounter.cpp index 8c99bd8084..e7ac6f1cfa 100644 --- a/lib/StaticAnalyzer/Core/BlockCounter.cpp +++ b/lib/StaticAnalyzer/Core/BlockCounter.cpp @@ -1,9 +1,8 @@ //==- BlockCounter.h - ADT for counting block visits -------------*- C++ -*-// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // diff --git a/lib/StaticAnalyzer/Core/BugReporter.cpp b/lib/StaticAnalyzer/Core/BugReporter.cpp index fd7f532104..168050955f 100644 --- a/lib/StaticAnalyzer/Core/BugReporter.cpp +++ b/lib/StaticAnalyzer/Core/BugReporter.cpp @@ -1,9 +1,8 @@ //===- BugReporter.cpp - Generate PathDiagnostics for bugs ----------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -1247,7 +1246,7 @@ static void generatePathDiagnosticsForNode(const ExplodedNode *N, static std::unique_ptr<PathDiagnostic> generateEmptyDiagnosticForReport(BugReport *R, SourceManager &SM) { - BugType &BT = R->getBugType(); + const BugType &BT = R->getBugType(); return llvm::make_unique<PathDiagnostic>( R->getBugType().getCheckName(), R->getDeclWithIssue(), R->getBugType().getName(), R->getDescription(), @@ -1370,8 +1369,7 @@ static void addContextEdges(PathPieces &pieces, SourceManager &SM, break; // If the source is in the same context, we're already good. - if (std::find(SrcContexts.begin(), SrcContexts.end(), DstContext) != - SrcContexts.end()) + if (llvm::find(SrcContexts, DstContext) != SrcContexts.end()) break; // Update the subexpression node to point to the context edge. @@ -2613,6 +2611,7 @@ std::pair<BugReport*, std::unique_ptr<VisitorsDiagnosticsTy>> findValidReport( R->addVisitor(llvm::make_unique<NilReceiverBRVisitor>()); R->addVisitor(llvm::make_unique<ConditionBRVisitor>()); R->addVisitor(llvm::make_unique<CXXSelfAssignmentBRVisitor>()); + R->addVisitor(llvm::make_unique<TagVisitor>()); BugReporterContext BRC(Reporter, ErrorGraph.BackMap); @@ -2684,7 +2683,7 @@ GRBugReporter::generatePathDiagnostics( return Out; } -void BugReporter::Register(BugType *BT) { +void BugReporter::Register(const BugType *BT) { BugTypes = F.add(BugTypes, BT); } @@ -2718,7 +2717,7 @@ void BugReporter::emitReport(std::unique_ptr<BugReport> R) { R->Profile(ID); // Lookup the equivance class. If there isn't one, create it. - BugType& BT = R->getBugType(); + const BugType& BT = R->getBugType(); Register(&BT); void *InsertPos; BugReportEquivClass* EQ = EQClasses.FindNodeOrInsertPos(ID, InsertPos); @@ -2836,7 +2835,7 @@ FindReportInEquivalenceClass(BugReportEquivClass& EQ, SmallVectorImpl<BugReport*> &bugReports) { BugReportEquivClass::iterator I = EQ.begin(), E = EQ.end(); assert(I != E); - BugType& BT = I->getBugType(); + const BugType& BT = I->getBugType(); // If we don't need to suppress any of the nodes because they are // post-dominated by a sink, simply add all the nodes in the equivalence class diff --git a/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp b/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp index da94b6eb21..0c48c430a2 100644 --- a/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp +++ b/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp @@ -1,9 +1,8 @@ //===- BugReporterVisitors.cpp - Helpers for reporting bugs ---------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -154,6 +153,32 @@ const Expr *bugreporter::getDerefExpr(const Stmt *S) { return E; } +/// Comparing internal representations of symbolic values (via +/// SVal::operator==()) is a valid way to check if the value was updated, +/// unless it's a LazyCompoundVal that may have a different internal +/// representation every time it is loaded from the state. In this function we +/// do an approximate comparison for lazy compound values, checking that they +/// are the immediate snapshots of the tracked region's bindings within the +/// node's respective states but not really checking that these snapshots +/// actually contain the same set of bindings. +static bool hasVisibleUpdate(const ExplodedNode *LeftNode, SVal LeftVal, + const ExplodedNode *RightNode, SVal RightVal) { + if (LeftVal == RightVal) + return true; + + const auto LLCV = LeftVal.getAs<nonloc::LazyCompoundVal>(); + if (!LLCV) + return false; + + const auto RLCV = RightVal.getAs<nonloc::LazyCompoundVal>(); + if (!RLCV) + return false; + + return LLCV->getRegion() == RLCV->getRegion() && + LLCV->getStore() == LeftNode->getState()->getStore() && + RLCV->getStore() == RightNode->getState()->getStore(); +} + //===----------------------------------------------------------------------===// // Definitions for bug reporter visitors. //===----------------------------------------------------------------------===// @@ -281,9 +306,14 @@ public: ID.AddPointer(RegionOfInterest); } + void *getTag() const { + static int Tag = 0; + return static_cast<void *>(&Tag); + } + std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N, BugReporterContext &BR, - BugReport &) override { + BugReport &R) override { const LocationContext *Ctx = N->getLocationContext(); const StackFrameContext *SCtx = Ctx->getStackFrame(); @@ -297,9 +327,6 @@ public: CallEventRef<> Call = BR.getStateManager().getCallEventManager().getCaller(SCtx, State); - if (SM.isInSystemHeader(Call->getDecl()->getSourceRange().getBegin())) - return nullptr; - // Region of interest corresponds to an IVar, exiting a method // which could have written into that IVar, but did not. if (const auto *MC = dyn_cast<ObjCMethodCall>(Call)) { @@ -308,9 +335,8 @@ public: if (RegionOfInterest->isSubRegionOf(SelfRegion) && potentiallyWritesIntoIvar(Call->getRuntimeDefinition().getDecl(), IvarR->getDecl())) - return notModifiedDiagnostics(Ctx, *CallExitLoc, Call, {}, SelfRegion, - "self", /*FirstIsReferenceType=*/false, - 1); + return maybeEmitNote(R, *Call, N, {}, SelfRegion, "self", + /*FirstIsReferenceType=*/false, 1); } } @@ -318,9 +344,8 @@ public: const MemRegion *ThisR = CCall->getCXXThisVal().getAsRegion(); if (RegionOfInterest->isSubRegionOf(ThisR) && !CCall->getDecl()->isImplicit()) - return notModifiedDiagnostics(Ctx, *CallExitLoc, Call, {}, ThisR, - "this", - /*FirstIsReferenceType=*/false, 1); + return maybeEmitNote(R, *Call, N, {}, ThisR, "this", + /*FirstIsReferenceType=*/false, 1); // Do not generate diagnostics for not modified parameters in // constructors. @@ -330,28 +355,26 @@ public: ArrayRef<ParmVarDecl *> parameters = getCallParameters(Call); for (unsigned I = 0; I < Call->getNumArgs() && I < parameters.size(); ++I) { const ParmVarDecl *PVD = parameters[I]; - SVal S = Call->getArgSVal(I); + SVal V = Call->getArgSVal(I); bool ParamIsReferenceType = PVD->getType()->isReferenceType(); std::string ParamName = PVD->getNameAsString(); int IndirectionLevel = 1; QualType T = PVD->getType(); - while (const MemRegion *R = S.getAsRegion()) { - if (RegionOfInterest->isSubRegionOf(R) && !isPointerToConst(T)) - return notModifiedDiagnostics(Ctx, *CallExitLoc, Call, {}, R, - ParamName, ParamIsReferenceType, - IndirectionLevel); + while (const MemRegion *MR = V.getAsRegion()) { + if (RegionOfInterest->isSubRegionOf(MR) && !isPointerToConst(T)) + return maybeEmitNote(R, *Call, N, {}, MR, ParamName, + ParamIsReferenceType, IndirectionLevel); QualType PT = T->getPointeeType(); if (PT.isNull() || PT->isVoidType()) break; if (const RecordDecl *RD = PT->getAsRecordDecl()) - if (auto P = findRegionOfInterestInRecord(RD, State, R)) - return notModifiedDiagnostics( - Ctx, *CallExitLoc, Call, *P, RegionOfInterest, ParamName, - ParamIsReferenceType, IndirectionLevel); + if (auto P = findRegionOfInterestInRecord(RD, State, MR)) + return maybeEmitNote(R, *Call, N, *P, RegionOfInterest, ParamName, + ParamIsReferenceType, IndirectionLevel); - S = State->getSVal(R, PT); + V = State->getSVal(MR, PT); T = PT; IndirectionLevel++; } @@ -521,22 +544,46 @@ private: Ty->getPointeeType().getCanonicalType().isConstQualified(); } - /// \return Diagnostics piece for region not modified in the current function. + /// Consume the information on the no-store stack frame in order to + /// either emit a note or suppress the report enirely. + /// \return Diagnostics piece for region not modified in the current function, + /// if it decides to emit one. std::shared_ptr<PathDiagnosticPiece> - notModifiedDiagnostics(const LocationContext *Ctx, CallExitBegin &CallExitLoc, - CallEventRef<> Call, const RegionVector &FieldChain, - const MemRegion *MatchedRegion, StringRef FirstElement, - bool FirstIsReferenceType, unsigned IndirectionLevel) { - - PathDiagnosticLocation L; - if (const ReturnStmt *RS = CallExitLoc.getReturnStmt()) { - L = PathDiagnosticLocation::createBegin(RS, SM, Ctx); - } else { - L = PathDiagnosticLocation( - Call->getRuntimeDefinition().getDecl()->getSourceRange().getEnd(), - SM); + maybeEmitNote(BugReport &R, const CallEvent &Call, const ExplodedNode *N, + const RegionVector &FieldChain, const MemRegion *MatchedRegion, + StringRef FirstElement, bool FirstIsReferenceType, + unsigned IndirectionLevel) { + // Optimistically suppress uninitialized value bugs that result + // from system headers having a chance to initialize the value + // but failing to do so. It's too unlikely a system header's fault. + // It's much more likely a situation in which the function has a failure + // mode that the user decided not to check. If we want to hunt such + // omitted checks, we should provide an explicit function-specific note + // describing the precondition under which the function isn't supposed to + // initialize its out-parameter, and additionally check that such + // precondition can actually be fulfilled on the current path. + if (Call.isInSystemHeader()) { + // We make an exception for system header functions that have no branches. + // Such functions unconditionally fail to initialize the variable. + // If they call other functions that have more paths within them, + // this suppression would still apply when we visit these inner functions. + // One common example of a standard function that doesn't ever initialize + // its out parameter is operator placement new; it's up to the follow-up + // constructor (if any) to initialize the memory. + if (!N->getStackFrame()->getCFG()->isLinear()) + R.markInvalid(getTag(), nullptr); + return nullptr; } + PathDiagnosticLocation L = + PathDiagnosticLocation::create(N->getLocation(), SM); + + // For now this shouldn't trigger, but once it does (as we add more + // functions to the body farm), we'll need to decide if these reports + // are worth suppressing as well. + if (!L.hasValidLocation()) + return nullptr; + SmallString<256> sbuf; llvm::raw_svector_ostream os(sbuf); os << "Returning without writing to '"; @@ -1188,7 +1235,7 @@ FindLastStoreBRVisitor::VisitNode(const ExplodedNode *Succ, if (Succ->getState()->getSVal(R) != V) return nullptr; - if (Pred->getState()->getSVal(R) == V) { + if (hasVisibleUpdate(Pred, Pred->getState()->getSVal(R), Succ, V)) { Optional<PostStore> PS = Succ->getLocationAs<PostStore>(); if (!PS || PS->getLocationValue() != R) return nullptr; @@ -1209,6 +1256,7 @@ FindLastStoreBRVisitor::VisitNode(const ExplodedNode *Succ, // UndefinedVal.) if (Optional<CallEnter> CE = Succ->getLocationAs<CallEnter>()) { if (const auto *VR = dyn_cast<VarRegion>(R)) { + const auto *Param = cast<ParmVarDecl>(VR->getDecl()); ProgramStateManager &StateMgr = BRC.getStateManager(); @@ -1799,15 +1847,6 @@ std::shared_ptr<PathDiagnosticPiece> ConditionBRVisitor::VisitNodeImpl(const ExplodedNode *N, BugReporterContext &BRC, BugReport &BR) { ProgramPoint progPoint = N->getLocation(); - ProgramStateRef CurrentState = N->getState(); - ProgramStateRef PrevState = N->getFirstPred()->getState(); - - // Compare the GDMs of the state, because that is where constraints - // are managed. Note that ensure that we only look at nodes that - // were generated by the analyzer engine proper, not checkers. - if (CurrentState->getGDM().getRoot() == - PrevState->getGDM().getRoot()) - return nullptr; // If an assumption was made on a branch, it should be caught // here by looking at the state transition. @@ -1876,6 +1915,8 @@ std::shared_ptr<PathDiagnosticPiece> ConditionBRVisitor::VisitTerminator( break; } + Cond = Cond->IgnoreParens(); + // However, when we encounter a logical operator as a branch condition, // then the condition is actually its RHS, because LHS would be // the condition for the logical operator terminator. @@ -1895,6 +1936,18 @@ std::shared_ptr<PathDiagnosticPiece> ConditionBRVisitor::VisitTrueTest(const Expr *Cond, bool tookTrue, BugReporterContext &BRC, BugReport &R, const ExplodedNode *N) { + ProgramStateRef CurrentState = N->getState(); + ProgramStateRef PreviousState = N->getFirstPred()->getState(); + const LocationContext *LCtx = N->getLocationContext(); + + // If the constraint information is changed between the current and the + // previous program state we assuming the newly seen constraint information. + // If we cannot evaluate the condition (and the constraints are the same) + // the analyzer has no information about the value and just assuming it. + if (BRC.getStateManager().haveEqualConstraints(CurrentState, PreviousState) && + CurrentState->getSVal(Cond, LCtx).isValid()) + return nullptr; + // These will be modified in code below, but we need to preserve the original // values in case we want to throw the generic message. const Expr *CondTmp = Cond; @@ -1930,7 +1983,6 @@ ConditionBRVisitor::VisitTrueTest(const Expr *Cond, bool tookTrue, // Condition too complex to explain? Just say something so that the user // knew we've made some path decision at this point. - const LocationContext *LCtx = N->getLocationContext(); PathDiagnosticLocation Loc(Cond, BRC.getSourceManager(), LCtx); if (!Loc.isValid() || !Loc.asLocation().isValid()) return nullptr; @@ -1949,43 +2001,22 @@ bool ConditionBRVisitor::patternMatch(const Expr *Ex, const Expr *OriginalExpr = Ex; Ex = Ex->IgnoreParenCasts(); - // Use heuristics to determine if Ex is a macro expending to a literal and - // if so, use the macro's name. - SourceLocation LocStart = Ex->getBeginLoc(); - SourceLocation LocEnd = Ex->getEndLoc(); - if (LocStart.isMacroID() && LocEnd.isMacroID() && - (isa<GNUNullExpr>(Ex) || - isa<ObjCBoolLiteralExpr>(Ex) || - isa<CXXBoolLiteralExpr>(Ex) || - isa<IntegerLiteral>(Ex) || - isa<FloatingLiteral>(Ex))) { - StringRef StartName = Lexer::getImmediateMacroNameForDiagnostics(LocStart, - BRC.getSourceManager(), BRC.getASTContext().getLangOpts()); - StringRef EndName = Lexer::getImmediateMacroNameForDiagnostics(LocEnd, - BRC.getSourceManager(), BRC.getASTContext().getLangOpts()); - bool beginAndEndAreTheSameMacro = StartName.equals(EndName); - - bool partOfParentMacro = false; - if (ParentEx->getBeginLoc().isMacroID()) { - StringRef PName = Lexer::getImmediateMacroNameForDiagnostics( - ParentEx->getBeginLoc(), BRC.getSourceManager(), - BRC.getASTContext().getLangOpts()); - partOfParentMacro = PName.equals(StartName); - } - - if (beginAndEndAreTheSameMacro && !partOfParentMacro ) { - // Get the location of the macro name as written by the caller. - SourceLocation Loc = LocStart; - while (LocStart.isMacroID()) { - Loc = LocStart; - LocStart = BRC.getSourceManager().getImmediateMacroCallerLoc(LocStart); + if (isa<GNUNullExpr>(Ex) || isa<ObjCBoolLiteralExpr>(Ex) || + isa<CXXBoolLiteralExpr>(Ex) || isa<IntegerLiteral>(Ex) || + isa<FloatingLiteral>(Ex)) { + // Use heuristics to determine if the expression is a macro + // expanding to a literal and if so, use the macro's name. + SourceLocation BeginLoc = OriginalExpr->getBeginLoc(); + SourceLocation EndLoc = OriginalExpr->getEndLoc(); + if (BeginLoc.isMacroID() && EndLoc.isMacroID()) { + SourceManager &SM = BRC.getSourceManager(); + const LangOptions &LO = BRC.getASTContext().getLangOpts(); + if (Lexer::isAtStartOfMacroExpansion(BeginLoc, SM, LO) && + Lexer::isAtEndOfMacroExpansion(EndLoc, SM, LO)) { + CharSourceRange R = Lexer::getAsCharRange({BeginLoc, EndLoc}, SM, LO); + Out << Lexer::getSourceText(R, SM, LO); + return false; } - StringRef MacroName = Lexer::getImmediateMacroNameForDiagnostics( - Loc, BRC.getSourceManager(), BRC.getASTContext().getLangOpts()); - - // Return the macro name. - Out << MacroName; - return false; } } @@ -2392,26 +2423,6 @@ CXXSelfAssignmentBRVisitor::VisitNode(const ExplodedNode *Succ, return std::move(Piece); } -std::shared_ptr<PathDiagnosticPiece> -TaintBugVisitor::VisitNode(const ExplodedNode *N, - BugReporterContext &BRC, BugReport &) { - - // Find the ExplodedNode where the taint was first introduced - if (!N->getState()->isTainted(V) || N->getFirstPred()->getState()->isTainted(V)) - return nullptr; - - const Stmt *S = PathDiagnosticLocation::getStmt(N); - if (!S) - return nullptr; - - const LocationContext *NCtx = N->getLocationContext(); - PathDiagnosticLocation L = - PathDiagnosticLocation::createBegin(S, BRC.getSourceManager(), NCtx); - if (!L.isValid() || !L.asLocation().isValid()) - return nullptr; - - return std::make_shared<PathDiagnosticEventPiece>(L, "Taint originated here"); -} FalsePositiveRefutationBRVisitor::FalsePositiveRefutationBRVisitor() : Constraints(ConstraintRangeTy::Factory().getEmptyMap()) {} @@ -2422,7 +2433,7 @@ void FalsePositiveRefutationBRVisitor::finalizeVisitor( VisitNode(EndPathNode, BRC, BR); // Create a refutation manager - SMTSolverRef RefutationSolver = CreateZ3Solver(); + llvm::SMTSolverRef RefutationSolver = llvm::CreateZ3Solver(); ASTContext &Ctx = BRC.getASTContext(); // Add constraints to the solver @@ -2430,7 +2441,7 @@ void FalsePositiveRefutationBRVisitor::finalizeVisitor( const SymbolRef Sym = I.first; auto RangeIt = I.second.begin(); - SMTExprRef Constraints = SMTConv::getRangeExpr( + llvm::SMTExprRef Constraints = SMTConv::getRangeExpr( RefutationSolver, Ctx, Sym, RangeIt->From(), RangeIt->To(), /*InRange=*/true); while ((++RangeIt) != I.second.end()) { @@ -2472,6 +2483,30 @@ FalsePositiveRefutationBRVisitor::VisitNode(const ExplodedNode *N, return nullptr; } +int NoteTag::Kind = 0; + +void TagVisitor::Profile(llvm::FoldingSetNodeID &ID) const { + static int Tag = 0; + ID.AddPointer(&Tag); +} + +std::shared_ptr<PathDiagnosticPiece> +TagVisitor::VisitNode(const ExplodedNode *N, BugReporterContext &BRC, + BugReport &R) { + ProgramPoint PP = N->getLocation(); + const NoteTag *T = dyn_cast_or_null<NoteTag>(PP.getTag()); + if (!T) + return nullptr; + + if (Optional<std::string> Msg = T->generateMessage(BRC, R)) { + PathDiagnosticLocation Loc = + PathDiagnosticLocation::create(PP, BRC.getSourceManager()); + return std::make_shared<PathDiagnosticEventPiece>(Loc, *Msg); + } + + return nullptr; +} + void FalsePositiveRefutationBRVisitor::Profile( llvm::FoldingSetNodeID &ID) const { static int Tag = 0; diff --git a/lib/StaticAnalyzer/Core/CMakeLists.txt b/lib/StaticAnalyzer/Core/CMakeLists.txt index 167f78af62..942aedd388 100644 --- a/lib/StaticAnalyzer/Core/CMakeLists.txt +++ b/lib/StaticAnalyzer/Core/CMakeLists.txt @@ -1,12 +1,5 @@ set(LLVM_LINK_COMPONENTS support) -# Link Z3 if the user wants to build it. -if(CLANG_ANALYZER_WITH_Z3) - set(Z3_LINK_FILES ${Z3_LIBRARIES}) -else() - set(Z3_LINK_FILES "") -endif() - add_clang_library(clangStaticAnalyzerCore APSIntType.cpp AnalysisManager.cpp @@ -43,18 +36,16 @@ add_clang_library(clangStaticAnalyzerCore RangeConstraintManager.cpp RangedConstraintManager.cpp RegionStore.cpp - RetainSummaryManager.cpp SarifDiagnostics.cpp SimpleConstraintManager.cpp SimpleSValBuilder.cpp + SMTConstraintManager.cpp Store.cpp SubEngine.cpp SValBuilder.cpp SVals.cpp SymbolManager.cpp - TaintManager.cpp WorkList.cpp - Z3ConstraintManager.cpp LINK_LIBS clangAST @@ -64,12 +55,5 @@ add_clang_library(clangStaticAnalyzerCore clangCrossTU clangLex clangRewrite - ${Z3_LINK_FILES} ) -if(CLANG_ANALYZER_WITH_Z3) - target_include_directories(clangStaticAnalyzerCore SYSTEM - PRIVATE - ${Z3_INCLUDE_DIR} - ) -endif() diff --git a/lib/StaticAnalyzer/Core/CallEvent.cpp b/lib/StaticAnalyzer/Core/CallEvent.cpp index 0e7f31502e..11dda7c3ac 100644 --- a/lib/StaticAnalyzer/Core/CallEvent.cpp +++ b/lib/StaticAnalyzer/Core/CallEvent.cpp @@ -1,9 +1,8 @@ //===- CallEvent.cpp - Wrapper for all function and method calls ----------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // diff --git a/lib/StaticAnalyzer/Core/Checker.cpp b/lib/StaticAnalyzer/Core/Checker.cpp index 72bfd84b40..f4e6f909d7 100644 --- a/lib/StaticAnalyzer/Core/Checker.cpp +++ b/lib/StaticAnalyzer/Core/Checker.cpp @@ -1,9 +1,8 @@ //== Checker.cpp - Registration mechanism for checkers -----------*- C++ -*--=// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // diff --git a/lib/StaticAnalyzer/Core/CheckerContext.cpp b/lib/StaticAnalyzer/Core/CheckerContext.cpp index 6cf931abbd..725ff1002e 100644 --- a/lib/StaticAnalyzer/Core/CheckerContext.cpp +++ b/lib/StaticAnalyzer/Core/CheckerContext.cpp @@ -1,9 +1,8 @@ //== CheckerContext.cpp - Context info for path-sensitive checkers-----------=// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // diff --git a/lib/StaticAnalyzer/Core/CheckerHelpers.cpp b/lib/StaticAnalyzer/Core/CheckerHelpers.cpp index e73a22ae39..34cdc9db69 100644 --- a/lib/StaticAnalyzer/Core/CheckerHelpers.cpp +++ b/lib/StaticAnalyzer/Core/CheckerHelpers.cpp @@ -1,9 +1,8 @@ //===---- CheckerHelpers.cpp - Helper functions for checkers ----*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // diff --git a/lib/StaticAnalyzer/Core/CheckerManager.cpp b/lib/StaticAnalyzer/Core/CheckerManager.cpp index 688c47e984..53d872021a 100644 --- a/lib/StaticAnalyzer/Core/CheckerManager.cpp +++ b/lib/StaticAnalyzer/Core/CheckerManager.cpp @@ -1,9 +1,8 @@ //===- CheckerManager.cpp - Static Analyzer Checker Manager ---------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -16,6 +15,7 @@ #include "clang/AST/Stmt.h" #include "clang/Analysis/ProgramPoint.h" #include "clang/Basic/LLVM.h" +#include "clang/Driver/DriverDiagnostic.h" #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" @@ -59,6 +59,15 @@ void CheckerManager::finishedCheckerRegistration() { #endif } +void CheckerManager::reportInvalidCheckerOptionValue( + const CheckerBase *C, StringRef OptionName, StringRef ExpectedValueDesc) { + + Context.getDiagnostics() + .Report(diag::err_analyzer_checker_option_invalid_input) + << (llvm::Twine() + C->getTagDescription() + ":" + OptionName).str() + << ExpectedValueDesc; +} + //===----------------------------------------------------------------------===// // Functions for running checkers for AST traversing.. //===----------------------------------------------------------------------===// diff --git a/lib/StaticAnalyzer/Core/CommonBugCategories.cpp b/lib/StaticAnalyzer/Core/CommonBugCategories.cpp index cdae3ef011..5450131438 100644 --- a/lib/StaticAnalyzer/Core/CommonBugCategories.cpp +++ b/lib/StaticAnalyzer/Core/CommonBugCategories.cpp @@ -1,9 +1,8 @@ //=--- CommonBugCategories.cpp - Provides common issue categories -*- C++ -*-=// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// diff --git a/lib/StaticAnalyzer/Core/ConstraintManager.cpp b/lib/StaticAnalyzer/Core/ConstraintManager.cpp index ef9c44c51b..d642c35302 100644 --- a/lib/StaticAnalyzer/Core/ConstraintManager.cpp +++ b/lib/StaticAnalyzer/Core/ConstraintManager.cpp @@ -1,9 +1,8 @@ //===- ConstraintManager.cpp - Constraints on symbolic values. ------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // diff --git a/lib/StaticAnalyzer/Core/CoreEngine.cpp b/lib/StaticAnalyzer/Core/CoreEngine.cpp index 196854cb09..cbe997669b 100644 --- a/lib/StaticAnalyzer/Core/CoreEngine.cpp +++ b/lib/StaticAnalyzer/Core/CoreEngine.cpp @@ -1,9 +1,8 @@ //===- CoreEngine.cpp - Path-Sensitive Dataflow Engine --------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // diff --git a/lib/StaticAnalyzer/Core/DynamicTypeMap.cpp b/lib/StaticAnalyzer/Core/DynamicTypeMap.cpp index da7854df1d..c5ee8ce4c2 100644 --- a/lib/StaticAnalyzer/Core/DynamicTypeMap.cpp +++ b/lib/StaticAnalyzer/Core/DynamicTypeMap.cpp @@ -1,9 +1,8 @@ //===- DynamicTypeMap.cpp - Dynamic Type Info related APIs ----------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // diff --git a/lib/StaticAnalyzer/Core/Environment.cpp b/lib/StaticAnalyzer/Core/Environment.cpp index b45f93b6dd..9d888ece17 100644 --- a/lib/StaticAnalyzer/Core/Environment.cpp +++ b/lib/StaticAnalyzer/Core/Environment.cpp @@ -1,9 +1,8 @@ //===- Environment.cpp - Map from Stmt* to Locations/Values ---------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // diff --git a/lib/StaticAnalyzer/Core/ExplodedGraph.cpp b/lib/StaticAnalyzer/Core/ExplodedGraph.cpp index d6bcbb96b5..c86b1436ba 100644 --- a/lib/StaticAnalyzer/Core/ExplodedGraph.cpp +++ b/lib/StaticAnalyzer/Core/ExplodedGraph.cpp @@ -1,9 +1,8 @@ //===- ExplodedGraph.cpp - Local, Path-Sens. "Exploded Graph" -------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // diff --git a/lib/StaticAnalyzer/Core/ExprEngine.cpp b/lib/StaticAnalyzer/Core/ExprEngine.cpp index 151eef56fe..ee9c0a42c1 100644 --- a/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -1,9 +1,8 @@ //===- ExprEngine.cpp - Path-Sensitive Expression-Level Dataflow ----------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -198,9 +197,13 @@ ExprEngine::ExprEngine(cross_tu::CrossTranslationUnitContext &CTU, mgr.getConstraintManagerCreator(), G.getAllocator(), this), SymMgr(StateMgr.getSymbolManager()), - svalBuilder(StateMgr.getSValBuilder()), ObjCNoRet(mgr.getASTContext()), + MRMgr(StateMgr.getRegionManager()), + svalBuilder(StateMgr.getSValBuilder()), + ObjCNoRet(mgr.getASTContext()), BR(mgr, *this), - VisitedCallees(VisitedCalleesIn), HowToInline(HowToInlineIn) { + VisitedCallees(VisitedCalleesIn), + HowToInline(HowToInlineIn) + { unsigned TrimInterval = mgr.options.GraphTrimInterval; if (TrimInterval != 0) { // Enable eager node reclamation when constructing the ExplodedGraph. @@ -2620,43 +2623,39 @@ void ExprEngine::VisitAtomicExpr(const AtomicExpr *AE, ExplodedNode *Pred, getCheckerManager().runCheckersForPostStmt(Dst, AfterInvalidateSet, AE, *this); } -// A value escapes in three possible cases: +// A value escapes in four possible cases: // (1) We are binding to something that is not a memory region. -// (2) We are binding to a MemrRegion that does not have stack storage. -// (3) We are binding to a MemRegion with stack storage that the store +// (2) We are binding to a MemRegion that does not have stack storage. +// (3) We are binding to a top-level parameter region with a non-trivial +// destructor. We won't see the destructor during analysis, but it's there. +// (4) We are binding to a MemRegion with stack storage that the store // does not understand. -ProgramStateRef ExprEngine::processPointerEscapedOnBind(ProgramStateRef State, - SVal Loc, - SVal Val, - const LocationContext *LCtx) { - // Are we storing to something that causes the value to "escape"? - bool escapes = true; - - // TODO: Move to StoreManager. - if (Optional<loc::MemRegionVal> regionLoc = Loc.getAs<loc::MemRegionVal>()) { - escapes = !regionLoc->getRegion()->hasStackStorage(); - - if (!escapes) { - // To test (3), generate a new state with the binding added. If it is - // the same state, then it escapes (since the store cannot represent - // the binding). - // Do this only if we know that the store is not supposed to generate the - // same state. - SVal StoredVal = State->getSVal(regionLoc->getRegion()); - if (StoredVal != Val) - escapes = (State == (State->bindLoc(*regionLoc, Val, LCtx))); - } - } - - // If our store can represent the binding and we aren't storing to something - // that doesn't have local storage then just return and have the simulation - // state continue as is. - if (!escapes) - return State; +ProgramStateRef +ExprEngine::processPointerEscapedOnBind(ProgramStateRef State, SVal Loc, + SVal Val, const LocationContext *LCtx) { + + // Cases (1) and (2). + const MemRegion *MR = Loc.getAsRegion(); + if (!MR || !MR->hasStackStorage()) + return escapeValue(State, Val, PSK_EscapeOnBind); + + // Case (3). + if (const auto *VR = dyn_cast<VarRegion>(MR->getBaseRegion())) + if (VR->hasStackParametersStorage() && VR->getStackFrame()->inTopFrame()) + if (const auto *RD = VR->getValueType()->getAsCXXRecordDecl()) + if (!RD->hasTrivialDestructor()) + return escapeValue(State, Val, PSK_EscapeOnBind); + + // Case (4): in order to test that, generate a new state with the binding + // added. If it is the same state, then it escapes (since the store cannot + // represent the binding). + // Do this only if we know that the store is not supposed to generate the + // same state. + SVal StoredVal = State->getSVal(MR); + if (StoredVal != Val) + if (State == (State->bindLoc(loc::MemRegionVal(MR), Val, LCtx))) + return escapeValue(State, Val, PSK_EscapeOnBind); - // Otherwise, find all symbols referenced by 'val' that we are tracking - // and stop tracking them. - State = escapeValue(State, Val, PSK_EscapeOnBind); return State; } diff --git a/lib/StaticAnalyzer/Core/ExprEngineC.cpp b/lib/StaticAnalyzer/Core/ExprEngineC.cpp index b980628878..df78b49130 100644 --- a/lib/StaticAnalyzer/Core/ExprEngineC.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngineC.cpp @@ -1,9 +1,8 @@ //=-- ExprEngineC.cpp - ExprEngine support for C expressions ----*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -416,7 +415,9 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex, case CK_IntToOCLSampler: case CK_LValueBitCast: case CK_FixedPointCast: - case CK_FixedPointToBoolean: { + case CK_FixedPointToBoolean: + case CK_FixedPointToIntegral: + case CK_IntegralToFixedPoint: { state = handleLValueBitCast(state, Ex, LCtx, T, ExTy, CastE, Bldr, Pred); continue; @@ -626,6 +627,21 @@ void ExprEngine::VisitDeclStmt(const DeclStmt *DS, ExplodedNode *Pred, void ExprEngine::VisitLogicalExpr(const BinaryOperator* B, ExplodedNode *Pred, ExplodedNodeSet &Dst) { + // This method acts upon CFG elements for logical operators && and || + // and attaches the value (true or false) to them as expressions. + // It doesn't produce any state splits. + // If we made it that far, we're past the point when we modeled the short + // circuit. It means that we should have precise knowledge about whether + // we've short-circuited. If we did, we already know the value we need to + // bind. If we didn't, the value of the RHS (casted to the boolean type) + // is the answer. + // Currently this method tries to figure out whether we've short-circuited + // by looking at the ExplodedGraph. This method is imperfect because there + // could inevitably have been merges that would have resulted in multiple + // potential path traversal histories. We bail out when we fail. + // Due to this ambiguity, a more reliable solution would have been to + // track the short circuit operation history path-sensitively until + // we evaluate the respective logical operator. assert(B->getOpcode() == BO_LAnd || B->getOpcode() == BO_LOr); @@ -647,10 +663,20 @@ void ExprEngine::VisitLogicalExpr(const BinaryOperator* B, ExplodedNode *Pred, ProgramPoint P = N->getLocation(); assert(P.getAs<PreStmt>()|| P.getAs<PreStmtPurgeDeadSymbols>()); (void) P; - assert(N->pred_size() == 1); + if (N->pred_size() != 1) { + // We failed to track back where we came from. + Bldr.generateNode(B, Pred, state); + return; + } N = *N->pred_begin(); } - assert(N->pred_size() == 1); + + if (N->pred_size() != 1) { + // We failed to track back where we came from. + Bldr.generateNode(B, Pred, state); + return; + } + N = *N->pred_begin(); BlockEdge BE = N->getLocation().castAs<BlockEdge>(); SVal X; @@ -703,7 +729,7 @@ void ExprEngine::VisitInitListExpr(const InitListExpr *IE, QualType T = getContext().getCanonicalType(IE->getType()); unsigned NumInitElements = IE->getNumInits(); - if (!IE->isGLValue() && + if (!IE->isGLValue() && !IE->isTransparent() && (T->isArrayType() || T->isRecordType() || T->isVectorType() || T->isAnyComplexType())) { llvm::ImmutableList<SVal> vals = getBasicVals().getEmptySValList(); diff --git a/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp b/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp index 6445b9df5a..aaab01f98c 100644 --- a/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp @@ -1,9 +1,8 @@ //===- ExprEngineCXX.cpp - ExprEngine support for C++ -----------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -604,6 +603,7 @@ void ExprEngine::VisitCXXDestructor(QualType ObjectType, ExplodedNode *Pred, ExplodedNodeSet &Dst, const EvalCallOptions &CallOpts) { + assert(S && "A destructor without a trigger!"); const LocationContext *LCtx = Pred->getLocationContext(); ProgramStateRef State = Pred->getState(); @@ -611,6 +611,19 @@ void ExprEngine::VisitCXXDestructor(QualType ObjectType, assert(RecordDecl && "Only CXXRecordDecls should have destructors"); const CXXDestructorDecl *DtorDecl = RecordDecl->getDestructor(); + // FIXME: There should always be a Decl, otherwise the destructor call + // shouldn't have been added to the CFG in the first place. + if (!DtorDecl) { + // Skip the invalid destructor. We cannot simply return because + // it would interrupt the analysis instead. + static SimpleProgramPointTag T("ExprEngine", "SkipInvalidDestructor"); + // FIXME: PostImplicitCall with a null decl may crash elsewhere anyway. + PostImplicitCall PP(/*Decl=*/nullptr, S->getEndLoc(), LCtx, &T); + NodeBuilder Bldr(Pred, Dst, *currBldrCtx); + Bldr.generateNode(PP, Pred->getState(), Pred); + return; + } + CallEventManager &CEMgr = getStateManager().getCallEventManager(); CallEventRef<CXXDestructorCall> Call = CEMgr.getCXXDestructorCall(DtorDecl, S, Dest, IsBaseDtor, State, LCtx); @@ -629,7 +642,6 @@ void ExprEngine::VisitCXXDestructor(QualType ObjectType, I != E; ++I) defaultEvalCall(Bldr, *I, *Call, CallOpts); - ExplodedNodeSet DstPostCall; getCheckerManager().runCheckersForPostCall(Dst, DstInvalidated, *Call, *this); } diff --git a/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp b/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp index 758195d8d9..3fe06aea63 100644 --- a/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp @@ -1,9 +1,8 @@ //=-- ExprEngineCallAndReturn.cpp - Support for call/return -----*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -365,6 +364,26 @@ void ExprEngine::processCallExit(ExplodedNode *CEBNode) { } } +bool ExprEngine::isSmall(AnalysisDeclContext *ADC) const { + // When there are no branches in the function, it means that there's no + // exponential complexity introduced by inlining such function. + // Such functions also don't trigger various fundamental problems + // with our inlining mechanism, such as the problem of + // inlined defensive checks. Hence isLinear(). + const CFG *Cfg = ADC->getCFG(); + return Cfg->isLinear() || Cfg->size() <= AMgr.options.AlwaysInlineSize; +} + +bool ExprEngine::isLarge(AnalysisDeclContext *ADC) const { + const CFG *Cfg = ADC->getCFG(); + return Cfg->size() >= AMgr.options.MinCFGSizeTreatFunctionsAsLarge; +} + +bool ExprEngine::isHuge(AnalysisDeclContext *ADC) const { + const CFG *Cfg = ADC->getCFG(); + return Cfg->getNumBlockIDs() > AMgr.options.MaxInlinableSize; +} + void ExprEngine::examineStackFrames(const Decl *D, const LocationContext *LCtx, bool &IsRecursive, unsigned &StackDepth) { IsRecursive = false; @@ -385,8 +404,7 @@ void ExprEngine::examineStackFrames(const Decl *D, const LocationContext *LCtx, // Do not count the small functions when determining the stack depth. AnalysisDeclContext *CalleeADC = AMgr.getAnalysisDeclContext(DI); - const CFG *CalleeCFG = CalleeADC->getCFG(); - if (CalleeCFG->getNumBlockIDs() > AMgr.options.AlwaysInlineSize) + if (!isSmall(CalleeADC)) ++StackDepth; } LCtx = LCtx->getParent(); @@ -833,8 +851,7 @@ static bool isCXXSharedPtrDtor(const FunctionDecl *FD) { /// This checks static properties of the function, such as its signature and /// CFG, to determine whether the analyzer should ever consider inlining it, /// in any context. -static bool mayInlineDecl(AnalysisManager &AMgr, - AnalysisDeclContext *CalleeADC) { +bool ExprEngine::mayInlineDecl(AnalysisDeclContext *CalleeADC) const { AnalyzerOptions &Opts = AMgr.getAnalyzerOptions(); // FIXME: Do not inline variadic calls. if (CallEvent::isVariadic(CalleeADC->getDecl())) @@ -879,7 +896,7 @@ static bool mayInlineDecl(AnalysisManager &AMgr, return false; // Do not inline large functions. - if (CalleeCFG->getNumBlockIDs() > Opts.MaxInlinableSize) + if (isHuge(CalleeADC)) return false; // It is possible that the live variables analysis cannot be @@ -919,7 +936,7 @@ bool ExprEngine::shouldInlineCall(const CallEvent &Call, const Decl *D, } else { // We haven't actually checked the static properties of this function yet. // Do that now, and record our decision in the function summaries. - if (mayInlineDecl(getAnalysisManager(), CalleeADC)) { + if (mayInlineDecl(CalleeADC)) { Engine.FunctionSummaries->markMayInline(D); } else { Engine.FunctionSummaries->markShouldNotInline(D); @@ -940,29 +957,23 @@ bool ExprEngine::shouldInlineCall(const CallEvent &Call, const Decl *D, return false; } - const CFG *CalleeCFG = CalleeADC->getCFG(); - // Do not inline if recursive or we've reached max stack frame count. bool IsRecursive = false; unsigned StackDepth = 0; examineStackFrames(D, Pred->getLocationContext(), IsRecursive, StackDepth); if ((StackDepth >= Opts.InlineMaxStackDepth) && - ((CalleeCFG->getNumBlockIDs() > Opts.AlwaysInlineSize) - || IsRecursive)) + (!isSmall(CalleeADC) || IsRecursive)) return false; // Do not inline large functions too many times. if ((Engine.FunctionSummaries->getNumTimesInlined(D) > Opts.MaxTimesInlineLarge) && - CalleeCFG->getNumBlockIDs() >= - Opts.MinCFGSizeTreatFunctionsAsLarge) { + isLarge(CalleeADC)) { NumReachedInlineCountMax++; return false; } - if (HowToInline == Inline_Minimal && - (CalleeCFG->getNumBlockIDs() > Opts.AlwaysInlineSize - || IsRecursive)) + if (HowToInline == Inline_Minimal && (!isSmall(CalleeADC) || IsRecursive)) return false; return true; diff --git a/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp b/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp index 6b8402f621..eb9a0be2e5 100644 --- a/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp @@ -1,9 +1,8 @@ //=-- ExprEngineObjC.cpp - ExprEngine support for Objective-C ---*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // diff --git a/lib/StaticAnalyzer/Core/FunctionSummary.cpp b/lib/StaticAnalyzer/Core/FunctionSummary.cpp index 94edd84d15..2b9a45133b 100644 --- a/lib/StaticAnalyzer/Core/FunctionSummary.cpp +++ b/lib/StaticAnalyzer/Core/FunctionSummary.cpp @@ -1,9 +1,8 @@ //===- FunctionSummary.cpp - Stores summaries of functions. ---------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // diff --git a/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp b/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp index fc82f11769..79aaae8cbb 100644 --- a/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp +++ b/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp @@ -1,9 +1,8 @@ //===- HTMLDiagnostics.cpp - HTML Diagnostics for Paths -------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -274,7 +273,7 @@ std::string HTMLDiagnostics::GenerateHTML(const PathDiagnostic& D, Rewriter &R, std::vector<FileID> FileIDs; for (auto I : path) { FileID FID = I->getLocation().asLocation().getExpansionLoc().getFileID(); - if (std::find(FileIDs.begin(), FileIDs.end(), FID) != FileIDs.end()) + if (llvm::is_contained(FileIDs, FID)) continue; FileIDs.push_back(FID); diff --git a/lib/StaticAnalyzer/Core/IssueHash.cpp b/lib/StaticAnalyzer/Core/IssueHash.cpp index 6c55c61dd3..e7497f3fbd 100644 --- a/lib/StaticAnalyzer/Core/IssueHash.cpp +++ b/lib/StaticAnalyzer/Core/IssueHash.cpp @@ -1,9 +1,8 @@ //===---------- IssueHash.cpp - Generate identification hashes --*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "clang/StaticAnalyzer/Core/IssueHash.h" @@ -121,7 +120,7 @@ static std::string GetEnclosingDeclContextSignature(const Decl *D) { return ""; } -static StringRef GetNthLineOfFile(llvm::MemoryBuffer *Buffer, int Line) { +static StringRef GetNthLineOfFile(const llvm::MemoryBuffer *Buffer, int Line) { if (!Buffer) return ""; @@ -145,7 +144,7 @@ static std::string NormalizeLine(const SourceManager &SM, FullSourceLoc &L, col++; SourceLocation StartOfLine = SM.translateLineCol(SM.getFileID(L), L.getExpansionLineNumber(), col); - llvm::MemoryBuffer *Buffer = + const llvm::MemoryBuffer *Buffer = SM.getBuffer(SM.getFileID(StartOfLine), StartOfLine); if (!Buffer) return {}; diff --git a/lib/StaticAnalyzer/Core/LoopUnrolling.cpp b/lib/StaticAnalyzer/Core/LoopUnrolling.cpp index da4574c615..ae9e073416 100644 --- a/lib/StaticAnalyzer/Core/LoopUnrolling.cpp +++ b/lib/StaticAnalyzer/Core/LoopUnrolling.cpp @@ -1,9 +1,8 @@ //===--- LoopUnrolling.cpp - Unroll loops -----------------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// /// diff --git a/lib/StaticAnalyzer/Core/LoopWidening.cpp b/lib/StaticAnalyzer/Core/LoopWidening.cpp index 8f6cb9a6b0..9a7b1a24b8 100644 --- a/lib/StaticAnalyzer/Core/LoopWidening.cpp +++ b/lib/StaticAnalyzer/Core/LoopWidening.cpp @@ -1,9 +1,8 @@ //===--- LoopWidening.cpp - Widen loops -------------------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// /// diff --git a/lib/StaticAnalyzer/Core/MemRegion.cpp b/lib/StaticAnalyzer/Core/MemRegion.cpp index 9a1d4d73c2..f763701af7 100644 --- a/lib/StaticAnalyzer/Core/MemRegion.cpp +++ b/lib/StaticAnalyzer/Core/MemRegion.cpp @@ -1,9 +1,8 @@ //===- MemRegion.cpp - Abstract memory regions for static analysis --------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -845,6 +844,7 @@ getStackOrCaptureRegionForDeclContext(const LocationContext *LC, const VarRegion* MemRegionManager::getVarRegion(const VarDecl *D, const LocationContext *LC) { + D = D->getCanonicalDecl(); const MemRegion *sReg = nullptr; if (D->hasGlobalStorage() && !D->isStaticLocal()) { @@ -931,6 +931,7 @@ const VarRegion* MemRegionManager::getVarRegion(const VarDecl *D, const VarRegion *MemRegionManager::getVarRegion(const VarDecl *D, const MemRegion *superR) { + D = D->getCanonicalDecl(); return getSubRegion<VarRegion>(D, superR); } @@ -1008,6 +1009,7 @@ MemRegionManager::getElementRegion(QualType elementType, NonLoc Idx, const FunctionCodeRegion * MemRegionManager::getFunctionCodeRegion(const NamedDecl *FD) { + // To think: should we canonicalize the declaration here? return getSubRegion<FunctionCodeRegion>(FD, getCodeRegion()); } diff --git a/lib/StaticAnalyzer/Core/PathDiagnostic.cpp b/lib/StaticAnalyzer/Core/PathDiagnostic.cpp index 3e93bb6a7c..cc1e7e1798 100644 --- a/lib/StaticAnalyzer/Core/PathDiagnostic.cpp +++ b/lib/StaticAnalyzer/Core/PathDiagnostic.cpp @@ -1,9 +1,8 @@ //===- PathDiagnostic.cpp - Path-Specific Diagnostic Handling -------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -572,6 +571,8 @@ static SourceLocation getValidSourceLocation(const Stmt* S, } while (!L.isValid()); } + // FIXME: Ironically, this assert actually fails in some cases. + //assert(L.isValid()); return L; } @@ -672,7 +673,15 @@ PathDiagnosticLocation::createConditionalColonLoc( PathDiagnosticLocation PathDiagnosticLocation::createMemberLoc(const MemberExpr *ME, const SourceManager &SM) { - return PathDiagnosticLocation(ME->getMemberLoc(), SM, SingleLocK); + + assert(ME->getMemberLoc().isValid() || ME->getBeginLoc().isValid()); + + // In some cases, getMemberLoc isn't valid -- in this case we'll return with + // some other related valid SourceLocation. + if (ME->getMemberLoc().isValid()) + return PathDiagnosticLocation(ME->getMemberLoc(), SM, SingleLocK); + + return PathDiagnosticLocation(ME->getBeginLoc(), SM, SingleLocK); } PathDiagnosticLocation @@ -735,6 +744,12 @@ PathDiagnosticLocation::create(const ProgramPoint& P, return getLocationForCaller(CEE->getCalleeContext(), CEE->getLocationContext(), SMng); + } else if (auto CEB = P.getAs<CallExitBegin>()) { + if (const ReturnStmt *RS = CEB->getReturnStmt()) + return PathDiagnosticLocation::createBegin(RS, SMng, + CEB->getLocationContext()); + return PathDiagnosticLocation( + CEB->getLocationContext()->getDecl()->getSourceRange().getEnd(), SMng); } else if (Optional<BlockEntrance> BE = P.getAs<BlockEntrance>()) { CFGElement BlockFront = BE->getBlock()->front(); if (auto StmtElt = BlockFront.getAs<CFGStmt>()) { diff --git a/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp b/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp index db4cf76578..c03bab0fe1 100644 --- a/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp +++ b/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp @@ -1,9 +1,8 @@ //===--- PlistDiagnostics.cpp - Plist Diagnostics for Paths -----*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -23,6 +22,7 @@ #include "clang/StaticAnalyzer/Core/IssueHash.h" #include "clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h" #include "llvm/ADT/Statistic.h" +#include "llvm/ADT/SmallPtrSet.h" #include "llvm/ADT/SmallVector.h" #include "llvm/Support/Casting.h" @@ -777,10 +777,20 @@ public: /// As we expand the last line, we'll immediately replace PRINT(str) with /// print(x). The information that both 'str' and 'x' refers to the same string /// is an information we have to forward, hence the argument \p PrevArgs. -static std::string getMacroNameAndPrintExpansion(TokenPrinter &Printer, - SourceLocation MacroLoc, - const Preprocessor &PP, - const MacroArgMap &PrevArgs); +/// +/// To avoid infinite recursion we maintain the already processed tokens in +/// a set. This is carried as a parameter through the recursive calls. The set +/// is extended with the currently processed token and after processing it, the +/// token is removed. If the token is already in the set, then recursion stops: +/// +/// #define f(y) x +/// #define x f(x) +static std::string getMacroNameAndPrintExpansion( + TokenPrinter &Printer, + SourceLocation MacroLoc, + const Preprocessor &PP, + const MacroArgMap &PrevArgs, + llvm::SmallPtrSet<IdentifierInfo *, 8> &AlreadyProcessedTokens); /// Retrieves the name of the macro and what it's arguments expand into /// at \p ExpanLoc. @@ -829,19 +839,38 @@ static ExpansionInfo getExpandedMacro(SourceLocation MacroLoc, llvm::SmallString<200> ExpansionBuf; llvm::raw_svector_ostream OS(ExpansionBuf); TokenPrinter Printer(OS, PP); + llvm::SmallPtrSet<IdentifierInfo*, 8> AlreadyProcessedTokens; + std::string MacroName = - getMacroNameAndPrintExpansion(Printer, MacroLoc, PP, MacroArgMap{}); + getMacroNameAndPrintExpansion(Printer, MacroLoc, PP, MacroArgMap{}, + AlreadyProcessedTokens); return { MacroName, OS.str() }; } -static std::string getMacroNameAndPrintExpansion(TokenPrinter &Printer, - SourceLocation MacroLoc, - const Preprocessor &PP, - const MacroArgMap &PrevArgs) { +static std::string getMacroNameAndPrintExpansion( + TokenPrinter &Printer, + SourceLocation MacroLoc, + const Preprocessor &PP, + const MacroArgMap &PrevArgs, + llvm::SmallPtrSet<IdentifierInfo *, 8> &AlreadyProcessedTokens) { const SourceManager &SM = PP.getSourceManager(); MacroNameAndArgs Info = getMacroNameAndArgs(SM.getExpansionLoc(MacroLoc), PP); + IdentifierInfo* IDInfo = PP.getIdentifierInfo(Info.Name); + + // TODO: If the macro definition contains another symbol then this function is + // called recursively. In case this symbol is the one being defined, it will + // be an infinite recursion which is stopped by this "if" statement. However, + // in this case we don't get the full expansion text in the Plist file. See + // the test file where "value" is expanded to "garbage_" instead of + // "garbage_value". + if (AlreadyProcessedTokens.find(IDInfo) != AlreadyProcessedTokens.end()) + return Info.Name; + AlreadyProcessedTokens.insert(IDInfo); + + if (!Info.MI) + return Info.Name; // Manually expand its arguments from the previous macro. Info.Args.expandFromPrevMacro(PrevArgs); @@ -863,14 +892,15 @@ static std::string getMacroNameAndPrintExpansion(TokenPrinter &Printer, // If this token is a macro that should be expanded inside the current // macro. - if (const MacroInfo *MI = - getMacroInfoForLocation(PP, SM, II, T.getLocation())) { - getMacroNameAndPrintExpansion(Printer, T.getLocation(), PP, Info.Args); + if (getMacroInfoForLocation(PP, SM, II, T.getLocation())) { + getMacroNameAndPrintExpansion(Printer, T.getLocation(), PP, Info.Args, + AlreadyProcessedTokens); // If this is a function-like macro, skip its arguments, as // getExpandedMacro() already printed them. If this is the case, let's // first jump to the '(' token. - if (MI->getNumParams() != 0) + auto N = std::next(It); + if (N != E && N->is(tok::l_paren)) It = getMatchingRParen(++It, E); continue; } @@ -897,8 +927,17 @@ static std::string getMacroNameAndPrintExpansion(TokenPrinter &Printer, } getMacroNameAndPrintExpansion(Printer, ArgIt->getLocation(), PP, - Info.Args); - if (MI->getNumParams() != 0) + Info.Args, AlreadyProcessedTokens); + // Peek the next token if it is a tok::l_paren. This way we can decide + // if this is the application or just a reference to a function maxro + // symbol: + // + // #define apply(f) ... + // #define func(x) ... + // apply(func) + // apply(func(42)) + auto N = std::next(ArgIt); + if (N != ArgEnd && N->is(tok::l_paren)) ArgIt = getMatchingRParen(++ArgIt, ArgEnd); } continue; @@ -909,6 +948,8 @@ static std::string getMacroNameAndPrintExpansion(TokenPrinter &Printer, Printer.printToken(T); } + AlreadyProcessedTokens.erase(IDInfo); + return Info.Name; } @@ -937,7 +978,14 @@ static MacroNameAndArgs getMacroNameAndArgs(SourceLocation ExpanLoc, assert(II && "Failed to acquire the IndetifierInfo for the macro!"); const MacroInfo *MI = getMacroInfoForLocation(PP, SM, II, ExpanLoc); - assert(MI && "The macro must've been defined at it's expansion location!"); + // assert(MI && "The macro must've been defined at it's expansion location!"); + // + // We should always be able to obtain the MacroInfo in a given TU, but if + // we're running the analyzer with CTU, the Preprocessor won't contain the + // directive history (or anything for that matter) from another TU. + // TODO: assert when we're not running with CTU. + if (!MI) + return { MacroName, MI, {} }; // Acquire the macro's arguments. // @@ -951,8 +999,16 @@ static MacroNameAndArgs getMacroNameAndArgs(SourceLocation ExpanLoc, return { MacroName, MI, {} }; RawLexer.LexFromRawLexer(TheTok); - assert(TheTok.is(tok::l_paren) && - "The token after the macro's identifier token should be '('!"); + // When this is a token which expands to another macro function then its + // parentheses are not at its expansion locaiton. For example: + // + // #define foo(x) int bar() { return x; } + // #define apply_zero(f) f(0) + // apply_zero(foo) + // ^ + // This is not a tok::l_paren, but foo is a function. + if (TheTok.isNot(tok::l_paren)) + return { MacroName, MI, {} }; MacroArgMap Args; diff --git a/lib/StaticAnalyzer/Core/PrettyStackTraceLocationContext.h b/lib/StaticAnalyzer/Core/PrettyStackTraceLocationContext.h index 4bb694819c..c79273dca8 100644 --- a/lib/StaticAnalyzer/Core/PrettyStackTraceLocationContext.h +++ b/lib/StaticAnalyzer/Core/PrettyStackTraceLocationContext.h @@ -1,9 +1,8 @@ //==- PrettyStackTraceLocationContext.h - show analysis backtrace --*- C++ -*-// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// diff --git a/lib/StaticAnalyzer/Core/ProgramState.cpp b/lib/StaticAnalyzer/Core/ProgramState.cpp index 2e2e2ec94f..04ed507055 100644 --- a/lib/StaticAnalyzer/Core/ProgramState.cpp +++ b/lib/StaticAnalyzer/Core/ProgramState.cpp @@ -1,9 +1,8 @@ //= ProgramState.cpp - Path-Sensitive "State" for tracking values --*- C++ -*--= // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -17,7 +16,6 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/TaintManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeMap.h" #include "llvm/Support/raw_ostream.h" @@ -459,9 +457,6 @@ void ProgramState::print(raw_ostream &Out, // Print out the tracked dynamic types. printDynamicTypeInfo(this, Out, NL, Sep); - // Print out tainted symbols. - printTaint(Out, NL); - // Print checker-specific data. Mgr.getOwningEngine().printState(Out, this, NL, Sep, LC); } @@ -475,22 +470,6 @@ LLVM_DUMP_METHOD void ProgramState::dump() const { print(llvm::errs()); } -void ProgramState::printTaint(raw_ostream &Out, - const char *NL) const { - TaintMapImpl TM = get<TaintMap>(); - - if (!TM.isEmpty()) - Out <<"Tainted symbols:" << NL; - - for (TaintMapImpl::iterator I = TM.begin(), E = TM.end(); I != E; ++I) { - Out << I->first << " : " << I->second << NL; - } -} - -void ProgramState::dumpTaint() const { - printTaint(llvm::errs()); -} - AnalysisManager& ProgramState::getAnalysisManager() const { return stateMgr->getOwningEngine().getAnalysisManager(); } @@ -658,166 +637,3 @@ bool ProgramState::scanReachableSymbols( } return true; } - -ProgramStateRef ProgramState::addTaint(const Stmt *S, - const LocationContext *LCtx, - TaintTagType Kind) const { - if (const Expr *E = dyn_cast_or_null<Expr>(S)) - S = E->IgnoreParens(); - - return addTaint(getSVal(S, LCtx), Kind); -} - -ProgramStateRef ProgramState::addTaint(SVal V, - TaintTagType Kind) const { - SymbolRef Sym = V.getAsSymbol(); - if (Sym) - return addTaint(Sym, Kind); - - // If the SVal represents a structure, try to mass-taint all values within the - // structure. For now it only works efficiently on lazy compound values that - // were conjured during a conservative evaluation of a function - either as - // return values of functions that return structures or arrays by value, or as - // values of structures or arrays passed into the function by reference, - // directly or through pointer aliasing. Such lazy compound values are - // characterized by having exactly one binding in their captured store within - // their parent region, which is a conjured symbol default-bound to the base - // region of the parent region. - if (auto LCV = V.getAs<nonloc::LazyCompoundVal>()) { - if (Optional<SVal> binding = getStateManager().StoreMgr->getDefaultBinding(*LCV)) { - if (SymbolRef Sym = binding->getAsSymbol()) - return addPartialTaint(Sym, LCV->getRegion(), Kind); - } - } - - const MemRegion *R = V.getAsRegion(); - return addTaint(R, Kind); -} - -ProgramStateRef ProgramState::addTaint(const MemRegion *R, - TaintTagType Kind) const { - if (const SymbolicRegion *SR = dyn_cast_or_null<SymbolicRegion>(R)) - return addTaint(SR->getSymbol(), Kind); - return this; -} - -ProgramStateRef ProgramState::addTaint(SymbolRef Sym, - TaintTagType Kind) const { - // If this is a symbol cast, remove the cast before adding the taint. Taint - // is cast agnostic. - while (const SymbolCast *SC = dyn_cast<SymbolCast>(Sym)) - Sym = SC->getOperand(); - - ProgramStateRef NewState = set<TaintMap>(Sym, Kind); - assert(NewState); - return NewState; -} - -ProgramStateRef ProgramState::addPartialTaint(SymbolRef ParentSym, - const SubRegion *SubRegion, - TaintTagType Kind) const { - // Ignore partial taint if the entire parent symbol is already tainted. - if (contains<TaintMap>(ParentSym) && *get<TaintMap>(ParentSym) == Kind) - return this; - - // Partial taint applies if only a portion of the symbol is tainted. - if (SubRegion == SubRegion->getBaseRegion()) - return addTaint(ParentSym, Kind); - - const TaintedSubRegions *SavedRegs = get<DerivedSymTaint>(ParentSym); - TaintedSubRegions Regs = - SavedRegs ? *SavedRegs : stateMgr->TSRFactory.getEmptyMap(); - - Regs = stateMgr->TSRFactory.add(Regs, SubRegion, Kind); - ProgramStateRef NewState = set<DerivedSymTaint>(ParentSym, Regs); - assert(NewState); - return NewState; -} - -bool ProgramState::isTainted(const Stmt *S, const LocationContext *LCtx, - TaintTagType Kind) const { - if (const Expr *E = dyn_cast_or_null<Expr>(S)) - S = E->IgnoreParens(); - - SVal val = getSVal(S, LCtx); - return isTainted(val, Kind); -} - -bool ProgramState::isTainted(SVal V, TaintTagType Kind) const { - if (const SymExpr *Sym = V.getAsSymExpr()) - return isTainted(Sym, Kind); - if (const MemRegion *Reg = V.getAsRegion()) - return isTainted(Reg, Kind); - return false; -} - -bool ProgramState::isTainted(const MemRegion *Reg, TaintTagType K) const { - if (!Reg) - return false; - - // Element region (array element) is tainted if either the base or the offset - // are tainted. - if (const ElementRegion *ER = dyn_cast<ElementRegion>(Reg)) - return isTainted(ER->getSuperRegion(), K) || isTainted(ER->getIndex(), K); - - if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(Reg)) - return isTainted(SR->getSymbol(), K); - - if (const SubRegion *ER = dyn_cast<SubRegion>(Reg)) - return isTainted(ER->getSuperRegion(), K); - - return false; -} - -bool ProgramState::isTainted(SymbolRef Sym, TaintTagType Kind) const { - if (!Sym) - return false; - - // Traverse all the symbols this symbol depends on to see if any are tainted. - for (SymExpr::symbol_iterator SI = Sym->symbol_begin(), SE =Sym->symbol_end(); - SI != SE; ++SI) { - if (!isa<SymbolData>(*SI)) - continue; - - if (const TaintTagType *Tag = get<TaintMap>(*SI)) { - if (*Tag == Kind) - return true; - } - - if (const SymbolDerived *SD = dyn_cast<SymbolDerived>(*SI)) { - // If this is a SymbolDerived with a tainted parent, it's also tainted. - if (isTainted(SD->getParentSymbol(), Kind)) - return true; - - // If this is a SymbolDerived with the same parent symbol as another - // tainted SymbolDerived and a region that's a sub-region of that tainted - // symbol, it's also tainted. - if (const TaintedSubRegions *Regs = - get<DerivedSymTaint>(SD->getParentSymbol())) { - const TypedValueRegion *R = SD->getRegion(); - for (auto I : *Regs) { - // FIXME: The logic to identify tainted regions could be more - // complete. For example, this would not currently identify - // overlapping fields in a union as tainted. To identify this we can - // check for overlapping/nested byte offsets. - if (Kind == I.second && R->isSubRegionOf(I.first)) - return true; - } - } - } - - // If memory region is tainted, data is also tainted. - if (const SymbolRegionValue *SRV = dyn_cast<SymbolRegionValue>(*SI)) { - if (isTainted(SRV->getRegion(), Kind)) - return true; - } - - // If this is a SymbolCast from a tainted value, it's also tainted. - if (const SymbolCast *SC = dyn_cast<SymbolCast>(*SI)) { - if (isTainted(SC->getOperand(), Kind)) - return true; - } - } - - return false; -} diff --git a/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp b/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp index d9b58d0f51..5c3eb0d66a 100644 --- a/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp +++ b/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp @@ -1,9 +1,8 @@ //== RangeConstraintManager.cpp - Manage range constraints.------*- C++ -*--==// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -174,6 +173,22 @@ RangeSet RangeSet::Intersect(BasicValueFactory &BV, Factory &F, return newRanges; } +// Returns a set containing the values in the receiving set, intersected with +// the range set passed as parameter. +RangeSet RangeSet::Intersect(BasicValueFactory &BV, Factory &F, + const RangeSet &Other) const { + PrimRangeSet newRanges = F.getEmptySet(); + + for (iterator i = Other.begin(), e = Other.end(); i != e; ++i) { + RangeSet newPiece = Intersect(BV, F, i->From(), i->To()); + for (iterator j = newPiece.begin(), ee = newPiece.end(); j != ee; ++j) { + newRanges = F.add(newRanges, *j); + } + } + + return newRanges; +} + // Turn all [A, B] ranges to [-B, -A]. Ranges [MIN, B] are turned to range set // [MIN, MIN] U [-B, MAX], when MIN and MAX are the minimal and the maximal // signed values of the type. @@ -231,6 +246,11 @@ public: // Implementation for interface from ConstraintManager. //===------------------------------------------------------------------===// + bool haveEqualConstraints(ProgramStateRef S1, + ProgramStateRef S2) const override { + return S1->get<ConstraintRange>() == S2->get<ConstraintRange>(); + } + bool canReasonAbout(SVal X) const override; ConditionTruthVal checkNull(ProgramStateRef State, SymbolRef Sym) override; @@ -457,14 +477,21 @@ static RangeSet applyBitwiseConstraints( RangeSet RangeConstraintManager::getRange(ProgramStateRef State, SymbolRef Sym) { - if (ConstraintRangeTy::data_type *V = State->get<ConstraintRange>(Sym)) - return *V; - - BasicValueFactory &BV = getBasicVals(); + ConstraintRangeTy::data_type *V = State->get<ConstraintRange>(Sym); // If Sym is a difference of symbols A - B, then maybe we have range set // stored for B - A. - if (const RangeSet *R = getRangeForMinusSymbol(State, Sym)) + BasicValueFactory &BV = getBasicVals(); + const RangeSet *R = getRangeForMinusSymbol(State, Sym); + + // If we have range set stored for both A - B and B - A then calculate the + // effective range set by intersecting the range set for A - B and the + // negated range set of B - A. + if (V && R) + return V->Intersect(BV, F, R->Negate(BV, F)); + if (V) + return *V; + if (R) return R->Negate(BV, F); // Lazily generate a new RangeSet representing all possible values for the diff --git a/lib/StaticAnalyzer/Core/RangedConstraintManager.cpp b/lib/StaticAnalyzer/Core/RangedConstraintManager.cpp index 146dc20ad0..4748c106eb 100644 --- a/lib/StaticAnalyzer/Core/RangedConstraintManager.cpp +++ b/lib/StaticAnalyzer/Core/RangedConstraintManager.cpp @@ -1,9 +1,8 @@ //== RangedConstraintManager.cpp --------------------------------*- C++ -*--==// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // diff --git a/lib/StaticAnalyzer/Core/RegionStore.cpp b/lib/StaticAnalyzer/Core/RegionStore.cpp index b2339be4f2..603be35bdb 100644 --- a/lib/StaticAnalyzer/Core/RegionStore.cpp +++ b/lib/StaticAnalyzer/Core/RegionStore.cpp @@ -1,9 +1,8 @@ //== RegionStore.cpp - Field-sensitive store model --------------*- C++ -*--==// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -131,10 +130,6 @@ namespace llvm { return os; } - template <typename T> struct isPodLike; - template <> struct isPodLike<BindingKey> { - static const bool value = true; - }; } // end llvm namespace #ifndef NDEBUG @@ -1660,7 +1655,7 @@ SVal RegionStoreManager::getBindingForElement(RegionBindingsConstRef B, const VarDecl *VD = VR->getDecl(); // Either the array or the array element has to be const. if (VD->getType().isConstQualified() || R->getElementType().isConstQualified()) { - if (const Expr *Init = VD->getInit()) { + if (const Expr *Init = VD->getAnyInitializer()) { if (const auto *InitList = dyn_cast<InitListExpr>(Init)) { // The array index has to be known. if (auto CI = R->getIndex().getAs<nonloc::ConcreteInt>()) { @@ -1750,7 +1745,7 @@ SVal RegionStoreManager::getBindingForField(RegionBindingsConstRef B, unsigned Index = FD->getFieldIndex(); // Either the record variable or the field has to be const qualified. if (RecordVarTy.isConstQualified() || Ty.isConstQualified()) - if (const Expr *Init = VD->getInit()) + if (const Expr *Init = VD->getAnyInitializer()) if (const auto *InitList = dyn_cast<InitListExpr>(Init)) { if (Index < InitList->getNumInits()) { if (const Expr *FieldInit = InitList->getInit(Index)) @@ -1932,7 +1927,10 @@ SVal RegionStoreManager::getBindingForVar(RegionBindingsConstRef B, const VarRegion *R) { // Check if the region has a binding. - if (const Optional<SVal> &V = B.getDirectBinding(R)) + if (Optional<SVal> V = B.getDirectBinding(R)) + return *V; + + if (Optional<SVal> V = B.getDefaultBinding(R)) return *V; // Lazily derive a value for the VarRegion. @@ -1945,7 +1943,7 @@ SVal RegionStoreManager::getBindingForVar(RegionBindingsConstRef B, // Is 'VD' declared constant? If so, retrieve the constant value. if (VD->getType().isConstQualified()) { - if (const Expr *Init = VD->getInit()) { + if (const Expr *Init = VD->getAnyInitializer()) { if (Optional<SVal> V = svalBuilder.getConstantVal(Init)) return *V; @@ -2339,12 +2337,64 @@ RegionBindingsRef RegionStoreManager::bindStruct(RegionBindingsConstRef B, if (V.isUnknown() || !V.getAs<nonloc::CompoundVal>()) return bindAggregate(B, R, UnknownVal()); + // The raw CompoundVal is essentially a symbolic InitListExpr: an (immutable) + // list of other values. It appears pretty much only when there's an actual + // initializer list expression in the program, and the analyzer tries to + // unwrap it as soon as possible. + // This code is where such unwrap happens: when the compound value is put into + // the object that it was supposed to initialize (it's an *initializer* list, + // after all), instead of binding the whole value to the whole object, we bind + // sub-values to sub-objects. Sub-values may themselves be compound values, + // and in this case the procedure becomes recursive. + // FIXME: The annoying part about compound values is that they don't carry + // any sort of information about which value corresponds to which sub-object. + // It's simply a list of values in the middle of nowhere; we expect to match + // them to sub-objects, essentially, "by index": first value binds to + // the first field, second value binds to the second field, etc. + // It would have been much safer to organize non-lazy compound values as + // a mapping from fields/bases to values. const nonloc::CompoundVal& CV = V.castAs<nonloc::CompoundVal>(); nonloc::CompoundVal::iterator VI = CV.begin(), VE = CV.end(); - RecordDecl::field_iterator FI, FE; RegionBindingsRef NewB(B); + // In C++17 aggregates may have base classes, handle those as well. + // They appear before fields in the initializer list / compound value. + if (const auto *CRD = dyn_cast<CXXRecordDecl>(RD)) { + // If the object was constructed with a constructor, its value is a + // LazyCompoundVal. If it's a raw CompoundVal, it means that we're + // performing aggregate initialization. The only exception from this + // rule is sending an Objective-C++ message that returns a C++ object + // to a nil receiver; in this case the semantics is to return a + // zero-initialized object even if it's a C++ object that doesn't have + // this sort of constructor; the CompoundVal is empty in this case. + assert((CRD->isAggregate() || (Ctx.getLangOpts().ObjC && VI == VE)) && + "Non-aggregates are constructed with a constructor!"); + + for (const auto &B : CRD->bases()) { + // (Multiple inheritance is fine though.) + assert(!B.isVirtual() && "Aggregates cannot have virtual base classes!"); + + if (VI == VE) + break; + + QualType BTy = B.getType(); + assert(BTy->isStructureOrClassType() && "Base classes must be classes!"); + + const CXXRecordDecl *BRD = BTy->getAsCXXRecordDecl(); + assert(BRD && "Base classes must be C++ classes!"); + + const CXXBaseObjectRegion *BR = + MRMgr.getCXXBaseObjectRegion(BRD, R, /*IsVirtual=*/false); + + NewB = bindStruct(NewB, BR, *VI); + + ++VI; + } + } + + RecordDecl::field_iterator FI, FE; + for (FI = RD->field_begin(), FE = RD->field_end(); FI != FE; ++FI) { if (VI == VE) @@ -2391,10 +2441,7 @@ RegionStoreManager::bindAggregate(RegionBindingsConstRef B, namespace { class RemoveDeadBindingsWorker : public ClusterAnalysis<RemoveDeadBindingsWorker> { - using ChildrenListTy = SmallVector<const SymbolDerived *, 4>; - using MapParentsToDerivedTy = llvm::DenseMap<SymbolRef, ChildrenListTy>; - - MapParentsToDerivedTy ParentsToDerived; + SmallVector<const SymbolicRegion *, 12> Postponed; SymbolReaper &SymReaper; const StackFrameContext *CurrentLCtx; @@ -2415,10 +2462,8 @@ public: bool AddToWorkList(const MemRegion *R); + bool UpdatePostponed(); void VisitBinding(SVal V); - -private: - void populateWorklistFromSymbol(SymbolRef s); }; } @@ -2438,11 +2483,10 @@ void RemoveDeadBindingsWorker::VisitAddedToCluster(const MemRegion *baseR, } if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(baseR)) { - if (SymReaper.isLive(SR->getSymbol())) { + if (SymReaper.isLive(SR->getSymbol())) AddToWorkList(SR, &C); - } else if (const auto *SD = dyn_cast<SymbolDerived>(SR->getSymbol())) { - ParentsToDerived[SD->getParentSymbol()].push_back(SD); - } + else + Postponed.push_back(SR); return; } @@ -2455,7 +2499,7 @@ void RemoveDeadBindingsWorker::VisitAddedToCluster(const MemRegion *baseR, // CXXThisRegion in the current or parent location context is live. if (const CXXThisRegion *TR = dyn_cast<CXXThisRegion>(baseR)) { const auto *StackReg = - cast<StackArgumentsSpaceRegion>(TR->getSuperRegion()); + cast<StackArgumentsSpaceRegion>(TR->getSuperRegion()); const StackFrameContext *RegCtx = StackReg->getStackFrame(); if (CurrentLCtx && (RegCtx == CurrentLCtx || RegCtx->isParentOf(CurrentLCtx))) @@ -2499,15 +2543,6 @@ void RemoveDeadBindingsWorker::VisitBinding(SVal V) { // If V is a region, then add it to the worklist. if (const MemRegion *R = V.getAsRegion()) { AddToWorkList(R); - - if (const auto *TVR = dyn_cast<TypedValueRegion>(R)) { - DefinedOrUnknownSVal RVS = - RM.getSValBuilder().getRegionValueSymbolVal(TVR); - if (const MemRegion *SR = RVS.getAsRegion()) { - AddToWorkList(SR); - } - } - SymReaper.markLive(R); // All regions captured by a block are also live. @@ -2521,30 +2556,25 @@ void RemoveDeadBindingsWorker::VisitBinding(SVal V) { // Update the set of live symbols. - for (auto SI = V.symbol_begin(), SE = V.symbol_end(); SI != SE; ++SI) { - populateWorklistFromSymbol(*SI); - - for (const auto *SD : ParentsToDerived[*SI]) - populateWorklistFromSymbol(SD); - + for (auto SI = V.symbol_begin(), SE = V.symbol_end(); SI!=SE; ++SI) SymReaper.markLive(*SI); - } } -void RemoveDeadBindingsWorker::populateWorklistFromSymbol(SymbolRef S) { - if (const auto *SD = dyn_cast<SymbolData>(S)) { - if (Loc::isLocType(SD->getType()) && !SymReaper.isLive(SD)) { - const SymbolicRegion *SR = RM.getRegionManager().getSymbolicRegion(SD); +bool RemoveDeadBindingsWorker::UpdatePostponed() { + // See if any postponed SymbolicRegions are actually live now, after + // having done a scan. + bool Changed = false; - if (B.contains(SR)) - AddToWorkList(SR); - - const SymbolicRegion *SHR = - RM.getRegionManager().getSymbolicHeapRegion(SD); - if (B.contains(SHR)) - AddToWorkList(SHR); + for (auto I = Postponed.begin(), E = Postponed.end(); I != E; ++I) { + if (const SymbolicRegion *SR = *I) { + if (SymReaper.isLive(SR->getSymbol())) { + Changed |= AddToWorkList(SR); + *I = nullptr; + } } } + + return Changed; } StoreRef RegionStoreManager::removeDeadBindings(Store store, @@ -2560,7 +2590,7 @@ StoreRef RegionStoreManager::removeDeadBindings(Store store, W.AddToWorkList(*I); } - W.RunWorkList(); + do W.RunWorkList(); while (W.UpdatePostponed()); // We have now scanned the store, marking reachable regions and symbols // as live. We now remove all the regions that are dead from the store diff --git a/lib/StaticAnalyzer/Core/RetainSummaryManager.cpp b/lib/StaticAnalyzer/Core/RetainSummaryManager.cpp deleted file mode 100644 index 2e40cc3338..0000000000 --- a/lib/StaticAnalyzer/Core/RetainSummaryManager.cpp +++ /dev/null @@ -1,1229 +0,0 @@ -//== RetainSummaryManager.cpp - Summaries for reference counting --*- C++ -*--// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file defines summaries implementation for retain counting, which -// implements a reference count checker for Core Foundation, Cocoa -// and OSObject (on Mac OS X). -// -//===----------------------------------------------------------------------===// - -#include "clang/StaticAnalyzer/Core/RetainSummaryManager.h" -#include "clang/Analysis/DomainSpecific/CocoaConventions.h" -#include "clang/AST/Attr.h" -#include "clang/AST/DeclCXX.h" -#include "clang/AST/DeclObjC.h" -#include "clang/AST/ParentMap.h" -#include "clang/ASTMatchers/ASTMatchFinder.h" - -using namespace clang; -using namespace ento; - -template <class T> -constexpr static bool isOneOf() { - return false; -} - -/// Helper function to check whether the class is one of the -/// rest of varargs. -template <class T, class P, class... ToCompare> -constexpr static bool isOneOf() { - return std::is_same<T, P>::value || isOneOf<T, ToCompare...>(); -} - -namespace { - -/// Fake attribute class for RC* attributes. -struct GeneralizedReturnsRetainedAttr { - static bool classof(const Attr *A) { - if (auto AA = dyn_cast<AnnotateAttr>(A)) - return AA->getAnnotation() == "rc_ownership_returns_retained"; - return false; - } -}; - -struct GeneralizedReturnsNotRetainedAttr { - static bool classof(const Attr *A) { - if (auto AA = dyn_cast<AnnotateAttr>(A)) - return AA->getAnnotation() == "rc_ownership_returns_not_retained"; - return false; - } -}; - -struct GeneralizedConsumedAttr { - static bool classof(const Attr *A) { - if (auto AA = dyn_cast<AnnotateAttr>(A)) - return AA->getAnnotation() == "rc_ownership_consumed"; - return false; - } -}; - -} - -template <class T> -Optional<ObjKind> RetainSummaryManager::hasAnyEnabledAttrOf(const Decl *D, - QualType QT) { - ObjKind K; - if (isOneOf<T, CFConsumedAttr, CFReturnsRetainedAttr, - CFReturnsNotRetainedAttr>()) { - if (!TrackObjCAndCFObjects) - return None; - - K = ObjKind::CF; - } else if (isOneOf<T, NSConsumedAttr, NSConsumesSelfAttr, - NSReturnsAutoreleasedAttr, NSReturnsRetainedAttr, - NSReturnsNotRetainedAttr, NSConsumesSelfAttr>()) { - - if (!TrackObjCAndCFObjects) - return None; - - if (isOneOf<T, NSReturnsRetainedAttr, NSReturnsAutoreleasedAttr, - NSReturnsNotRetainedAttr>() && - !cocoa::isCocoaObjectRef(QT)) - return None; - K = ObjKind::ObjC; - } else if (isOneOf<T, OSConsumedAttr, OSConsumesThisAttr, - OSReturnsNotRetainedAttr, OSReturnsRetainedAttr, - OSReturnsRetainedOnZeroAttr, - OSReturnsRetainedOnNonZeroAttr>()) { - if (!TrackOSObjects) - return None; - K = ObjKind::OS; - } else if (isOneOf<T, GeneralizedReturnsNotRetainedAttr, - GeneralizedReturnsRetainedAttr, - GeneralizedConsumedAttr>()) { - K = ObjKind::Generalized; - } else { - llvm_unreachable("Unexpected attribute"); - } - if (D->hasAttr<T>()) - return K; - return None; -} - -template <class T1, class T2, class... Others> -Optional<ObjKind> RetainSummaryManager::hasAnyEnabledAttrOf(const Decl *D, - QualType QT) { - if (auto Out = hasAnyEnabledAttrOf<T1>(D, QT)) - return Out; - return hasAnyEnabledAttrOf<T2, Others...>(D, QT); -} - -const RetainSummary * -RetainSummaryManager::getPersistentSummary(const RetainSummary &OldSumm) { - // Unique "simple" summaries -- those without ArgEffects. - if (OldSumm.isSimple()) { - ::llvm::FoldingSetNodeID ID; - OldSumm.Profile(ID); - - void *Pos; - CachedSummaryNode *N = SimpleSummaries.FindNodeOrInsertPos(ID, Pos); - - if (!N) { - N = (CachedSummaryNode *) BPAlloc.Allocate<CachedSummaryNode>(); - new (N) CachedSummaryNode(OldSumm); - SimpleSummaries.InsertNode(N, Pos); - } - - return &N->getValue(); - } - - RetainSummary *Summ = (RetainSummary *) BPAlloc.Allocate<RetainSummary>(); - new (Summ) RetainSummary(OldSumm); - return Summ; -} - -static bool isSubclass(const Decl *D, - StringRef ClassName) { - using namespace ast_matchers; - DeclarationMatcher SubclassM = cxxRecordDecl(isSameOrDerivedFrom(ClassName)); - return !(match(SubclassM, *D, D->getASTContext()).empty()); -} - -static bool isOSObjectSubclass(const Decl *D) { - return isSubclass(D, "OSObject"); -} - -static bool isOSObjectDynamicCast(StringRef S) { - return S == "safeMetaCast"; -} - -static bool isOSIteratorSubclass(const Decl *D) { - return isSubclass(D, "OSIterator"); -} - -static bool hasRCAnnotation(const Decl *D, StringRef rcAnnotation) { - for (const auto *Ann : D->specific_attrs<AnnotateAttr>()) { - if (Ann->getAnnotation() == rcAnnotation) - return true; - } - return false; -} - -static bool isRetain(const FunctionDecl *FD, StringRef FName) { - return FName.startswith_lower("retain") || FName.endswith_lower("retain"); -} - -static bool isRelease(const FunctionDecl *FD, StringRef FName) { - return FName.startswith_lower("release") || FName.endswith_lower("release"); -} - -static bool isAutorelease(const FunctionDecl *FD, StringRef FName) { - return FName.startswith_lower("autorelease") || - FName.endswith_lower("autorelease"); -} - -static bool isMakeCollectable(StringRef FName) { - return FName.contains_lower("MakeCollectable"); -} - -/// A function is OSObject related if it is declared on a subclass -/// of OSObject, or any of the parameters is a subclass of an OSObject. -static bool isOSObjectRelated(const CXXMethodDecl *MD) { - if (isOSObjectSubclass(MD->getParent())) - return true; - - for (ParmVarDecl *Param : MD->parameters()) { - QualType PT = Param->getType()->getPointeeType(); - if (!PT.isNull()) - if (CXXRecordDecl *RD = PT->getAsCXXRecordDecl()) - if (isOSObjectSubclass(RD)) - return true; - } - - return false; -} - -const RetainSummary * -RetainSummaryManager::getSummaryForOSObject(const FunctionDecl *FD, - StringRef FName, QualType RetTy) { - if (RetTy->isPointerType()) { - const CXXRecordDecl *PD = RetTy->getPointeeType()->getAsCXXRecordDecl(); - if (PD && isOSObjectSubclass(PD)) { - if (const IdentifierInfo *II = FD->getIdentifier()) { - if (isOSObjectDynamicCast(II->getName())) - return getDefaultSummary(); - - // All objects returned with functions *not* starting with - // get, or iterators, are returned at +1. - if ((!II->getName().startswith("get") && - !II->getName().startswith("Get")) || - isOSIteratorSubclass(PD)) { - return getOSSummaryCreateRule(FD); - } else { - return getOSSummaryGetRule(FD); - } - } - } - } - - if (const auto *MD = dyn_cast<CXXMethodDecl>(FD)) { - const CXXRecordDecl *Parent = MD->getParent(); - if (TrackOSObjects && Parent && isOSObjectSubclass(Parent)) { - if (FName == "release") - return getOSSummaryReleaseRule(FD); - - if (FName == "retain") - return getOSSummaryRetainRule(FD); - - if (FName == "free") - return getOSSummaryFreeRule(FD); - - if (MD->getOverloadedOperator() == OO_New) - return getOSSummaryCreateRule(MD); - } - } - - return nullptr; -} - -const RetainSummary *RetainSummaryManager::getSummaryForObjCOrCFObject( - const FunctionDecl *FD, - StringRef FName, - QualType RetTy, - const FunctionType *FT, - bool &AllowAnnotations) { - - ArgEffects ScratchArgs(AF.getEmptyMap()); - - std::string RetTyName = RetTy.getAsString(); - if (FName == "pthread_create" || FName == "pthread_setspecific") { - // Part of: <rdar://problem/7299394> and <rdar://problem/11282706>. - // This will be addressed better with IPA. - return getPersistentStopSummary(); - } else if(FName == "NSMakeCollectable") { - // Handle: id NSMakeCollectable(CFTypeRef) - AllowAnnotations = false; - return RetTy->isObjCIdType() ? getUnarySummary(FT, DoNothing) - : getPersistentStopSummary(); - } else if (FName == "CMBufferQueueDequeueAndRetain" || - FName == "CMBufferQueueDequeueIfDataReadyAndRetain") { - // Part of: <rdar://problem/39390714>. - return getPersistentSummary(RetEffect::MakeOwned(ObjKind::CF), - ScratchArgs, - ArgEffect(DoNothing), - ArgEffect(DoNothing)); - } else if (FName == "CFPlugInInstanceCreate") { - return getPersistentSummary(RetEffect::MakeNoRet(), ScratchArgs); - } else if (FName == "IORegistryEntrySearchCFProperty" || - (RetTyName == "CFMutableDictionaryRef" && - (FName == "IOBSDNameMatching" || FName == "IOServiceMatching" || - FName == "IOServiceNameMatching" || - FName == "IORegistryEntryIDMatching" || - FName == "IOOpenFirmwarePathMatching"))) { - // Part of <rdar://problem/6961230>. (IOKit) - // This should be addressed using a API table. - return getPersistentSummary(RetEffect::MakeOwned(ObjKind::CF), ScratchArgs, - ArgEffect(DoNothing), ArgEffect(DoNothing)); - } else if (FName == "IOServiceGetMatchingService" || - FName == "IOServiceGetMatchingServices") { - // FIXES: <rdar://problem/6326900> - // This should be addressed using a API table. This strcmp is also - // a little gross, but there is no need to super optimize here. - ScratchArgs = AF.add(ScratchArgs, 1, ArgEffect(DecRef, ObjKind::CF)); - return getPersistentSummary(RetEffect::MakeNoRet(), - ScratchArgs, - ArgEffect(DoNothing), ArgEffect(DoNothing)); - } else if (FName == "IOServiceAddNotification" || - FName == "IOServiceAddMatchingNotification") { - // Part of <rdar://problem/6961230>. (IOKit) - // This should be addressed using a API table. - ScratchArgs = AF.add(ScratchArgs, 2, ArgEffect(DecRef, ObjKind::CF)); - return getPersistentSummary(RetEffect::MakeNoRet(), - ScratchArgs, - ArgEffect(DoNothing), ArgEffect(DoNothing)); - } else if (FName == "CVPixelBufferCreateWithBytes") { - // FIXES: <rdar://problem/7283567> - // Eventually this can be improved by recognizing that the pixel - // buffer passed to CVPixelBufferCreateWithBytes is released via - // a callback and doing full IPA to make sure this is done correctly. - // FIXME: This function has an out parameter that returns an - // allocated object. - ScratchArgs = AF.add(ScratchArgs, 7, ArgEffect(StopTracking)); - return getPersistentSummary(RetEffect::MakeNoRet(), - ScratchArgs, - ArgEffect(DoNothing), ArgEffect(DoNothing)); - } else if (FName == "CGBitmapContextCreateWithData") { - // FIXES: <rdar://problem/7358899> - // Eventually this can be improved by recognizing that 'releaseInfo' - // passed to CGBitmapContextCreateWithData is released via - // a callback and doing full IPA to make sure this is done correctly. - ScratchArgs = AF.add(ScratchArgs, 8, ArgEffect(ArgEffect(StopTracking))); - return getPersistentSummary(RetEffect::MakeOwned(ObjKind::CF), ScratchArgs, - ArgEffect(DoNothing), ArgEffect(DoNothing)); - } else if (FName == "CVPixelBufferCreateWithPlanarBytes") { - // FIXES: <rdar://problem/7283567> - // Eventually this can be improved by recognizing that the pixel - // buffer passed to CVPixelBufferCreateWithPlanarBytes is released - // via a callback and doing full IPA to make sure this is done - // correctly. - ScratchArgs = AF.add(ScratchArgs, 12, ArgEffect(StopTracking)); - return getPersistentSummary(RetEffect::MakeNoRet(), - ScratchArgs, - ArgEffect(DoNothing), ArgEffect(DoNothing)); - } else if (FName == "VTCompressionSessionEncodeFrame") { - // The context argument passed to VTCompressionSessionEncodeFrame() - // is passed to the callback specified when creating the session - // (e.g. with VTCompressionSessionCreate()) which can release it. - // To account for this possibility, conservatively stop tracking - // the context. - ScratchArgs = AF.add(ScratchArgs, 5, ArgEffect(StopTracking)); - return getPersistentSummary(RetEffect::MakeNoRet(), - ScratchArgs, - ArgEffect(DoNothing), ArgEffect(DoNothing)); - } else if (FName == "dispatch_set_context" || - FName == "xpc_connection_set_context") { - // <rdar://problem/11059275> - The analyzer currently doesn't have - // a good way to reason about the finalizer function for libdispatch. - // If we pass a context object that is memory managed, stop tracking it. - // <rdar://problem/13783514> - Same problem, but for XPC. - // FIXME: this hack should possibly go away once we can handle - // libdispatch and XPC finalizers. - ScratchArgs = AF.add(ScratchArgs, 1, ArgEffect(StopTracking)); - return getPersistentSummary(RetEffect::MakeNoRet(), - ScratchArgs, - ArgEffect(DoNothing), ArgEffect(DoNothing)); - } else if (FName.startswith("NSLog")) { - return getDoNothingSummary(); - } else if (FName.startswith("NS") && - (FName.find("Insert") != StringRef::npos)) { - // Whitelist NSXXInsertXX, for example NSMapInsertIfAbsent, since they can - // be deallocated by NSMapRemove. (radar://11152419) - ScratchArgs = AF.add(ScratchArgs, 1, ArgEffect(StopTracking)); - ScratchArgs = AF.add(ScratchArgs, 2, ArgEffect(StopTracking)); - return getPersistentSummary(RetEffect::MakeNoRet(), - ScratchArgs, ArgEffect(DoNothing), - ArgEffect(DoNothing)); - } - - if (RetTy->isPointerType()) { - - // For CoreFoundation ('CF') types. - if (cocoa::isRefType(RetTy, "CF", FName)) { - if (isRetain(FD, FName)) { - // CFRetain isn't supposed to be annotated. However, this may as - // well be a user-made "safe" CFRetain function that is incorrectly - // annotated as cf_returns_retained due to lack of better options. - // We want to ignore such annotation. - AllowAnnotations = false; - - return getUnarySummary(FT, IncRef); - } else if (isAutorelease(FD, FName)) { - // The headers use cf_consumed, but we can fully model CFAutorelease - // ourselves. - AllowAnnotations = false; - - return getUnarySummary(FT, Autorelease); - } else if (isMakeCollectable(FName)) { - AllowAnnotations = false; - return getUnarySummary(FT, DoNothing); - } else { - return getCFCreateGetRuleSummary(FD); - } - } - - // For CoreGraphics ('CG') and CoreVideo ('CV') types. - if (cocoa::isRefType(RetTy, "CG", FName) || - cocoa::isRefType(RetTy, "CV", FName)) { - if (isRetain(FD, FName)) - return getUnarySummary(FT, IncRef); - else - return getCFCreateGetRuleSummary(FD); - } - - // For all other CF-style types, use the Create/Get - // rule for summaries but don't support Retain functions - // with framework-specific prefixes. - if (coreFoundation::isCFObjectRef(RetTy)) { - return getCFCreateGetRuleSummary(FD); - } - - if (FD->hasAttr<CFAuditedTransferAttr>()) { - return getCFCreateGetRuleSummary(FD); - } - } - - // Check for release functions, the only kind of functions that we care - // about that don't return a pointer type. - if (FName.startswith("CG") || FName.startswith("CF")) { - // Test for 'CGCF'. - FName = FName.substr(FName.startswith("CGCF") ? 4 : 2); - - if (isRelease(FD, FName)) - return getUnarySummary(FT, DecRef); - else { - assert(ScratchArgs.isEmpty()); - // Remaining CoreFoundation and CoreGraphics functions. - // We use to assume that they all strictly followed the ownership idiom - // and that ownership cannot be transferred. While this is technically - // correct, many methods allow a tracked object to escape. For example: - // - // CFMutableDictionaryRef x = CFDictionaryCreateMutable(...); - // CFDictionaryAddValue(y, key, x); - // CFRelease(x); - // ... it is okay to use 'x' since 'y' has a reference to it - // - // We handle this and similar cases with the follow heuristic. If the - // function name contains "InsertValue", "SetValue", "AddValue", - // "AppendValue", or "SetAttribute", then we assume that arguments may - // "escape." This means that something else holds on to the object, - // allowing it be used even after its local retain count drops to 0. - ArgEffectKind E = - (StrInStrNoCase(FName, "InsertValue") != StringRef::npos || - StrInStrNoCase(FName, "AddValue") != StringRef::npos || - StrInStrNoCase(FName, "SetValue") != StringRef::npos || - StrInStrNoCase(FName, "AppendValue") != StringRef::npos || - StrInStrNoCase(FName, "SetAttribute") != StringRef::npos) - ? MayEscape - : DoNothing; - - return getPersistentSummary(RetEffect::MakeNoRet(), ScratchArgs, - ArgEffect(DoNothing), ArgEffect(E, ObjKind::CF)); - } - } - - return nullptr; -} - -const RetainSummary * -RetainSummaryManager::generateSummary(const FunctionDecl *FD, - bool &AllowAnnotations) { - // We generate "stop" summaries for implicitly defined functions. - if (FD->isImplicit()) - return getPersistentStopSummary(); - - const IdentifierInfo *II = FD->getIdentifier(); - - StringRef FName = II ? II->getName() : ""; - - // Strip away preceding '_'. Doing this here will effect all the checks - // down below. - FName = FName.substr(FName.find_first_not_of('_')); - - // Inspect the result type. Strip away any typedefs. - const auto *FT = FD->getType()->getAs<FunctionType>(); - QualType RetTy = FT->getReturnType(); - - if (TrackOSObjects) - if (const RetainSummary *S = getSummaryForOSObject(FD, FName, RetTy)) - return S; - - if (TrackObjCAndCFObjects) - if (const RetainSummary *S = - getSummaryForObjCOrCFObject(FD, FName, RetTy, FT, AllowAnnotations)) - return S; - - if (const auto *MD = dyn_cast<CXXMethodDecl>(FD)) - if (!(TrackOSObjects && isOSObjectRelated(MD))) - return getPersistentSummary(RetEffect::MakeNoRet(), - ArgEffects(AF.getEmptyMap()), - ArgEffect(DoNothing), - ArgEffect(StopTracking), - ArgEffect(DoNothing)); - - return getDefaultSummary(); -} - -const RetainSummary * -RetainSummaryManager::getFunctionSummary(const FunctionDecl *FD) { - // If we don't know what function we're calling, use our default summary. - if (!FD) - return getDefaultSummary(); - - // Look up a summary in our cache of FunctionDecls -> Summaries. - FuncSummariesTy::iterator I = FuncSummaries.find(FD); - if (I != FuncSummaries.end()) - return I->second; - - // No summary? Generate one. - bool AllowAnnotations = true; - const RetainSummary *S = generateSummary(FD, AllowAnnotations); - - // Annotations override defaults. - if (AllowAnnotations) - updateSummaryFromAnnotations(S, FD); - - FuncSummaries[FD] = S; - return S; -} - -//===----------------------------------------------------------------------===// -// Summary creation for functions (largely uses of Core Foundation). -//===----------------------------------------------------------------------===// - -static ArgEffect getStopTrackingHardEquivalent(ArgEffect E) { - switch (E.getKind()) { - case DoNothing: - case Autorelease: - case DecRefBridgedTransferred: - case IncRef: - case UnretainedOutParameter: - case RetainedOutParameter: - case RetainedOutParameterOnZero: - case RetainedOutParameterOnNonZero: - case MayEscape: - case StopTracking: - case StopTrackingHard: - return E.withKind(StopTrackingHard); - case DecRef: - case DecRefAndStopTrackingHard: - return E.withKind(DecRefAndStopTrackingHard); - case Dealloc: - return E.withKind(Dealloc); - } - - llvm_unreachable("Unknown ArgEffect kind"); -} - -void RetainSummaryManager::updateSummaryForCall(const RetainSummary *&S, - const CallEvent &Call) { - if (Call.hasNonZeroCallbackArg()) { - ArgEffect RecEffect = - getStopTrackingHardEquivalent(S->getReceiverEffect()); - ArgEffect DefEffect = - getStopTrackingHardEquivalent(S->getDefaultArgEffect()); - - ArgEffects ScratchArgs(AF.getEmptyMap()); - ArgEffects CustomArgEffects = S->getArgEffects(); - for (ArgEffects::iterator I = CustomArgEffects.begin(), - E = CustomArgEffects.end(); - I != E; ++I) { - ArgEffect Translated = getStopTrackingHardEquivalent(I->second); - if (Translated.getKind() != DefEffect.getKind()) - ScratchArgs = AF.add(ScratchArgs, I->first, Translated); - } - - RetEffect RE = RetEffect::MakeNoRetHard(); - - // Special cases where the callback argument CANNOT free the return value. - // This can generally only happen if we know that the callback will only be - // called when the return value is already being deallocated. - if (const SimpleFunctionCall *FC = dyn_cast<SimpleFunctionCall>(&Call)) { - if (IdentifierInfo *Name = FC->getDecl()->getIdentifier()) { - // When the CGBitmapContext is deallocated, the callback here will free - // the associated data buffer. - // The callback in dispatch_data_create frees the buffer, but not - // the data object. - if (Name->isStr("CGBitmapContextCreateWithData") || - Name->isStr("dispatch_data_create")) - RE = S->getRetEffect(); - } - } - - S = getPersistentSummary(RE, ScratchArgs, RecEffect, DefEffect); - } - - // Special case '[super init];' and '[self init];' - // - // Even though calling '[super init]' without assigning the result to self - // and checking if the parent returns 'nil' is a bad pattern, it is common. - // Additionally, our Self Init checker already warns about it. To avoid - // overwhelming the user with messages from both checkers, we model the case - // of '[super init]' in cases when it is not consumed by another expression - // as if the call preserves the value of 'self'; essentially, assuming it can - // never fail and return 'nil'. - // Note, we don't want to just stop tracking the value since we want the - // RetainCount checker to report leaks and use-after-free if SelfInit checker - // is turned off. - if (const ObjCMethodCall *MC = dyn_cast<ObjCMethodCall>(&Call)) { - if (MC->getMethodFamily() == OMF_init && MC->isReceiverSelfOrSuper()) { - - // Check if the message is not consumed, we know it will not be used in - // an assignment, ex: "self = [super init]". - const Expr *ME = MC->getOriginExpr(); - const LocationContext *LCtx = MC->getLocationContext(); - ParentMap &PM = LCtx->getAnalysisDeclContext()->getParentMap(); - if (!PM.isConsumedExpr(ME)) { - RetainSummaryTemplate ModifiableSummaryTemplate(S, *this); - ModifiableSummaryTemplate->setReceiverEffect(ArgEffect(DoNothing)); - ModifiableSummaryTemplate->setRetEffect(RetEffect::MakeNoRet()); - } - } - } -} - -const RetainSummary * -RetainSummaryManager::getSummary(const CallEvent &Call, - QualType ReceiverType) { - const RetainSummary *Summ; - switch (Call.getKind()) { - case CE_Function: - case CE_CXXMember: - case CE_CXXMemberOperator: - case CE_CXXConstructor: - case CE_CXXAllocator: - Summ = getFunctionSummary(cast_or_null<FunctionDecl>(Call.getDecl())); - break; - case CE_Block: - case CE_CXXDestructor: - // FIXME: These calls are currently unsupported. - return getPersistentStopSummary(); - case CE_ObjCMessage: { - const ObjCMethodCall &Msg = cast<ObjCMethodCall>(Call); - if (Msg.isInstanceMessage()) - Summ = getInstanceMethodSummary(Msg, ReceiverType); - else - Summ = getClassMethodSummary(Msg); - break; - } - } - - updateSummaryForCall(Summ, Call); - - assert(Summ && "Unknown call type?"); - return Summ; -} - - -const RetainSummary * -RetainSummaryManager::getCFCreateGetRuleSummary(const FunctionDecl *FD) { - if (coreFoundation::followsCreateRule(FD)) - return getCFSummaryCreateRule(FD); - - return getCFSummaryGetRule(FD); -} - -bool RetainSummaryManager::isTrustedReferenceCountImplementation( - const FunctionDecl *FD) { - return hasRCAnnotation(FD, "rc_ownership_trusted_implementation"); -} - -Optional<RetainSummaryManager::BehaviorSummary> -RetainSummaryManager::canEval(const CallExpr *CE, const FunctionDecl *FD, - bool &hasTrustedImplementationAnnotation) { - - IdentifierInfo *II = FD->getIdentifier(); - if (!II) - return None; - - StringRef FName = II->getName(); - FName = FName.substr(FName.find_first_not_of('_')); - - QualType ResultTy = CE->getCallReturnType(Ctx); - if (ResultTy->isObjCIdType()) { - if (II->isStr("NSMakeCollectable")) - return BehaviorSummary::Identity; - } else if (ResultTy->isPointerType()) { - // Handle: (CF|CG|CV)Retain - // CFAutorelease - // It's okay to be a little sloppy here. - if (FName == "CMBufferQueueDequeueAndRetain" || - FName == "CMBufferQueueDequeueIfDataReadyAndRetain") { - // Part of: <rdar://problem/39390714>. - // These are not retain. They just return something and retain it. - return None; - } - if (cocoa::isRefType(ResultTy, "CF", FName) || - cocoa::isRefType(ResultTy, "CG", FName) || - cocoa::isRefType(ResultTy, "CV", FName)) - if (isRetain(FD, FName) || isAutorelease(FD, FName) || - isMakeCollectable(FName)) - return BehaviorSummary::Identity; - - // safeMetaCast is called by OSDynamicCast. - // We assume that OSDynamicCast is either an identity (cast is OK, - // the input was non-zero), - // or that it returns zero (when the cast failed, or the input - // was zero). - if (TrackOSObjects && isOSObjectDynamicCast(FName)) { - return BehaviorSummary::IdentityOrZero; - } - - const FunctionDecl* FDD = FD->getDefinition(); - if (FDD && isTrustedReferenceCountImplementation(FDD)) { - hasTrustedImplementationAnnotation = true; - return BehaviorSummary::Identity; - } - } - - if (const auto *MD = dyn_cast<CXXMethodDecl>(FD)) { - const CXXRecordDecl *Parent = MD->getParent(); - if (TrackOSObjects && Parent && isOSObjectSubclass(Parent)) - if (FName == "release" || FName == "retain") - return BehaviorSummary::NoOp; - } - - return None; -} - -const RetainSummary * -RetainSummaryManager::getUnarySummary(const FunctionType* FT, - ArgEffectKind AE) { - - // Unary functions have no arg effects by definition. - ArgEffects ScratchArgs(AF.getEmptyMap()); - - // Sanity check that this is *really* a unary function. This can - // happen if people do weird things. - const FunctionProtoType* FTP = dyn_cast<FunctionProtoType>(FT); - if (!FTP || FTP->getNumParams() != 1) - return getPersistentStopSummary(); - - ArgEffect Effect(AE, ObjKind::CF); - - ScratchArgs = AF.add(ScratchArgs, 0, Effect); - return getPersistentSummary(RetEffect::MakeNoRet(), - ScratchArgs, - ArgEffect(DoNothing), ArgEffect(DoNothing)); -} - -const RetainSummary * -RetainSummaryManager::getOSSummaryRetainRule(const FunctionDecl *FD) { - return getPersistentSummary(RetEffect::MakeNoRet(), - AF.getEmptyMap(), - /*ReceiverEff=*/ArgEffect(DoNothing), - /*DefaultEff=*/ArgEffect(DoNothing), - /*ThisEff=*/ArgEffect(IncRef, ObjKind::OS)); -} - -const RetainSummary * -RetainSummaryManager::getOSSummaryReleaseRule(const FunctionDecl *FD) { - return getPersistentSummary(RetEffect::MakeNoRet(), - AF.getEmptyMap(), - /*ReceiverEff=*/ArgEffect(DoNothing), - /*DefaultEff=*/ArgEffect(DoNothing), - /*ThisEff=*/ArgEffect(DecRef, ObjKind::OS)); -} - -const RetainSummary * -RetainSummaryManager::getOSSummaryFreeRule(const FunctionDecl *FD) { - return getPersistentSummary(RetEffect::MakeNoRet(), - AF.getEmptyMap(), - /*ReceiverEff=*/ArgEffect(DoNothing), - /*DefaultEff=*/ArgEffect(DoNothing), - /*ThisEff=*/ArgEffect(Dealloc, ObjKind::OS)); -} - -const RetainSummary * -RetainSummaryManager::getOSSummaryCreateRule(const FunctionDecl *FD) { - return getPersistentSummary(RetEffect::MakeOwned(ObjKind::OS), - AF.getEmptyMap()); -} - -const RetainSummary * -RetainSummaryManager::getOSSummaryGetRule(const FunctionDecl *FD) { - return getPersistentSummary(RetEffect::MakeNotOwned(ObjKind::OS), - AF.getEmptyMap()); -} - -const RetainSummary * -RetainSummaryManager::getCFSummaryCreateRule(const FunctionDecl *FD) { - return getPersistentSummary(RetEffect::MakeOwned(ObjKind::CF), - ArgEffects(AF.getEmptyMap())); -} - -const RetainSummary * -RetainSummaryManager::getCFSummaryGetRule(const FunctionDecl *FD) { - return getPersistentSummary(RetEffect::MakeNotOwned(ObjKind::CF), - ArgEffects(AF.getEmptyMap()), - ArgEffect(DoNothing), ArgEffect(DoNothing)); -} - - - - -//===----------------------------------------------------------------------===// -// Summary creation for Selectors. -//===----------------------------------------------------------------------===// - -Optional<RetEffect> -RetainSummaryManager::getRetEffectFromAnnotations(QualType RetTy, - const Decl *D) { - if (hasAnyEnabledAttrOf<NSReturnsRetainedAttr>(D, RetTy)) - return ObjCAllocRetE; - - if (auto K = hasAnyEnabledAttrOf<CFReturnsRetainedAttr, OSReturnsRetainedAttr, - GeneralizedReturnsRetainedAttr>(D, RetTy)) - return RetEffect::MakeOwned(*K); - - if (auto K = hasAnyEnabledAttrOf< - CFReturnsNotRetainedAttr, OSReturnsNotRetainedAttr, - GeneralizedReturnsNotRetainedAttr, NSReturnsNotRetainedAttr, - NSReturnsAutoreleasedAttr>(D, RetTy)) - return RetEffect::MakeNotOwned(*K); - - if (const auto *MD = dyn_cast<CXXMethodDecl>(D)) - for (const auto *PD : MD->overridden_methods()) - if (auto RE = getRetEffectFromAnnotations(RetTy, PD)) - return RE; - - return None; -} - -/// \return Whether the chain of typedefs starting from {@code QT} -/// has a typedef with a given name {@code Name}. -static bool hasTypedefNamed(QualType QT, - StringRef Name) { - while (auto *T = dyn_cast<TypedefType>(QT)) { - const auto &Context = T->getDecl()->getASTContext(); - if (T->getDecl()->getIdentifier() == &Context.Idents.get(Name)) - return true; - QT = T->getDecl()->getUnderlyingType(); - } - return false; -} - -static QualType getCallableReturnType(const NamedDecl *ND) { - if (const auto *FD = dyn_cast<FunctionDecl>(ND)) { - return FD->getReturnType(); - } else if (const auto *MD = dyn_cast<ObjCMethodDecl>(ND)) { - return MD->getReturnType(); - } else { - llvm_unreachable("Unexpected decl"); - } -} - -bool RetainSummaryManager::applyParamAnnotationEffect( - const ParmVarDecl *pd, unsigned parm_idx, const NamedDecl *FD, - RetainSummaryTemplate &Template) { - QualType QT = pd->getType(); - if (auto K = - hasAnyEnabledAttrOf<NSConsumedAttr, CFConsumedAttr, OSConsumedAttr, - GeneralizedConsumedAttr>(pd, QT)) { - Template->addArg(AF, parm_idx, ArgEffect(DecRef, *K)); - return true; - } else if (auto K = hasAnyEnabledAttrOf< - CFReturnsRetainedAttr, OSReturnsRetainedAttr, - OSReturnsRetainedOnNonZeroAttr, OSReturnsRetainedOnZeroAttr, - GeneralizedReturnsRetainedAttr>(pd, QT)) { - - // For OSObjects, we try to guess whether the object is created based - // on the return value. - if (K == ObjKind::OS) { - QualType QT = getCallableReturnType(FD); - - bool HasRetainedOnZero = pd->hasAttr<OSReturnsRetainedOnZeroAttr>(); - bool HasRetainedOnNonZero = pd->hasAttr<OSReturnsRetainedOnNonZeroAttr>(); - - // The usual convention is to create an object on non-zero return, but - // it's reverted if the typedef chain has a typedef kern_return_t, - // because kReturnSuccess constant is defined as zero. - // The convention can be overwritten by custom attributes. - bool SuccessOnZero = - HasRetainedOnZero || - (hasTypedefNamed(QT, "kern_return_t") && !HasRetainedOnNonZero); - bool ShouldSplit = !QT.isNull() && !QT->isVoidType(); - ArgEffectKind AK = RetainedOutParameter; - if (ShouldSplit && SuccessOnZero) { - AK = RetainedOutParameterOnZero; - } else if (ShouldSplit && (!SuccessOnZero || HasRetainedOnNonZero)) { - AK = RetainedOutParameterOnNonZero; - } - Template->addArg(AF, parm_idx, ArgEffect(AK, ObjKind::OS)); - } - - // For others: - // Do nothing. Retained out parameters will either point to a +1 reference - // or NULL, but the way you check for failure differs depending on the - // API. Consequently, we don't have a good way to track them yet. - return true; - } else if (auto K = hasAnyEnabledAttrOf<CFReturnsNotRetainedAttr, - OSReturnsNotRetainedAttr, - GeneralizedReturnsNotRetainedAttr>( - pd, QT)) { - Template->addArg(AF, parm_idx, ArgEffect(UnretainedOutParameter, *K)); - return true; - } - - if (const auto *MD = dyn_cast<CXXMethodDecl>(FD)) { - for (const auto *OD : MD->overridden_methods()) { - const ParmVarDecl *OP = OD->parameters()[parm_idx]; - if (applyParamAnnotationEffect(OP, parm_idx, OD, Template)) - return true; - } - } - - return false; -} - -void -RetainSummaryManager::updateSummaryFromAnnotations(const RetainSummary *&Summ, - const FunctionDecl *FD) { - if (!FD) - return; - - assert(Summ && "Must have a summary to add annotations to."); - RetainSummaryTemplate Template(Summ, *this); - - // Effects on the parameters. - unsigned parm_idx = 0; - for (auto pi = FD->param_begin(), - pe = FD->param_end(); pi != pe; ++pi, ++parm_idx) - applyParamAnnotationEffect(*pi, parm_idx, FD, Template); - - QualType RetTy = FD->getReturnType(); - if (Optional<RetEffect> RetE = getRetEffectFromAnnotations(RetTy, FD)) - Template->setRetEffect(*RetE); - - if (hasAnyEnabledAttrOf<OSConsumesThisAttr>(FD, RetTy)) - Template->setThisEffect(ArgEffect(DecRef, ObjKind::OS)); -} - -void -RetainSummaryManager::updateSummaryFromAnnotations(const RetainSummary *&Summ, - const ObjCMethodDecl *MD) { - if (!MD) - return; - - assert(Summ && "Must have a valid summary to add annotations to"); - RetainSummaryTemplate Template(Summ, *this); - - // Effects on the receiver. - if (hasAnyEnabledAttrOf<NSConsumesSelfAttr>(MD, MD->getReturnType())) - Template->setReceiverEffect(ArgEffect(DecRef, ObjKind::ObjC)); - - // Effects on the parameters. - unsigned parm_idx = 0; - for (auto pi = MD->param_begin(), pe = MD->param_end(); pi != pe; - ++pi, ++parm_idx) - applyParamAnnotationEffect(*pi, parm_idx, MD, Template); - - QualType RetTy = MD->getReturnType(); - if (Optional<RetEffect> RetE = getRetEffectFromAnnotations(RetTy, MD)) - Template->setRetEffect(*RetE); -} - -const RetainSummary * -RetainSummaryManager::getStandardMethodSummary(const ObjCMethodDecl *MD, - Selector S, QualType RetTy) { - // Any special effects? - ArgEffect ReceiverEff = ArgEffect(DoNothing, ObjKind::ObjC); - RetEffect ResultEff = RetEffect::MakeNoRet(); - - // 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? - // The selector could be, say, @selector(copy). - if (cocoa::isCocoaObjectRef(RetTy)) - ResultEff = RetEffect::MakeNotOwned(ObjKind::ObjC); - else if (coreFoundation::isCFObjectRef(RetTy)) { - // ObjCMethodDecl currently doesn't consider CF objects as valid return - // values for alloc, new, copy, or mutableCopy, so we have to - // double-check with the selector. This is ugly, but there aren't that - // many Objective-C methods that return CF objects, right? - if (MD) { - switch (S.getMethodFamily()) { - case OMF_alloc: - case OMF_new: - case OMF_copy: - case OMF_mutableCopy: - ResultEff = RetEffect::MakeOwned(ObjKind::CF); - break; - default: - ResultEff = RetEffect::MakeNotOwned(ObjKind::CF); - break; - } - } else { - ResultEff = RetEffect::MakeNotOwned(ObjKind::CF); - } - } - break; - case OMF_init: - ResultEff = ObjCInitRetE; - ReceiverEff = ArgEffect(DecRef, ObjKind::ObjC); - break; - case OMF_alloc: - case OMF_new: - case OMF_copy: - case OMF_mutableCopy: - if (cocoa::isCocoaObjectRef(RetTy)) - ResultEff = ObjCAllocRetE; - else if (coreFoundation::isCFObjectRef(RetTy)) - ResultEff = RetEffect::MakeOwned(ObjKind::CF); - break; - case OMF_autorelease: - ReceiverEff = ArgEffect(Autorelease, ObjKind::ObjC); - break; - case OMF_retain: - ReceiverEff = ArgEffect(IncRef, ObjKind::ObjC); - break; - case OMF_release: - ReceiverEff = ArgEffect(DecRef, ObjKind::ObjC); - break; - case OMF_dealloc: - ReceiverEff = ArgEffect(Dealloc, ObjKind::ObjC); - break; - case OMF_self: - // -self is handled specially by the ExprEngine to propagate the receiver. - break; - case OMF_retainCount: - case OMF_finalize: - // These methods don't return objects. - break; - } - - // If one of the arguments in the selector has the keyword 'delegate' we - // should stop tracking the reference count for the receiver. This is - // because the reference count is quite possibly handled by a delegate - // method. - if (S.isKeywordSelector()) { - for (unsigned i = 0, e = S.getNumArgs(); i != e; ++i) { - StringRef Slot = S.getNameForSlot(i); - if (Slot.substr(Slot.size() - 8).equals_lower("delegate")) { - if (ResultEff == ObjCInitRetE) - ResultEff = RetEffect::MakeNoRetHard(); - else - ReceiverEff = ArgEffect(StopTrackingHard, ObjKind::ObjC); - } - } - } - - if (ReceiverEff.getKind() == DoNothing && - ResultEff.getKind() == RetEffect::NoRet) - return getDefaultSummary(); - - return getPersistentSummary(ResultEff, ArgEffects(AF.getEmptyMap()), - ArgEffect(ReceiverEff), ArgEffect(MayEscape)); -} - -const RetainSummary *RetainSummaryManager::getInstanceMethodSummary( - const ObjCMethodCall &Msg, - QualType ReceiverType) { - const ObjCInterfaceDecl *ReceiverClass = nullptr; - - // We do better tracking of the type of the object than the core ExprEngine. - // See if we have its type in our private state. - if (!ReceiverType.isNull()) - if (const auto *PT = ReceiverType->getAs<ObjCObjectPointerType>()) - ReceiverClass = PT->getInterfaceDecl(); - - // If we don't know what kind of object this is, fall back to its static type. - if (!ReceiverClass) - ReceiverClass = Msg.getReceiverInterface(); - - // FIXME: The receiver could be a reference to a class, meaning that - // we should use the class method. - // id x = [NSObject class]; - // [x performSelector:... withObject:... afterDelay:...]; - Selector S = Msg.getSelector(); - const ObjCMethodDecl *Method = Msg.getDecl(); - if (!Method && ReceiverClass) - Method = ReceiverClass->getInstanceMethod(S); - - return getMethodSummary(S, ReceiverClass, Method, Msg.getResultType(), - ObjCMethodSummaries); -} - -const RetainSummary * -RetainSummaryManager::getMethodSummary(Selector S, - const ObjCInterfaceDecl *ID, - const ObjCMethodDecl *MD, QualType RetTy, - ObjCMethodSummariesTy &CachedSummaries) { - - // Objective-C method summaries are only applicable to ObjC and CF objects. - if (!TrackObjCAndCFObjects) - return getDefaultSummary(); - - // Look up a summary in our summary cache. - const RetainSummary *Summ = CachedSummaries.find(ID, S); - - if (!Summ) { - Summ = getStandardMethodSummary(MD, S, RetTy); - - // Annotations override defaults. - updateSummaryFromAnnotations(Summ, MD); - - // Memoize the summary. - CachedSummaries[ObjCSummaryKey(ID, S)] = Summ; - } - - return Summ; -} - -void RetainSummaryManager::InitializeClassMethodSummaries() { - ArgEffects ScratchArgs = AF.getEmptyMap(); - - // Create the [NSAssertionHandler currentHander] summary. - addClassMethSummary("NSAssertionHandler", "currentHandler", - getPersistentSummary(RetEffect::MakeNotOwned(ObjKind::ObjC), - ScratchArgs)); - - // Create the [NSAutoreleasePool addObject:] summary. - ScratchArgs = AF.add(ScratchArgs, 0, ArgEffect(Autorelease)); - addClassMethSummary("NSAutoreleasePool", "addObject", - getPersistentSummary(RetEffect::MakeNoRet(), ScratchArgs, - ArgEffect(DoNothing), - ArgEffect(Autorelease))); -} - -void RetainSummaryManager::InitializeMethodSummaries() { - - ArgEffects ScratchArgs = AF.getEmptyMap(); - // Create the "init" selector. It just acts as a pass-through for the - // receiver. - const RetainSummary *InitSumm = getPersistentSummary( - ObjCInitRetE, ScratchArgs, ArgEffect(DecRef, ObjKind::ObjC)); - addNSObjectMethSummary(GetNullarySelector("init", Ctx), InitSumm); - - // awakeAfterUsingCoder: behaves basically like an 'init' method. It - // claims the receiver and returns a retained object. - addNSObjectMethSummary(GetUnarySelector("awakeAfterUsingCoder", Ctx), - InitSumm); - - // The next methods are allocators. - const RetainSummary *AllocSumm = getPersistentSummary(ObjCAllocRetE, - ScratchArgs); - const RetainSummary *CFAllocSumm = - getPersistentSummary(RetEffect::MakeOwned(ObjKind::CF), ScratchArgs); - - // Create the "retain" selector. - RetEffect NoRet = RetEffect::MakeNoRet(); - const RetainSummary *Summ = getPersistentSummary( - NoRet, ScratchArgs, ArgEffect(IncRef, ObjKind::ObjC)); - addNSObjectMethSummary(GetNullarySelector("retain", Ctx), Summ); - - // Create the "release" selector. - Summ = getPersistentSummary(NoRet, ScratchArgs, - ArgEffect(DecRef, ObjKind::ObjC)); - addNSObjectMethSummary(GetNullarySelector("release", Ctx), Summ); - - // Create the -dealloc summary. - Summ = getPersistentSummary(NoRet, ScratchArgs, ArgEffect(Dealloc, - ObjKind::ObjC)); - addNSObjectMethSummary(GetNullarySelector("dealloc", Ctx), Summ); - - // Create the "autorelease" selector. - Summ = getPersistentSummary(NoRet, ScratchArgs, ArgEffect(Autorelease, - ObjKind::ObjC)); - addNSObjectMethSummary(GetNullarySelector("autorelease", Ctx), Summ); - - // For NSWindow, allocated objects are (initially) self-owned. - // FIXME: For now we opt for false negatives with NSWindow, as these objects - // self-own themselves. However, they only do this once they are displayed. - // Thus, we need to track an NSWindow's display status. - // This is tracked in <rdar://problem/6062711>. - // See also http://llvm.org/bugs/show_bug.cgi?id=3714. - const RetainSummary *NoTrackYet = - getPersistentSummary(RetEffect::MakeNoRet(), ScratchArgs, - ArgEffect(StopTracking), ArgEffect(StopTracking)); - - addClassMethSummary("NSWindow", "alloc", NoTrackYet); - - // For NSPanel (which subclasses NSWindow), allocated objects are not - // self-owned. - // FIXME: For now we don't track NSPanels. object for the same reason - // as for NSWindow objects. - addClassMethSummary("NSPanel", "alloc", NoTrackYet); - - // For NSNull, objects returned by +null are singletons that ignore - // retain/release semantics. Just don't track them. - // <rdar://problem/12858915> - addClassMethSummary("NSNull", "null", NoTrackYet); - - // Don't track allocated autorelease pools, as it is okay to prematurely - // exit a method. - addClassMethSummary("NSAutoreleasePool", "alloc", NoTrackYet); - addClassMethSummary("NSAutoreleasePool", "allocWithZone", NoTrackYet, false); - addClassMethSummary("NSAutoreleasePool", "new", NoTrackYet); - - // Create summaries QCRenderer/QCView -createSnapShotImageOfType: - addInstMethSummary("QCRenderer", AllocSumm, "createSnapshotImageOfType"); - addInstMethSummary("QCView", AllocSumm, "createSnapshotImageOfType"); - - // Create summaries for CIContext, 'createCGImage' and - // 'createCGLayerWithSize'. These objects are CF objects, and are not - // automatically garbage collected. - addInstMethSummary("CIContext", CFAllocSumm, "createCGImage", "fromRect"); - addInstMethSummary("CIContext", CFAllocSumm, "createCGImage", "fromRect", - "format", "colorSpace"); - addInstMethSummary("CIContext", CFAllocSumm, "createCGLayerWithSize", "info"); -} - -CallEffects CallEffects::getEffect(const ObjCMethodDecl *MD) { - ASTContext &Ctx = MD->getASTContext(); - LangOptions L = Ctx.getLangOpts(); - RetainSummaryManager M(Ctx, L.ObjCAutoRefCount, - /*TrackNSAndCFObjects=*/true, - /*TrackOSObjects=*/false); - const RetainSummary *S = M.getMethodSummary(MD); - CallEffects CE(S->getRetEffect(), S->getReceiverEffect()); - unsigned N = MD->param_size(); - for (unsigned i = 0; i < N; ++i) { - CE.Args.push_back(S->getArg(i)); - } - return CE; -} - -CallEffects CallEffects::getEffect(const FunctionDecl *FD) { - ASTContext &Ctx = FD->getASTContext(); - LangOptions L = Ctx.getLangOpts(); - RetainSummaryManager M(Ctx, L.ObjCAutoRefCount, - /*TrackNSAndCFObjects=*/true, - /*TrackOSObjects=*/false); - const RetainSummary *S = M.getFunctionSummary(FD); - CallEffects CE(S->getRetEffect()); - unsigned N = FD->param_size(); - for (unsigned i = 0; i < N; ++i) { - CE.Args.push_back(S->getArg(i)); - } - return CE; -} diff --git a/lib/StaticAnalyzer/Core/SMTConstraintManager.cpp b/lib/StaticAnalyzer/Core/SMTConstraintManager.cpp new file mode 100644 index 0000000000..d5c14351d3 --- /dev/null +++ b/lib/StaticAnalyzer/Core/SMTConstraintManager.cpp @@ -0,0 +1,18 @@ +//== SMTConstraintManager.cpp -----------------------------------*- C++ -*--==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Core/PathSensitive/SMTConstraintManager.h" + +using namespace clang; +using namespace ento; + +std::unique_ptr<ConstraintManager> +ento::CreateZ3ConstraintManager(ProgramStateManager &StMgr, SubEngine *Eng) { + return llvm::make_unique<SMTConstraintManager>(Eng, StMgr.getSValBuilder()); +} diff --git a/lib/StaticAnalyzer/Core/SValBuilder.cpp b/lib/StaticAnalyzer/Core/SValBuilder.cpp index 6c0d487c8a..3a5841137e 100644 --- a/lib/StaticAnalyzer/Core/SValBuilder.cpp +++ b/lib/StaticAnalyzer/Core/SValBuilder.cpp @@ -1,9 +1,8 @@ //===- SValBuilder.cpp - Basic class for all SValBuilder implementations --===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // diff --git a/lib/StaticAnalyzer/Core/SVals.cpp b/lib/StaticAnalyzer/Core/SVals.cpp index 933c5c3300..b3c83e7792 100644 --- a/lib/StaticAnalyzer/Core/SVals.cpp +++ b/lib/StaticAnalyzer/Core/SVals.cpp @@ -1,9 +1,8 @@ -//===- RValues.cpp - Abstract RValues for Path-Sens. Value Tracking -------===// +//===-- SVals.cpp - Abstract RValues for Path-Sens. Value Tracking --------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // diff --git a/lib/StaticAnalyzer/Core/SarifDiagnostics.cpp b/lib/StaticAnalyzer/Core/SarifDiagnostics.cpp index fecbc00010..a8f529b7d3 100644 --- a/lib/StaticAnalyzer/Core/SarifDiagnostics.cpp +++ b/lib/StaticAnalyzer/Core/SarifDiagnostics.cpp @@ -1,9 +1,8 @@ //===--- SarifDiagnostics.cpp - Sarif Diagnostics for Paths -----*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -257,7 +256,7 @@ static json::Object createResult(const PathDiagnostic &Diag, json::Array &Files, static StringRef getRuleDescription(StringRef CheckName) { return llvm::StringSwitch<StringRef>(CheckName) #define GET_CHECKERS -#define CHECKER(FULLNAME, CLASS, HELPTEXT, DOC_URI) \ +#define CHECKER(FULLNAME, CLASS, HELPTEXT, DOC_URI, IS_HIDDEN) \ .Case(FULLNAME, HELPTEXT) #include "clang/StaticAnalyzer/Checkers/Checkers.inc" #undef CHECKER @@ -268,7 +267,7 @@ static StringRef getRuleDescription(StringRef CheckName) { static StringRef getRuleHelpURIStr(StringRef CheckName) { return llvm::StringSwitch<StringRef>(CheckName) #define GET_CHECKERS -#define CHECKER(FULLNAME, CLASS, HELPTEXT, DOC_URI) \ +#define CHECKER(FULLNAME, CLASS, HELPTEXT, DOC_URI, IS_HIDDEN) \ .Case(FULLNAME, DOC_URI) #include "clang/StaticAnalyzer/Checkers/Checkers.inc" #undef CHECKER diff --git a/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp b/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp index adb40178f5..85f60231a2 100644 --- a/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp +++ b/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp @@ -1,9 +1,8 @@ //== SimpleConstraintManager.cpp --------------------------------*- C++ -*--==// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // diff --git a/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp b/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp index fc57cecac9..aaf29abd47 100644 --- a/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp +++ b/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp @@ -1,9 +1,8 @@ // SimpleSValBuilder.cpp - A basic SValBuilder -----------------------*- C++ -*- // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -572,7 +571,15 @@ SVal SimpleSValBuilder::evalBinOpNN(ProgramStateRef state, // add 1 to a LocAsInteger, we'd better unpack the Loc and add to it, // then pack it back into a LocAsInteger. llvm::APSInt i = rhs.castAs<nonloc::ConcreteInt>().getValue(); - BasicVals.getAPSIntType(Context.VoidPtrTy).apply(i); + // If the region has a symbolic base, pay attention to the type; it + // might be coming from a non-default address space. For non-symbolic + // regions it doesn't matter that much because such comparisons would + // most likely evaluate to concrete false anyway. FIXME: We might + // still need to handle the non-comparison case. + if (SymbolRef lSym = lhs.getAsLocSymbol(true)) + BasicVals.getAPSIntType(lSym->getType()).apply(i); + else + BasicVals.getAPSIntType(Context.VoidPtrTy).apply(i); return evalBinOpLL(state, op, lhsL, makeLoc(i), resultTy); } default: diff --git a/lib/StaticAnalyzer/Core/Store.cpp b/lib/StaticAnalyzer/Core/Store.cpp index 4fa937d965..3cf616161c 100644 --- a/lib/StaticAnalyzer/Core/Store.cpp +++ b/lib/StaticAnalyzer/Core/Store.cpp @@ -1,9 +1,8 @@ //===- Store.cpp - Interface for maps from Locations to Values ------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // diff --git a/lib/StaticAnalyzer/Core/SubEngine.cpp b/lib/StaticAnalyzer/Core/SubEngine.cpp index 350f4b8bb3..d7ddd9cf46 100644 --- a/lib/StaticAnalyzer/Core/SubEngine.cpp +++ b/lib/StaticAnalyzer/Core/SubEngine.cpp @@ -1,9 +1,8 @@ //== SubEngine.cpp - Interface of the subengine of CoreEngine ------*- C++ -*-// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// diff --git a/lib/StaticAnalyzer/Core/SymbolManager.cpp b/lib/StaticAnalyzer/Core/SymbolManager.cpp index 66273f099a..675209f6fd 100644 --- a/lib/StaticAnalyzer/Core/SymbolManager.cpp +++ b/lib/StaticAnalyzer/Core/SymbolManager.cpp @@ -1,9 +1,8 @@ //===- SymbolManager.h - Management of Symbolic Values --------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -405,7 +404,7 @@ void SymbolReaper::markLive(SymbolRef sym) { } void SymbolReaper::markLive(const MemRegion *region) { - RegionRoots.insert(region); + RegionRoots.insert(region->getBaseRegion()); markElementIndicesLive(region); } @@ -426,11 +425,15 @@ void SymbolReaper::markInUse(SymbolRef sym) { } bool SymbolReaper::isLiveRegion(const MemRegion *MR) { + // TODO: For now, liveness of a memory region is equivalent to liveness of its + // base region. In fact we can do a bit better: say, if a particular FieldDecl + // is not used later in the path, we can diagnose a leak of a value within + // that field earlier than, say, the variable that contains the field dies. + MR = MR->getBaseRegion(); + if (RegionRoots.count(MR)) return true; - MR = MR->getBaseRegion(); - if (const auto *SR = dyn_cast<SymbolicRegion>(MR)) return isLive(SR->getSymbol()); diff --git a/lib/StaticAnalyzer/Core/TaintManager.cpp b/lib/StaticAnalyzer/Core/TaintManager.cpp deleted file mode 100644 index c34b0ca183..0000000000 --- a/lib/StaticAnalyzer/Core/TaintManager.cpp +++ /dev/null @@ -1,23 +0,0 @@ -//== TaintManager.cpp ------------------------------------------ -*- C++ -*--=// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#include "clang/StaticAnalyzer/Core/PathSensitive/TaintManager.h" - -using namespace clang; -using namespace ento; - -void *ProgramStateTrait<TaintMap>::GDMIndex() { - static int index = 0; - return &index; -} - -void *ProgramStateTrait<DerivedSymTaint>::GDMIndex() { - static int index; - return &index; -} diff --git a/lib/StaticAnalyzer/Core/WorkList.cpp b/lib/StaticAnalyzer/Core/WorkList.cpp index e705393cb8..129d172039 100644 --- a/lib/StaticAnalyzer/Core/WorkList.cpp +++ b/lib/StaticAnalyzer/Core/WorkList.cpp @@ -1,9 +1,8 @@ //===- WorkList.cpp - Analyzer work-list implementation--------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // diff --git a/lib/StaticAnalyzer/Core/Z3ConstraintManager.cpp b/lib/StaticAnalyzer/Core/Z3ConstraintManager.cpp deleted file mode 100644 index c4729f969f..0000000000 --- a/lib/StaticAnalyzer/Core/Z3ConstraintManager.cpp +++ /dev/null @@ -1,841 +0,0 @@ -//== Z3ConstraintManager.cpp --------------------------------*- C++ -*--==// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#include "clang/Basic/TargetInfo.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/SMTConstraintManager.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/SMTConv.h" - -#include "clang/Config/config.h" - -using namespace clang; -using namespace ento; - -#if CLANG_ANALYZER_WITH_Z3 - -#include <z3.h> - -namespace { - -/// Configuration class for Z3 -class Z3Config { - friend class Z3Context; - - Z3_config Config; - -public: - Z3Config() : Config(Z3_mk_config()) { - // Enable model finding - Z3_set_param_value(Config, "model", "true"); - // Disable proof generation - Z3_set_param_value(Config, "proof", "false"); - // Set timeout to 15000ms = 15s - Z3_set_param_value(Config, "timeout", "15000"); - } - - ~Z3Config() { Z3_del_config(Config); } -}; // end class Z3Config - -// Function used to report errors -void Z3ErrorHandler(Z3_context Context, Z3_error_code Error) { - llvm::report_fatal_error("Z3 error: " + - llvm::Twine(Z3_get_error_msg(Context, Error))); -} - -/// Wrapper for Z3 context -class Z3Context { -public: - Z3_context Context; - - Z3Context() { - Context = Z3_mk_context_rc(Z3Config().Config); - // The error function is set here because the context is the first object - // created by the backend - Z3_set_error_handler(Context, Z3ErrorHandler); - } - - virtual ~Z3Context() { - Z3_del_context(Context); - Context = nullptr; - } -}; // end class Z3Context - -/// Wrapper for Z3 Sort -class Z3Sort : public SMTSort { - friend class Z3Solver; - - Z3Context &Context; - - Z3_sort Sort; - -public: - /// Default constructor, mainly used by make_shared - Z3Sort(Z3Context &C, Z3_sort ZS) : Context(C), Sort(ZS) { - Z3_inc_ref(Context.Context, reinterpret_cast<Z3_ast>(Sort)); - } - - /// Override implicit copy constructor for correct reference counting. - Z3Sort(const Z3Sort &Other) : Context(Other.Context), Sort(Other.Sort) { - Z3_inc_ref(Context.Context, reinterpret_cast<Z3_ast>(Sort)); - } - - /// Override implicit copy assignment constructor for correct reference - /// counting. - Z3Sort &operator=(const Z3Sort &Other) { - Z3_inc_ref(Context.Context, reinterpret_cast<Z3_ast>(Other.Sort)); - Z3_dec_ref(Context.Context, reinterpret_cast<Z3_ast>(Sort)); - Sort = Other.Sort; - return *this; - } - - Z3Sort(Z3Sort &&Other) = delete; - Z3Sort &operator=(Z3Sort &&Other) = delete; - - ~Z3Sort() { - if (Sort) - Z3_dec_ref(Context.Context, reinterpret_cast<Z3_ast>(Sort)); - } - - bool isBitvectorSortImpl() const override { - return (Z3_get_sort_kind(Context.Context, Sort) == Z3_BV_SORT); - } - - bool isFloatSortImpl() const override { - return (Z3_get_sort_kind(Context.Context, Sort) == Z3_FLOATING_POINT_SORT); - } - - bool isBooleanSortImpl() const override { - return (Z3_get_sort_kind(Context.Context, Sort) == Z3_BOOL_SORT); - } - - unsigned getBitvectorSortSizeImpl() const override { - return Z3_get_bv_sort_size(Context.Context, Sort); - } - - unsigned getFloatSortSizeImpl() const override { - return Z3_fpa_get_ebits(Context.Context, Sort) + - Z3_fpa_get_sbits(Context.Context, Sort); - } - - bool equal_to(SMTSort const &Other) const override { - return Z3_is_eq_sort(Context.Context, Sort, - static_cast<const Z3Sort &>(Other).Sort); - } - - void print(raw_ostream &OS) const override { - OS << Z3_sort_to_string(Context.Context, Sort); - } -}; // end class Z3Sort - -static const Z3Sort &toZ3Sort(const SMTSort &S) { - return static_cast<const Z3Sort &>(S); -} - -class Z3Expr : public SMTExpr { - friend class Z3Solver; - - Z3Context &Context; - - Z3_ast AST; - -public: - Z3Expr(Z3Context &C, Z3_ast ZA) : SMTExpr(), Context(C), AST(ZA) { - Z3_inc_ref(Context.Context, AST); - } - - /// Override implicit copy constructor for correct reference counting. - Z3Expr(const Z3Expr &Copy) : SMTExpr(), Context(Copy.Context), AST(Copy.AST) { - Z3_inc_ref(Context.Context, AST); - } - - /// Override implicit copy assignment constructor for correct reference - /// counting. - Z3Expr &operator=(const Z3Expr &Other) { - Z3_inc_ref(Context.Context, Other.AST); - Z3_dec_ref(Context.Context, AST); - AST = Other.AST; - return *this; - } - - Z3Expr(Z3Expr &&Other) = delete; - Z3Expr &operator=(Z3Expr &&Other) = delete; - - ~Z3Expr() { - if (AST) - Z3_dec_ref(Context.Context, AST); - } - - void Profile(llvm::FoldingSetNodeID &ID) const override { - ID.AddInteger(Z3_get_ast_hash(Context.Context, AST)); - } - - /// Comparison of AST equality, not model equivalence. - bool equal_to(SMTExpr const &Other) const override { - assert(Z3_is_eq_sort(Context.Context, Z3_get_sort(Context.Context, AST), - Z3_get_sort(Context.Context, - static_cast<const Z3Expr &>(Other).AST)) && - "AST's must have the same sort"); - return Z3_is_eq_ast(Context.Context, AST, - static_cast<const Z3Expr &>(Other).AST); - } - - void print(raw_ostream &OS) const override { - OS << Z3_ast_to_string(Context.Context, AST); - } -}; // end class Z3Expr - -static const Z3Expr &toZ3Expr(const SMTExpr &E) { - return static_cast<const Z3Expr &>(E); -} - -class Z3Model { - friend class Z3Solver; - - Z3Context &Context; - - Z3_model Model; - -public: - Z3Model(Z3Context &C, Z3_model ZM) : Context(C), Model(ZM) { - Z3_model_inc_ref(Context.Context, Model); - } - - Z3Model(const Z3Model &Other) = delete; - Z3Model(Z3Model &&Other) = delete; - Z3Model &operator=(Z3Model &Other) = delete; - Z3Model &operator=(Z3Model &&Other) = delete; - - ~Z3Model() { - if (Model) - Z3_model_dec_ref(Context.Context, Model); - } - - void print(raw_ostream &OS) const { - OS << Z3_model_to_string(Context.Context, Model); - } - - LLVM_DUMP_METHOD void dump() const { print(llvm::errs()); } -}; // end class Z3Model - -/// Get the corresponding IEEE floating-point type for a given bitwidth. -static const llvm::fltSemantics &getFloatSemantics(unsigned BitWidth) { - switch (BitWidth) { - default: - llvm_unreachable("Unsupported floating-point semantics!"); - break; - case 16: - return llvm::APFloat::IEEEhalf(); - case 32: - return llvm::APFloat::IEEEsingle(); - case 64: - return llvm::APFloat::IEEEdouble(); - case 128: - return llvm::APFloat::IEEEquad(); - } -} - -// Determine whether two float semantics are equivalent -static bool areEquivalent(const llvm::fltSemantics &LHS, - const llvm::fltSemantics &RHS) { - return (llvm::APFloat::semanticsPrecision(LHS) == - llvm::APFloat::semanticsPrecision(RHS)) && - (llvm::APFloat::semanticsMinExponent(LHS) == - llvm::APFloat::semanticsMinExponent(RHS)) && - (llvm::APFloat::semanticsMaxExponent(LHS) == - llvm::APFloat::semanticsMaxExponent(RHS)) && - (llvm::APFloat::semanticsSizeInBits(LHS) == - llvm::APFloat::semanticsSizeInBits(RHS)); -} - -} // end anonymous namespace - -typedef llvm::ImmutableSet<std::pair<SymbolRef, Z3Expr>> ConstraintZ3Ty; -REGISTER_TRAIT_WITH_PROGRAMSTATE(ConstraintZ3, ConstraintZ3Ty) - -namespace { - -class Z3Solver : public SMTSolver { - friend class Z3ConstraintManager; - - Z3Context Context; - - Z3_solver Solver; - -public: - Z3Solver() : Solver(Z3_mk_simple_solver(Context.Context)) { - Z3_solver_inc_ref(Context.Context, Solver); - } - - Z3Solver(const Z3Solver &Other) = delete; - Z3Solver(Z3Solver &&Other) = delete; - Z3Solver &operator=(Z3Solver &Other) = delete; - Z3Solver &operator=(Z3Solver &&Other) = delete; - - ~Z3Solver() { - if (Solver) - Z3_solver_dec_ref(Context.Context, Solver); - } - - void addConstraint(const SMTExprRef &Exp) const override { - Z3_solver_assert(Context.Context, Solver, toZ3Expr(*Exp).AST); - } - - SMTSortRef getBoolSort() override { - return std::make_shared<Z3Sort>(Context, Z3_mk_bool_sort(Context.Context)); - } - - SMTSortRef getBitvectorSort(unsigned BitWidth) override { - return std::make_shared<Z3Sort>(Context, - Z3_mk_bv_sort(Context.Context, BitWidth)); - } - - SMTSortRef getSort(const SMTExprRef &Exp) override { - return std::make_shared<Z3Sort>( - Context, Z3_get_sort(Context.Context, toZ3Expr(*Exp).AST)); - } - - SMTSortRef getFloat16Sort() override { - return std::make_shared<Z3Sort>(Context, - Z3_mk_fpa_sort_16(Context.Context)); - } - - SMTSortRef getFloat32Sort() override { - return std::make_shared<Z3Sort>(Context, - Z3_mk_fpa_sort_32(Context.Context)); - } - - SMTSortRef getFloat64Sort() override { - return std::make_shared<Z3Sort>(Context, - Z3_mk_fpa_sort_64(Context.Context)); - } - - SMTSortRef getFloat128Sort() override { - return std::make_shared<Z3Sort>(Context, - Z3_mk_fpa_sort_128(Context.Context)); - } - - SMTExprRef newExprRef(const SMTExpr &E) const override { - return std::make_shared<Z3Expr>(toZ3Expr(E)); - } - - SMTExprRef mkBVNeg(const SMTExprRef &Exp) override { - return newExprRef( - Z3Expr(Context, Z3_mk_bvneg(Context.Context, toZ3Expr(*Exp).AST))); - } - - SMTExprRef mkBVNot(const SMTExprRef &Exp) override { - return newExprRef( - Z3Expr(Context, Z3_mk_bvnot(Context.Context, toZ3Expr(*Exp).AST))); - } - - SMTExprRef mkNot(const SMTExprRef &Exp) override { - return newExprRef( - Z3Expr(Context, Z3_mk_not(Context.Context, toZ3Expr(*Exp).AST))); - } - - SMTExprRef mkBVAdd(const SMTExprRef &LHS, const SMTExprRef &RHS) override { - return newExprRef( - Z3Expr(Context, Z3_mk_bvadd(Context.Context, toZ3Expr(*LHS).AST, - toZ3Expr(*RHS).AST))); - } - - SMTExprRef mkBVSub(const SMTExprRef &LHS, const SMTExprRef &RHS) override { - return newExprRef( - Z3Expr(Context, Z3_mk_bvsub(Context.Context, toZ3Expr(*LHS).AST, - toZ3Expr(*RHS).AST))); - } - - SMTExprRef mkBVMul(const SMTExprRef &LHS, const SMTExprRef &RHS) override { - return newExprRef( - Z3Expr(Context, Z3_mk_bvmul(Context.Context, toZ3Expr(*LHS).AST, - toZ3Expr(*RHS).AST))); - } - - SMTExprRef mkBVSRem(const SMTExprRef &LHS, const SMTExprRef &RHS) override { - return newExprRef( - Z3Expr(Context, Z3_mk_bvsrem(Context.Context, toZ3Expr(*LHS).AST, - toZ3Expr(*RHS).AST))); - } - - SMTExprRef mkBVURem(const SMTExprRef &LHS, const SMTExprRef &RHS) override { - return newExprRef( - Z3Expr(Context, Z3_mk_bvurem(Context.Context, toZ3Expr(*LHS).AST, - toZ3Expr(*RHS).AST))); - } - - SMTExprRef mkBVSDiv(const SMTExprRef &LHS, const SMTExprRef &RHS) override { - return newExprRef( - Z3Expr(Context, Z3_mk_bvsdiv(Context.Context, toZ3Expr(*LHS).AST, - toZ3Expr(*RHS).AST))); - } - - SMTExprRef mkBVUDiv(const SMTExprRef &LHS, const SMTExprRef &RHS) override { - return newExprRef( - Z3Expr(Context, Z3_mk_bvudiv(Context.Context, toZ3Expr(*LHS).AST, - toZ3Expr(*RHS).AST))); - } - - SMTExprRef mkBVShl(const SMTExprRef &LHS, const SMTExprRef &RHS) override { - return newExprRef( - Z3Expr(Context, Z3_mk_bvshl(Context.Context, toZ3Expr(*LHS).AST, - toZ3Expr(*RHS).AST))); - } - - SMTExprRef mkBVAshr(const SMTExprRef &LHS, const SMTExprRef &RHS) override { - return newExprRef( - Z3Expr(Context, Z3_mk_bvashr(Context.Context, toZ3Expr(*LHS).AST, - toZ3Expr(*RHS).AST))); - } - - SMTExprRef mkBVLshr(const SMTExprRef &LHS, const SMTExprRef &RHS) override { - return newExprRef( - Z3Expr(Context, Z3_mk_bvlshr(Context.Context, toZ3Expr(*LHS).AST, - toZ3Expr(*RHS).AST))); - } - - SMTExprRef mkBVXor(const SMTExprRef &LHS, const SMTExprRef &RHS) override { - return newExprRef( - Z3Expr(Context, Z3_mk_bvxor(Context.Context, toZ3Expr(*LHS).AST, - toZ3Expr(*RHS).AST))); - } - - SMTExprRef mkBVOr(const SMTExprRef &LHS, const SMTExprRef &RHS) override { - return newExprRef( - Z3Expr(Context, Z3_mk_bvor(Context.Context, toZ3Expr(*LHS).AST, - toZ3Expr(*RHS).AST))); - } - - SMTExprRef mkBVAnd(const SMTExprRef &LHS, const SMTExprRef &RHS) override { - return newExprRef( - Z3Expr(Context, Z3_mk_bvand(Context.Context, toZ3Expr(*LHS).AST, - toZ3Expr(*RHS).AST))); - } - - SMTExprRef mkBVUlt(const SMTExprRef &LHS, const SMTExprRef &RHS) override { - return newExprRef( - Z3Expr(Context, Z3_mk_bvult(Context.Context, toZ3Expr(*LHS).AST, - toZ3Expr(*RHS).AST))); - } - - SMTExprRef mkBVSlt(const SMTExprRef &LHS, const SMTExprRef &RHS) override { - return newExprRef( - Z3Expr(Context, Z3_mk_bvslt(Context.Context, toZ3Expr(*LHS).AST, - toZ3Expr(*RHS).AST))); - } - - SMTExprRef mkBVUgt(const SMTExprRef &LHS, const SMTExprRef &RHS) override { - return newExprRef( - Z3Expr(Context, Z3_mk_bvugt(Context.Context, toZ3Expr(*LHS).AST, - toZ3Expr(*RHS).AST))); - } - - SMTExprRef mkBVSgt(const SMTExprRef &LHS, const SMTExprRef &RHS) override { - return newExprRef( - Z3Expr(Context, Z3_mk_bvsgt(Context.Context, toZ3Expr(*LHS).AST, - toZ3Expr(*RHS).AST))); - } - - SMTExprRef mkBVUle(const SMTExprRef &LHS, const SMTExprRef &RHS) override { - return newExprRef( - Z3Expr(Context, Z3_mk_bvule(Context.Context, toZ3Expr(*LHS).AST, - toZ3Expr(*RHS).AST))); - } - - SMTExprRef mkBVSle(const SMTExprRef &LHS, const SMTExprRef &RHS) override { - return newExprRef( - Z3Expr(Context, Z3_mk_bvsle(Context.Context, toZ3Expr(*LHS).AST, - toZ3Expr(*RHS).AST))); - } - - SMTExprRef mkBVUge(const SMTExprRef &LHS, const SMTExprRef &RHS) override { - return newExprRef( - Z3Expr(Context, Z3_mk_bvuge(Context.Context, toZ3Expr(*LHS).AST, - toZ3Expr(*RHS).AST))); - } - - SMTExprRef mkBVSge(const SMTExprRef &LHS, const SMTExprRef &RHS) override { - return newExprRef( - Z3Expr(Context, Z3_mk_bvsge(Context.Context, toZ3Expr(*LHS).AST, - toZ3Expr(*RHS).AST))); - } - - SMTExprRef mkAnd(const SMTExprRef &LHS, const SMTExprRef &RHS) override { - Z3_ast Args[2] = {toZ3Expr(*LHS).AST, toZ3Expr(*RHS).AST}; - return newExprRef(Z3Expr(Context, Z3_mk_and(Context.Context, 2, Args))); - } - - SMTExprRef mkOr(const SMTExprRef &LHS, const SMTExprRef &RHS) override { - Z3_ast Args[2] = {toZ3Expr(*LHS).AST, toZ3Expr(*RHS).AST}; - return newExprRef(Z3Expr(Context, Z3_mk_or(Context.Context, 2, Args))); - } - - SMTExprRef mkEqual(const SMTExprRef &LHS, const SMTExprRef &RHS) override { - return newExprRef( - Z3Expr(Context, Z3_mk_eq(Context.Context, toZ3Expr(*LHS).AST, - toZ3Expr(*RHS).AST))); - } - - SMTExprRef mkFPNeg(const SMTExprRef &Exp) override { - return newExprRef( - Z3Expr(Context, Z3_mk_fpa_neg(Context.Context, toZ3Expr(*Exp).AST))); - } - - SMTExprRef mkFPIsInfinite(const SMTExprRef &Exp) override { - return newExprRef(Z3Expr( - Context, Z3_mk_fpa_is_infinite(Context.Context, toZ3Expr(*Exp).AST))); - } - - SMTExprRef mkFPIsNaN(const SMTExprRef &Exp) override { - return newExprRef( - Z3Expr(Context, Z3_mk_fpa_is_nan(Context.Context, toZ3Expr(*Exp).AST))); - } - - SMTExprRef mkFPIsNormal(const SMTExprRef &Exp) override { - return newExprRef(Z3Expr( - Context, Z3_mk_fpa_is_normal(Context.Context, toZ3Expr(*Exp).AST))); - } - - SMTExprRef mkFPIsZero(const SMTExprRef &Exp) override { - return newExprRef(Z3Expr( - Context, Z3_mk_fpa_is_zero(Context.Context, toZ3Expr(*Exp).AST))); - } - - SMTExprRef mkFPMul(const SMTExprRef &LHS, const SMTExprRef &RHS) override { - SMTExprRef RoundingMode = getFloatRoundingMode(); - return newExprRef( - Z3Expr(Context, - Z3_mk_fpa_mul(Context.Context, toZ3Expr(*LHS).AST, - toZ3Expr(*RHS).AST, toZ3Expr(*RoundingMode).AST))); - } - - SMTExprRef mkFPDiv(const SMTExprRef &LHS, const SMTExprRef &RHS) override { - SMTExprRef RoundingMode = getFloatRoundingMode(); - return newExprRef( - Z3Expr(Context, - Z3_mk_fpa_div(Context.Context, toZ3Expr(*LHS).AST, - toZ3Expr(*RHS).AST, toZ3Expr(*RoundingMode).AST))); - } - - SMTExprRef mkFPRem(const SMTExprRef &LHS, const SMTExprRef &RHS) override { - return newExprRef( - Z3Expr(Context, Z3_mk_fpa_rem(Context.Context, toZ3Expr(*LHS).AST, - toZ3Expr(*RHS).AST))); - } - - SMTExprRef mkFPAdd(const SMTExprRef &LHS, const SMTExprRef &RHS) override { - SMTExprRef RoundingMode = getFloatRoundingMode(); - return newExprRef( - Z3Expr(Context, - Z3_mk_fpa_add(Context.Context, toZ3Expr(*LHS).AST, - toZ3Expr(*RHS).AST, toZ3Expr(*RoundingMode).AST))); - } - - SMTExprRef mkFPSub(const SMTExprRef &LHS, const SMTExprRef &RHS) override { - SMTExprRef RoundingMode = getFloatRoundingMode(); - return newExprRef( - Z3Expr(Context, - Z3_mk_fpa_sub(Context.Context, toZ3Expr(*LHS).AST, - toZ3Expr(*RHS).AST, toZ3Expr(*RoundingMode).AST))); - } - - SMTExprRef mkFPLt(const SMTExprRef &LHS, const SMTExprRef &RHS) override { - return newExprRef( - Z3Expr(Context, Z3_mk_fpa_lt(Context.Context, toZ3Expr(*LHS).AST, - toZ3Expr(*RHS).AST))); - } - - SMTExprRef mkFPGt(const SMTExprRef &LHS, const SMTExprRef &RHS) override { - return newExprRef( - Z3Expr(Context, Z3_mk_fpa_gt(Context.Context, toZ3Expr(*LHS).AST, - toZ3Expr(*RHS).AST))); - } - - SMTExprRef mkFPLe(const SMTExprRef &LHS, const SMTExprRef &RHS) override { - return newExprRef( - Z3Expr(Context, Z3_mk_fpa_leq(Context.Context, toZ3Expr(*LHS).AST, - toZ3Expr(*RHS).AST))); - } - - SMTExprRef mkFPGe(const SMTExprRef &LHS, const SMTExprRef &RHS) override { - return newExprRef( - Z3Expr(Context, Z3_mk_fpa_geq(Context.Context, toZ3Expr(*LHS).AST, - toZ3Expr(*RHS).AST))); - } - - SMTExprRef mkFPEqual(const SMTExprRef &LHS, const SMTExprRef &RHS) override { - return newExprRef( - Z3Expr(Context, Z3_mk_fpa_eq(Context.Context, toZ3Expr(*LHS).AST, - toZ3Expr(*RHS).AST))); - } - - SMTExprRef mkIte(const SMTExprRef &Cond, const SMTExprRef &T, - const SMTExprRef &F) override { - return newExprRef( - Z3Expr(Context, Z3_mk_ite(Context.Context, toZ3Expr(*Cond).AST, - toZ3Expr(*T).AST, toZ3Expr(*F).AST))); - } - - SMTExprRef mkBVSignExt(unsigned i, const SMTExprRef &Exp) override { - return newExprRef(Z3Expr( - Context, Z3_mk_sign_ext(Context.Context, i, toZ3Expr(*Exp).AST))); - } - - SMTExprRef mkBVZeroExt(unsigned i, const SMTExprRef &Exp) override { - return newExprRef(Z3Expr( - Context, Z3_mk_zero_ext(Context.Context, i, toZ3Expr(*Exp).AST))); - } - - SMTExprRef mkBVExtract(unsigned High, unsigned Low, - const SMTExprRef &Exp) override { - return newExprRef(Z3Expr(Context, Z3_mk_extract(Context.Context, High, Low, - toZ3Expr(*Exp).AST))); - } - - SMTExprRef mkBVConcat(const SMTExprRef &LHS, const SMTExprRef &RHS) override { - return newExprRef( - Z3Expr(Context, Z3_mk_concat(Context.Context, toZ3Expr(*LHS).AST, - toZ3Expr(*RHS).AST))); - } - - SMTExprRef mkFPtoFP(const SMTExprRef &From, const SMTSortRef &To) override { - SMTExprRef RoundingMode = getFloatRoundingMode(); - return newExprRef(Z3Expr( - Context, - Z3_mk_fpa_to_fp_float(Context.Context, toZ3Expr(*RoundingMode).AST, - toZ3Expr(*From).AST, toZ3Sort(*To).Sort))); - } - - SMTExprRef mkSBVtoFP(const SMTExprRef &From, const SMTSortRef &To) override { - SMTExprRef RoundingMode = getFloatRoundingMode(); - return newExprRef(Z3Expr( - Context, - Z3_mk_fpa_to_fp_signed(Context.Context, toZ3Expr(*RoundingMode).AST, - toZ3Expr(*From).AST, toZ3Sort(*To).Sort))); - } - - SMTExprRef mkUBVtoFP(const SMTExprRef &From, const SMTSortRef &To) override { - SMTExprRef RoundingMode = getFloatRoundingMode(); - return newExprRef(Z3Expr( - Context, - Z3_mk_fpa_to_fp_unsigned(Context.Context, toZ3Expr(*RoundingMode).AST, - toZ3Expr(*From).AST, toZ3Sort(*To).Sort))); - } - - SMTExprRef mkFPtoSBV(const SMTExprRef &From, unsigned ToWidth) override { - SMTExprRef RoundingMode = getFloatRoundingMode(); - return newExprRef(Z3Expr( - Context, Z3_mk_fpa_to_sbv(Context.Context, toZ3Expr(*RoundingMode).AST, - toZ3Expr(*From).AST, ToWidth))); - } - - SMTExprRef mkFPtoUBV(const SMTExprRef &From, unsigned ToWidth) override { - SMTExprRef RoundingMode = getFloatRoundingMode(); - return newExprRef(Z3Expr( - Context, Z3_mk_fpa_to_ubv(Context.Context, toZ3Expr(*RoundingMode).AST, - toZ3Expr(*From).AST, ToWidth))); - } - - SMTExprRef mkBoolean(const bool b) override { - return newExprRef(Z3Expr(Context, b ? Z3_mk_true(Context.Context) - : Z3_mk_false(Context.Context))); - } - - SMTExprRef mkBitvector(const llvm::APSInt Int, unsigned BitWidth) override { - const SMTSortRef Sort = getBitvectorSort(BitWidth); - return newExprRef( - Z3Expr(Context, Z3_mk_numeral(Context.Context, Int.toString(10).c_str(), - toZ3Sort(*Sort).Sort))); - } - - SMTExprRef mkFloat(const llvm::APFloat Float) override { - SMTSortRef Sort = - getFloatSort(llvm::APFloat::semanticsSizeInBits(Float.getSemantics())); - - llvm::APSInt Int = llvm::APSInt(Float.bitcastToAPInt(), false); - SMTExprRef Z3Int = mkBitvector(Int, Int.getBitWidth()); - return newExprRef(Z3Expr( - Context, Z3_mk_fpa_to_fp_bv(Context.Context, toZ3Expr(*Z3Int).AST, - toZ3Sort(*Sort).Sort))); - } - - SMTExprRef mkSymbol(const char *Name, SMTSortRef Sort) override { - return newExprRef( - Z3Expr(Context, Z3_mk_const(Context.Context, - Z3_mk_string_symbol(Context.Context, Name), - toZ3Sort(*Sort).Sort))); - } - - llvm::APSInt getBitvector(const SMTExprRef &Exp, unsigned BitWidth, - bool isUnsigned) override { - return llvm::APSInt( - llvm::APInt(BitWidth, - Z3_get_numeral_string(Context.Context, toZ3Expr(*Exp).AST), - 10), - isUnsigned); - } - - bool getBoolean(const SMTExprRef &Exp) override { - return Z3_get_bool_value(Context.Context, toZ3Expr(*Exp).AST) == Z3_L_TRUE; - } - - SMTExprRef getFloatRoundingMode() override { - // TODO: Don't assume nearest ties to even rounding mode - return newExprRef(Z3Expr(Context, Z3_mk_fpa_rne(Context.Context))); - } - - bool toAPFloat(const SMTSortRef &Sort, const SMTExprRef &AST, - llvm::APFloat &Float, bool useSemantics) { - assert(Sort->isFloatSort() && "Unsupported sort to floating-point!"); - - llvm::APSInt Int(Sort->getFloatSortSize(), true); - const llvm::fltSemantics &Semantics = - getFloatSemantics(Sort->getFloatSortSize()); - SMTSortRef BVSort = getBitvectorSort(Sort->getFloatSortSize()); - if (!toAPSInt(BVSort, AST, Int, true)) { - return false; - } - - if (useSemantics && !areEquivalent(Float.getSemantics(), Semantics)) { - assert(false && "Floating-point types don't match!"); - return false; - } - - Float = llvm::APFloat(Semantics, Int); - return true; - } - - bool toAPSInt(const SMTSortRef &Sort, const SMTExprRef &AST, - llvm::APSInt &Int, bool useSemantics) { - if (Sort->isBitvectorSort()) { - if (useSemantics && Int.getBitWidth() != Sort->getBitvectorSortSize()) { - assert(false && "Bitvector types don't match!"); - return false; - } - - // FIXME: This function is also used to retrieve floating-point values, - // which can be 16, 32, 64 or 128 bits long. Bitvectors can be anything - // between 1 and 64 bits long, which is the reason we have this weird - // guard. In the future, we need proper calls in the backend to retrieve - // floating-points and its special values (NaN, +/-infinity, +/-zero), - // then we can drop this weird condition. - if (Sort->getBitvectorSortSize() <= 64 || - Sort->getBitvectorSortSize() == 128) { - Int = getBitvector(AST, Int.getBitWidth(), Int.isUnsigned()); - return true; - } - - assert(false && "Bitwidth not supported!"); - return false; - } - - if (Sort->isBooleanSort()) { - if (useSemantics && Int.getBitWidth() < 1) { - assert(false && "Boolean type doesn't match!"); - return false; - } - - Int = llvm::APSInt(llvm::APInt(Int.getBitWidth(), getBoolean(AST)), - Int.isUnsigned()); - return true; - } - - llvm_unreachable("Unsupported sort to integer!"); - } - - bool getInterpretation(const SMTExprRef &Exp, llvm::APSInt &Int) override { - Z3Model Model(Context, Z3_solver_get_model(Context.Context, Solver)); - Z3_func_decl Func = Z3_get_app_decl( - Context.Context, Z3_to_app(Context.Context, toZ3Expr(*Exp).AST)); - if (Z3_model_has_interp(Context.Context, Model.Model, Func) != Z3_L_TRUE) - return false; - - SMTExprRef Assign = newExprRef( - Z3Expr(Context, - Z3_model_get_const_interp(Context.Context, Model.Model, Func))); - SMTSortRef Sort = getSort(Assign); - return toAPSInt(Sort, Assign, Int, true); - } - - bool getInterpretation(const SMTExprRef &Exp, llvm::APFloat &Float) override { - Z3Model Model(Context, Z3_solver_get_model(Context.Context, Solver)); - Z3_func_decl Func = Z3_get_app_decl( - Context.Context, Z3_to_app(Context.Context, toZ3Expr(*Exp).AST)); - if (Z3_model_has_interp(Context.Context, Model.Model, Func) != Z3_L_TRUE) - return false; - - SMTExprRef Assign = newExprRef( - Z3Expr(Context, - Z3_model_get_const_interp(Context.Context, Model.Model, Func))); - SMTSortRef Sort = getSort(Assign); - return toAPFloat(Sort, Assign, Float, true); - } - - Optional<bool> check() const override { - Z3_lbool res = Z3_solver_check(Context.Context, Solver); - if (res == Z3_L_TRUE) - return true; - - if (res == Z3_L_FALSE) - return false; - - return Optional<bool>(); - } - - void push() override { return Z3_solver_push(Context.Context, Solver); } - - void pop(unsigned NumStates = 1) override { - assert(Z3_solver_get_num_scopes(Context.Context, Solver) >= NumStates); - return Z3_solver_pop(Context.Context, Solver, NumStates); - } - - bool isFPSupported() override { return true; } - - /// Reset the solver and remove all constraints. - void reset() override { Z3_solver_reset(Context.Context, Solver); } - - void print(raw_ostream &OS) const override { - OS << Z3_solver_to_string(Context.Context, Solver); - } -}; // end class Z3Solver - -class Z3ConstraintManager : public SMTConstraintManager<ConstraintZ3, Z3Expr> { - SMTSolverRef Solver = CreateZ3Solver(); - -public: - Z3ConstraintManager(SubEngine *SE, SValBuilder &SB) - : SMTConstraintManager(SE, SB, Solver) {} -}; // end class Z3ConstraintManager - -} // end anonymous namespace - -#endif - -SMTSolverRef clang::ento::CreateZ3Solver() { -#if CLANG_ANALYZER_WITH_Z3 - return llvm::make_unique<Z3Solver>(); -#else - llvm::report_fatal_error("Clang was not compiled with Z3 support, rebuild " - "with -DCLANG_ANALYZER_ENABLE_Z3_SOLVER=ON", - false); - return nullptr; -#endif -} - -std::unique_ptr<ConstraintManager> -ento::CreateZ3ConstraintManager(ProgramStateManager &StMgr, SubEngine *Eng) { -#if CLANG_ANALYZER_WITH_Z3 - return llvm::make_unique<Z3ConstraintManager>(Eng, StMgr.getSValBuilder()); -#else - llvm::report_fatal_error("Clang was not compiled with Z3 support, rebuild " - "with -DCLANG_ANALYZER_ENABLE_Z3_SOLVER=ON", - false); - return nullptr; -#endif -} |